diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 7949f14eb..8ad31de00 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,18 +1,18 @@ -[bumpversion] -current_version = 1.18.2 - -[bumpversion:file:src/zxbc/version.py] -search = VERSION: Final[str] = "{current_version}" -replace = VERSION: Final[str] = "{new_version}" - -[bumpversion:file:src/zxbasm/version.py] -search = VERSION = "{current_version}" -replace = VERSION = "{new_version}" - -[bumpversion:file:pyproject.toml] -search = version = "{current_version}" -replace = version = "{new_version}" - -[bumpversion:file:docs/archive.md] -search = {current_version} -replace = {new_version} +[bumpversion] +current_version = 1.18.7 + +[bumpversion:file:src/zxbc/version.py] +search = VERSION: Final[str] = "{current_version}" +replace = VERSION: Final[str] = "{new_version}" + +[bumpversion:file:src/zxbasm/version.py] +search = VERSION = "{current_version}" +replace = VERSION = "{new_version}" + +[bumpversion:file:pyproject.toml] +search = version = "{current_version}" +replace = version = "{new_version}" + +[bumpversion:file:docs/archive.md] +search = {current_version} +replace = {new_version} diff --git a/.coveragerc b/.coveragerc index f3a5cd4ea..2301d8835 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,3 +1,3 @@ -[run] -omit = - .tox/* +[run] +omit = + .tox/* diff --git a/.gitattributes b/.gitattributes index f4802a3df..335e83d39 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,18 +1,18 @@ - -* text=false - -*.bas -crlf -*.asm -crlf -*.bi -crlf - -*.txt text -*.md text -*.py text -*.ini text -*.yml text - -*.png binary -*.bin binary - -*.bas linguist-vendored -*.asm linguist-vendored + +* text=false + +*.bas -crlf +*.asm -crlf +*.bi -crlf + +*.txt text +*.md text +*.py text +*.ini text +*.yml text + +*.png binary +*.bin binary + +*.bas linguist-vendored +*.asm linguist-vendored diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index ad038dd86..2d1da814d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -1,54 +1,54 @@ -name: Bug Report -description: File a bug report. -title: "[Bug]: " -labels: ["bug", "triage"] -projects: ["boriel-basic/1"] -assignees: - - boriel -body: - - type: markdown - attributes: - value: | - Thanks for taking the time to fill out this bug report! - - type: input - id: contact - attributes: - label: Contact Details - description: How can we get in touch with you if we need more info? - placeholder: e.g. Telegram @nickname - validations: - required: false - - type: input - id: version - attributes: - label: Compiler version - description: | - Always check you have the latest version. - You can get the version with zxbc --version - placeholder: v1.17.3 - validations: - required: true - - type: textarea - id: what-happened - attributes: - label: What happened? - description: Also tell us, what did you expect to happen? - placeholder: Tell us what you see! - validations: - required: true - - type: textarea - id: logs - attributes: - label: Error and Warning messages - description: | - Please copy and paste any relevant log output. - This will be automatically formatted into code, so no need for backticks. - render: shell - - type: checkboxes - id: terms - attributes: - label: Code of Conduct - description: By submitting this issue, you agree to follow our [Code of Conduct](https://boriel-basic/zxbasic/blob/main/CODE_OF_CONDUCT.md). - options: - - label: I agree to follow this project's Code of Conduct - required: true +name: Bug Report +description: File a bug report. +title: "[Bug]: " +labels: ["bug", "triage"] +projects: ["boriel-basic/1"] +assignees: + - boriel +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: input + id: contact + attributes: + label: Contact Details + description: How can we get in touch with you if we need more info? + placeholder: e.g. Telegram @nickname + validations: + required: false + - type: input + id: version + attributes: + label: Compiler version + description: | + Always check you have the latest version. + You can get the version with zxbc --version + placeholder: v1.17.3 + validations: + required: true + - type: textarea + id: what-happened + attributes: + label: What happened? + description: Also tell us, what did you expect to happen? + placeholder: Tell us what you see! + validations: + required: true + - type: textarea + id: logs + attributes: + label: Error and Warning messages + description: | + Please copy and paste any relevant log output. + This will be automatically formatted into code, so no need for backticks. + render: shell + - type: checkboxes + id: terms + attributes: + label: Code of Conduct + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/boriel-basic/zxbasic/blob/main/CODE_OF_CONDUCT.md). + options: + - label: I agree to follow this project's Code of Conduct + required: true diff --git a/.github/ISSUE_TEMPLATE/bug_report_es.yaml b/.github/ISSUE_TEMPLATE/bug_report_es.yaml index c3da634f0..c834d2218 100644 --- a/.github/ISSUE_TEMPLATE/bug_report_es.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report_es.yaml @@ -1,54 +1,54 @@ -name: Informe de fallo -description: Informar de un problema -title: "[Bug]: " -labels: ["bug", "triage"] -projects: ["boriel-basic/1"] -assignees: - - boriel -body: - - type: markdown - attributes: - value: | - ¡Gracias por tomarte el tiempo de informar de este fallo! - - type: input - id: contact - attributes: - label: Detalles de Contacto - description: ¿Cómo podemos contactarte si ncesitamos información? (opcional) - placeholder: e.g. Telegram @nickname - validations: - required: false - - type: input - id: version - attributes: - label: Versión del compilador - description: | - Always check you have the latest version. - You can get the version with zxbc --version - placeholder: v1.17.3 - validations: - required: true - - type: textarea - id: what-happened - attributes: - label: ¿Qué ha pasado? - description: Cuéntanos también que esperabas que sucediera. - placeholder: (descripción del problema) - validations: - required: true - - type: textarea - id: logs - attributes: - label: Mensajes de error o warnings del compilador - description: | - Por favor, copia y pega la salida de los logs. - Serán formateados automáticamente como salida de log. No necesitas usar formato. - render: shell - - type: checkboxes - id: terms - attributes: - label: Código de Conducta - description: Al enviar este formulario, acuerdas seguir nuestro [Código de Conducta](https://boriel-basic/zxbasic/blob/main/CODE_OF_CONDUCT.md). - options: - - label: I agree to follow this project's Code of Conduct - required: true +name: Informe de fallo +description: Informar de un problema +title: "[Bug]: " +labels: ["bug", "triage"] +projects: ["boriel-basic/1"] +assignees: + - boriel +body: + - type: markdown + attributes: + value: | + ¡Gracias por tomarte el tiempo de informar de este fallo! + - type: input + id: contact + attributes: + label: Detalles de Contacto + description: ¿Cómo podemos contactarte si ncesitamos información? (opcional) + placeholder: e.g. Telegram @nickname + validations: + required: false + - type: input + id: version + attributes: + label: Versión del compilador + description: | + Always check you have the latest version. + You can get the version with zxbc --version + placeholder: v1.17.3 + validations: + required: true + - type: textarea + id: what-happened + attributes: + label: ¿Qué ha pasado? + description: Cuéntanos también que esperabas que sucediera. + placeholder: (descripción del problema) + validations: + required: true + - type: textarea + id: logs + attributes: + label: Mensajes de error o warnings del compilador + description: | + Por favor, copia y pega la salida de los logs. + Serán formateados automáticamente como salida de log. No necesitas usar formato. + render: shell + - type: checkboxes + id: terms + attributes: + label: Código de Conducta + description: Al enviar este formulario, acuerdas seguir nuestro [Código de Conducta](https://github.com/boriel-basic/zxbasic/blob/main/CODE_OF_CONDUCT.md). + options: + - label: I agree to follow this project's Code of Conduct + required: true diff --git a/.github/workflows/python-app.yml b/.github/workflows/run-tests.yml similarity index 86% rename from .github/workflows/python-app.yml rename to .github/workflows/run-tests.yml index 44d531397..7a583cc15 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/run-tests.yml @@ -1,7 +1,7 @@ # This workflow will install Python dependencies, run tests and lint with a single version of Python # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions -name: ZXBasic +name: Run Test on: pull_request: @@ -9,7 +9,7 @@ on: - docs env: - PYTHON_VERSION: 3.12.3 + PYTHON_VERSION: 3.14.3 jobs: build: @@ -19,7 +19,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Python ${{ env.PYTHON_VERSION }} uses: actions/setup-python@v5 @@ -29,12 +29,12 @@ jobs: - name: Install poetry run: | - pip install -U pip~=24.0 - pip install poetry~=1.7.1 + pip install -U pip~=26.0 + pip install poetry~=2.3.2 poetry config virtualenvs.in-project true - name: Set up poetry cache - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: .venv key: venv-${{ runner.os }}-py-${{ env.PYTHON_VERSION }}-${{ hashFiles('poetry.lock') }} diff --git a/.gitignore b/.gitignore index 8385908be..3282193d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,21 +1,26 @@ -# Created by .ignore support plugin (hsz.mobi) -*.egg-info/ -*.pyc -.cache/ -.pytest_cache/ -.mypy_cache/ -.pytype/ -.idea/ -.python-version -.tox/ -MANIFEST -dist/ -examples/*.bin -examples/*.tzx -scratch/ -.coverage -htmlcov/ -build/ -venv/ -.pypi-token -.coverage.* +# Created by .ignore support plugin (hsz.mobi) +*.egg-info/ +*.pyc +.cache/ +.pytest_cache/ +.mypy_cache/ +.pytype/ +.idea/ +.python-version +.tox/ +MANIFEST +dist/ +examples/*.bin +examples/*.tzx +scratch/ +.coverage +htmlcov/ +build/ +venv/ +.pypi-token +.coverage.* + +# zx81sd test build artifacts (compila.bat + split_sd81.py output) +/compila.bat +/TESTSD81*.P +/TESTSD81*.BIN diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 42cff9ce8..e55fcbc4c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,20 +1,20 @@ -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 - hooks: - - id: check-yaml - exclude: \.(bin|out)$ - - id: end-of-file-fixer - exclude: \.(bin|out)$ - - id: trailing-whitespace - exclude: \.(bin|out)$ - - - repo: https://github.com/astral-sh/ruff-pre-commit - # Ruff version. - rev: v0.9.4 - hooks: - # Run the linter. - - id: ruff - args: [ --fix ] - # Run the formatter. - - id: ruff-format +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-yaml + exclude: \.(bin|out)$ + - id: end-of-file-fixer + exclude: \.(bin|out)$ + - id: trailing-whitespace + exclude: \.(bin|out)$ + + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.15.12 + hooks: + # Run the linter. + - id: ruff + args: [ --fix ] + # Run the formatter. + - id: ruff-format diff --git a/.readthedocs.yaml b/.readthedocs.yaml index afe9f1939..ae98c99f5 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,21 +1,21 @@ -# .readthedocs.yaml -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - -# Required -version: 2 - -# Set the version of Python and other tools you might need -build: - os: ubuntu-22.04 - tools: - python: "3.11" - -python: - install: - - requirements: ./docs/requirements.txt - -# Build documentation in the docs/ directory with mkdocs -mkdocs: - configuration: mkdocs.yml - fail_on_warning: false +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +python: + install: + - requirements: ./docs/requirements.txt + +# Build documentation in the docs/ directory with mkdocs +mkdocs: + configuration: mkdocs.yml + fail_on_warning: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 81e2d1a79..04320bd24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,36 @@ -[v1.18.2](https://github.com/boriel-basic/zxbasic/tree/v1.18.1) +[v1.18.7](https://github.com/boriel-basic/zxbasic/tree/v1.18.7) +=== ++ ! Fix a bug with Fixed point numbers conversion to Long ++ ! Consolidate casting of any decimal number to -INF + +[v1.18.6](https://github.com/boriel-basic/zxbasic/tree/v1.18.6) +=== ++ ! Critical bugfix which prevented using zxnext arch backend + +[v1.18.5](https://github.com/boriel-basic/zxbasic/tree/v1.18.5) +=== ++ ! Fixed a regression bug with arrays passed byRef ++ ! Fixed a bug with mul for Long and ULong in zx48k arch ++ Refact some runtime libraries to use that of zx48k arch from zxnext + +[v1.18.4](https://github.com/boriel-basic/zxbasic/tree/v1.18.4) +=== ++ ! Fixed a bug with arrays and strings ++ Updated documentation ++ Added Scroll-Aligned (aligned to column) functions + +[v1.18.3](https://github.com/boriel-basic/zxbasic/tree/v1.18.3) +=== ++ ! Fix `ByRef` with an array element ++ ! Fix `@array` label emission in generated code ++ Small optimization for -O2 ++ Add `getPaintData()` and `getChars()` functions to `putchars.bas` lib ++ Refactor: move memory runtime routines to `runtime/mem/` ++ ! Fix linting and update tests suite ++ Documentation fixes and improvements (links, syntax highlighting) ++ License housekeeping: ensure all files use AGPLv3 headers + +[v1.18.2](https://github.com/boriel-basic/zxbasic/tree/v1.18.2) === + ! Allows installation with `pip install` in python 3.11 environments + * Add `hmirror.bas` library diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 000000000..6fe02f35b --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,35 @@ +# License + +``` +| SPDX-License-Identifier: AGPL-3.0-or-later +| © Copyright 2008-2024 José Manuel Rodríguez de la Rosa +| and contributors. +``` + +See https://www.gnu.org/licenses/agpl-3.0.html for license details. + +# Contributors + +Not all the Contributors are listed here. If you are missing, please let us know. + +* Jose Rodriguez ([@boriel](https://github.com/boriel)) +* Paul Fisher [@britlion](https://github.com/Britlion) +* David Saphier [@em00k](https://github.com/em00k) +* Agustín Gimenez Bernad [@gusmanb](https://github.com/gusmanb) +* Baltasar García Perez-Schofield ([@Baltasarq](https://github.com/Baltasarq)) +* Bartolomé Sánchez Salado ([@bartsanchez](https://github.com/bartsanchez)) +* Cronomantic ([@cronomantic](https://github.com/cronomantic)) +* Juan Segura Durán ([@Duefectu](https://github.com/Duefectu)) +* Einar Saukas ([@einar-saukas](https://github.com/einar-saukas)) +* Ivan Kosarev ([@kosarev](https://github.com/kosarev)) +* Miguel Angel Rodríguez Jódar ([@mcleod-ideafix](https://github.com/mcleod-ideafix)) +* [@MrKOSMOS](https://github.com/MrKOSMOS) +* [@patters-match](https://github.com/patters-match) +* [@programadorhedonista](https://github.com/programadorhedonista) +* Rogerio Biondi ([@rogeriobiondi](https://github.com/rogeriobiondi)) +* [@spectrumcomputing](https://github.com/spectrumcomputing) +* Stefan ([@polluks](https://github.com/polluks)) +* Alejandro Valero ([@wilco2009](https://github.com/wilco2009)) +* Pedro Gimeno ([@pgimeno](https://codeberg.org/pgimeno)) + +Many thanks! Every line counts! 🫶 diff --git a/README.md b/README.md index 19184b400..a3bd52fd0 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ This is a very little help file. - For DOCUMENTATION in English go to the [ZX BASIC docs](https://zxbasic.readthedocs.io/en/latest/). - - For help, support, updates meet the community at the [forum](https://www.boriel.com/forum). + - For help, support, updates meet the community at the [forum](https://forum.boriel.com/). INSTALLATION @@ -65,7 +65,7 @@ You can use this version in Windows, but will need to install a python interpret |---|---|---| | An in-game screenshot of Eleuterio by [@na_th_an](https://github.com/mojontwins) | Ingame screenshot of _El Hobbit_ by [@Wilco2009](https://github.com/wilco2009)| Ingame screenshot of _Knignt and Demonds DX_ by [Einar Saukas](https://github.com/einar-saukas) -See more examples at the [Relased Programs](https://zxbasic.readthedocs.io/en/latest/released_programs/) page. +See more examples at the [Released Programs](https://zxbasic.readthedocs.io/en/latest/released_programs/) page. QUICK START ----------- @@ -94,7 +94,7 @@ Create a text file with the following content: Save it as `hello.bas` and finally compile it with: ~~~~ -zxbc -taB hello.bas +zxbc -f tap --autorun --BASIC hello.bas ~~~~ If everything went well, a file named `hello.tap` should be created. diff --git a/docs/acs.md b/docs/acs.md index a4d364c36..bd757033c 100644 --- a/docs/acs.md +++ b/docs/acs.md @@ -25,7 +25,7 @@ PRINT "Arc Cosine value of a is "; ACS(a) * This function is 100% Sinclair BASIC Compatible * If the given argument type is not float, it will be [converted](cast.md) to float before operating with it. -##See also +## See also * [SIN](sin.md) and [ASN](asn.md) * [TAN](tan.md) and [ATN](atn.md) diff --git a/docs/archive.md b/docs/archive.md index 429c6d6a3..c57312e94 100644 --- a/docs/archive.md +++ b/docs/archive.md @@ -8,30 +8,30 @@ repository (git). ## Latest Version You can contribute to ZX BASIC by reporting possible bugs or improvement suggestions at the -[forum](http://www.boriel.com/forum) or in social media. +[forum](https://forum.boriel.com/) or in social media. -The latest stable version is **1.18.2**. +The latest stable version is **1.18.7**. Click on the desired icon below to download the package suitable for your platform: * [win32zip - https://www.boriel.com/files/zxb/zxbasic-1.18.2-win32.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.2-win32.zip) + https://www.boriel.com/files/zxb/zxbasic-1.18.7-win32.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.7-win32.zip)
Windows .exe zip package. No install needed, just uncompress it in a directory of your choice.
  * [macostargz - https://www.boriel.com/files/zxb/zxbasic-1.18.2-macos.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.2-macos.tar.gz) + https://www.boriel.com/files/zxb/zxbasic-1.18.7-macos.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.7-macos.tar.gz)
Mac OS x64 package. No install needed, just uncompress it in a directory of your choice (needs Python installed in your system).
  * [macostargz - https://www.boriel.com/files/zxb/zxbasic-1.18.2-linux64.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.2-linux64.tar.gz) + https://www.boriel.com/files/zxb/zxbasic-1.18.7-linux64.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.7-linux64.tar.gz)
Linux x64 binary package. No install needed, just uncompress it in a directory of your choice.
  * [zip - https://www.boriel.com/files/zxb/zxbasic-1.18.2.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.2.zip) + https://www.boriel.com/files/zxb/zxbasic-1.18.7.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.7.zip)
Windows, Linux, Mac zip package, with python scripts. Requires python installed in your system.
  * [tar.gz - https://www.boriel.com/files/zxb/zxbasic-1.18.2.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.2.tar.gz) + https://www.boriel.com/files/zxb/zxbasic-1.18.7.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.7.tar.gz)
Windows, Linux, Mac tar.gz package, with python scripts. Requires python installed in your system. ### What's new diff --git a/docs/asn.md b/docs/asn.md index eac4f9b9c..8853bee9e 100644 --- a/docs/asn.md +++ b/docs/asn.md @@ -22,8 +22,8 @@ PRINT "Arc Sine value of a is "; ASN(a) ## Remarks -* This function is 100% Sinclair BASIC Compatible -* If the given argument type is not float, it will be [converted](cast.md) to float before operating with it. +* This function is 100% Sinclair BASIC Compatible +* If the given argument type is not float, it will be [converted](cast.md) to float before operating with it. ## See also diff --git a/docs/bin.md b/docs/bin.md new file mode 100644 index 000000000..d37e002d7 --- /dev/null +++ b/docs/bin.md @@ -0,0 +1,27 @@ +# BIN + +## Syntax + +``` +BIN +``` + + +## Description + +`BIN` is used to denote a binary number. It exists mostly for compatibility reasons with Sinclair BASIC. +You can denote a binary number by prefixing it with `BIN`, but also using the `%` prefix or the `b` suffix. + +For example, the number `5` can be denoted as follows: + + * `BIN 101` + * `101b` + * `%101` + +These three expressions are equivalent. + + +## Remarks + +* This keyword is 100% Sinclair BASIC Compatible +* Binary numbers are always considered unsigned integers and the type depends on the size of the number. diff --git a/docs/draw.md b/docs/draw.md index 37214821e..c4b169574 100644 --- a/docs/draw.md +++ b/docs/draw.md @@ -1,47 +1,56 @@ # DRAW +The **DRAW** command is used to draw straight lines and circular arcs on the screen. ## Syntax ``` - DRAW dx, dy [, arc] +DRAW dx, dy [, arc] ``` - or +or ``` - DRAW ; dx, dy [, arc] +DRAW ; dx, dy [, arc] ``` -Draws a _straight line_ starting from the current drawing coordinates (x, y) -(see [PLOT](plot.md)) to `(x + dx, y + dy)` position. Coordinate `(0, 0)` -designates bottom-left screen corner. Drawing coordinates are updated -to the last position. -**DRAW** is enhanced in ZX BASIC to allow drawing on the last two screen rows (this was not possible in Sinclair BASIC). So now we have 16 lines more (192 in total). Sinclair BASIC only used top 176 scan lines. This means that in Sinclair BASIC +## Description +Draws a *straight line* starting from the current drawing position `(x, y)` to the new position `(x + dx, y + dy)`. The values `dx` and `dy` represent the relative horizontal and vertical distance from the current position. + +The coordinate `(0, 0)` designates the bottom-left corner of the screen. After the command completes, the current drawing coordinates are updated to the last plotted position. + +To set the initial drawing position, you can use the [PLOT](plot.md) command. + +### ZX BASIC Enhancements +**DRAW** is enhanced in ZX BASIC to allow drawing across all 192 scan lines of the Spectrum screen. Original Sinclair BASIC only permitted drawing on the top 176 lines, reserving the bottom 16 lines for system use. + +Because of this difference, programs ported from Sinclair BASIC will appear **shifted down by 16 pixels**. To maintain the same vertical screen position, you should add 16 to the Y-coordinate of the initial `PLOT` command: + +**Sinclair BASIC code:** ``` -PLOT x0, y0: DRAW x, y +PLOT x0, y0: DRAW dx, dy ``` -is equivalent in ZX BASIC to +**ZX BASIC equivalent (for same screen position):** ``` -PLOT x0, y0 + 16: DRAW x, y +PLOT x0, y0 + 16: DRAW dx, dy ``` - -**Remark**: This primitive uses Bresenham's algorithm for faster drawing instead of ROMs implementation. - ### Drawing Arcs -When used with 3 parameters it draws arcs the same way the Sinclair BASIC version does, -but again the 192 scan-lines are available. +When used with three parameters, **DRAW** draws a circular arc from the current position to the relative target `(x + dx, y + dy)`. ``` DRAW dx, dy, arc ``` -The above will draw an arc from the current position to (x + dx, y + dy) position, with a curve of ''arc'' radians. This routine also have some strange behaviors. High values of arc draws strange patterns. + +The `arc` parameter specifies the angle of the curve in radians. +* A positive `arc` value draws the curve turning to the left (counter-clockwise). +* A negative `arc` value draws the curve turning to the right (clockwise). + +**Remark:** This routine may exhibit unusual behavior when using very large values for `arc`. ### Remarks -* This function is not strictly Sinclair BASIC compatible since it uses all 192 screen lines instead of top 176. -If you translate **PLOT**, **DRAW** & **CIRCLE**, commands from Sinclair BASIC _as is_ -your drawing will be _shifted down_ 16 pixels. +* **Fast Drawing**: This primitive uses Bresenham's algorithm for faster drawing instead of the Spectrum ROM implementation. +* **Compatibility**: This function is not strictly Sinclair BASIC compatible because it uses the full 192 screen lines. If you translate **PLOT**, **DRAW**, or **CIRCLE** commands from Sinclair BASIC *as is*, your drawing will be *shifted down* by 16 pixels. ### See Also * [PLOT](plot.md) diff --git a/docs/external_resources.md b/docs/external_resources.md index 83cd40d92..3fa445a18 100644 --- a/docs/external_resources.md +++ b/docs/external_resources.md @@ -9,9 +9,9 @@ Other people contribute to it creating alternative IDEs, libraries, tools and ut ### Tommy Gun A free IDE created by [Tony Thompson](http://www.users.on.net/~tonyt73). This powerful tool is designed to create generic 8-bit and 16-bit projects, and can be set up to be used with ZX Basic. -Have a look at the [TommyGun files](https://sourceforge.net/projects/tommygun/files/) page. +Have a look at the [TommyGun files](https://sourceforge.net/projects/tommygun/files/) or [source code](https://github.com/tonyt73/TommyGun) pages. -To integrate ZX Basic with Tommy Gun, please follow this [tutorial](https://www.boriel.com/forum/showthread.php?tid=110). +To integrate ZX Basic with Tommy Gun, please follow this [tutorial](https://forum.boriel.com/showthread.php?tid=110). ### SpectNet IDE A free and impressive IDE created by [Istvan Novak](https://github.com/Dotneteer). @@ -25,7 +25,7 @@ further information. The documentation of it is very well maintained. * [Multipaint](http://multipaint.kameli.net/), a Java-based graphics editor that allows you to draw pictures with the color limitations of some typical screen formats from 8-bit computer platforms, like ZX Spectrum, C64 and Plus/4 high-resolution and multicolor, MSX 1 and Amstrad CPC0. -* [ZX-Paintbrush] (https://zx-modules.de/), a Windows graphics editor specifically for ZX-Spectrum screen formats, including multicolor. +* [ZX-Paintbrush](https://zx-modules.jimdofree.com/), a Windows graphics editor specifically for ZX-Spectrum screen formats, including multicolor. ## Utilities Other generic utilities not above are: @@ -42,16 +42,16 @@ I find this very useful. ##Engines There are several external engines that can be used within compiler to achieve effects like multicolour display, or sprite handling. -* [FASE](https://www.boriel.com/forum/showthread.php?tid=568) - (or [here](https://www.boriel.com/forum/showthread.php?tid=575)), a sprite, tile and map engine. +* [FASE](https://forum.boriel.com/showthread.php?tid=568) + (or [here](https://forum.boriel.com/showthread.php?tid=575)), a sprite, tile and map engine. * [Nirvana](https://spectrumcomputing.co.uk/entry/30001/ZX-Spectrum/NIRVANA_ENGINE) - (or [https://www.ime.usp.br/~einar/bifrost/ here]), a multicolour 8x2 (30x22 chars) graphics engine. + (or [here](https://www.ime.usp.br/~einar/bifrost/)), a multicolour 8x2 (30x22 chars) graphics engine. * [Nirvana+](https://spectrumcomputing.co.uk/entry/30002/ZX-Spectrum/NIRVANA_ENGINE) - (or [https://www.ime.usp.br/~einar/bifrost/ here]), a multicolour 8x2 (32x23 chars) graphics engine. + (or [here](https://www.ime.usp.br/~einar/bifrost/)), a multicolour 8x2 (32x23 chars) graphics engine. * [Bifrost*](https://spectrumcomputing.co.uk/entry/27405/ZX-Spectrum/BIFROST*_ENGINE) - (or [https://www.ime.usp.br/~einar/bifrost/ here]), a multicolour 8x1 (18x18 chars) graphics engine. + (or [here](https://www.ime.usp.br/~einar/bifrost/)), a multicolour 8x1 (18x18 chars) graphics engine. * [Bifrost*2](https://spectrumcomputing.co.uk/entry/30003/ZX-Spectrum/BIFROST*2_ENGINE) - (or [https://www.ime.usp.br/~einar/bifrost/ here]), a multicolour 8x1 (20x22 chars) graphics engine. + (or [here](https://www.ime.usp.br/~einar/bifrost/)), a multicolour 8x1 (20x22 chars) graphics engine. * [Zxodus\]\[Engine](https://spectrumcomputing.co.uk/entry/26639/ZX-Spectrum/ZXodus_Engine) - (or [here](https://www.boriel.com/forum/showthread.php?tid=568)), a 3d maze and RPG engine with + (or [here](https://forum.boriel.com/showthread.php?tid=568)), a 3d maze and RPG engine with multicolour 8x1 (18x18 chars) support. diff --git a/docs/for.md b/docs/for.md index 3ad506557..5fbc1dc7a 100644 --- a/docs/for.md +++ b/docs/for.md @@ -10,7 +10,6 @@ ## Parameters * _iterator_: a variable identifier that is used to iterate from an initial value to an end value. -* _datatype_: If specified, the variable iterator will automatically be declared with the type datatype. * _startvalue_: an expression that denotes the starting value of the iterator. * _endvalue_: an expression used to compare with the value of the iterator. * _stepvalue_: an expression that is added to the iterator after every iteration. diff --git a/docs/function.md b/docs/function.md index f622874ae..5a009d309 100644 --- a/docs/function.md +++ b/docs/function.md @@ -80,5 +80,6 @@ that can be ignored. * [ASM](asm.md) * [END](end.md) * [RETURN](return.md) +* [DECLARE](declare.md) * [ByREF](byref.md) * [ByVAL](byval.md) diff --git a/docs/identifier.md b/docs/identifier.md index 05cc3d0c7..2fa82fb4c 100644 --- a/docs/identifier.md +++ b/docs/identifier.md @@ -9,7 +9,7 @@ Note that there are a number of common statements that you may find in ZX BASIC ## Reserved Identifiers -The following identifiers are _reserved words_, and can't be used as variables, functions or labels. Reserved identifiers are _case insensitive_ (it doesn't matter whether you write them in upper or lower case letters, or a mix of them). So **PRINT**, **print** and **PrInT** means all the same in ZX BASIC. On the other hand, non-reserved words can be either case sensitive or not (depending on the [options](options.md)) in effect. +The following identifiers are _reserved words_, and can't be used as variables, functions or labels. Reserved identifiers are _case insensitive_ (it doesn't matter whether you write them in upper or lower case letters, or a mix of them). So **PRINT**, **print** and **PrInT** means all the same in ZX BASIC. On the other hand, non-reserved words can be either case sensitive or not (depending on the [options](zxb.md#command-line-options) in effect). Identifiers shown in bold are taken from the Sinclair BASIC (beware their meaning here might be different, however). Some of them has been marked as _statements_, _functions_ or _operators_: @@ -21,6 +21,7 @@ Identifiers shown in bold are taken from the Sinclair BASIC (beware their meanin * **[AT](at.md)** * **[ATN](atn.md)** **(function)** * **[bAND](bitwiselogic.md#bAND)** **(operator)** +* **[BIN](bin.md)** * **[bNOT](bitwiselogic.md#bNOT)** **(operator)** * **[bOR](bitwiselogic.md#bOR)** **(operator)** * **[bXOR](bitwiselogic.md#bXOR)** **(operator)** @@ -100,6 +101,7 @@ Identifiers shown in bold are taken from the Sinclair BASIC (beware their meanin * **[STOP](stop.md)** * **[STR](str.md)** **(function)** (Can also be written as **STR$**) * **[SUB](sub.md)** +* **[TAB](tab.md)** * **[TAN](tan.md)** **(function)** * **[THEN](if.md)** * **[TO](to.md)** @@ -120,7 +122,7 @@ You should also avoid defining (with a SUB or FUNCTION command) routines with th * **[CSRLIN (Library Function)](library/csrlin.md)** **(function)** * [HEX (Library Function)](library/hex.md) **(function)** * [HEX16 (Library Function)](library/hex.md) **(function)** -* **[INPUT (Library Function)](library/input.md)** **(function)** +* **[INPUT42 (Library Function)](library/input42.md)** **(function)** * **[GetKey (Library Function)](library/keys/getkey.md)** **(function)** * **[MultiKeys (Library Function)](library/keys/multikeys.md)** **(function)** * **[GetKeyScanCode (Library Function)](library/keys/getkeyscancode.md)** **(function)** diff --git a/docs/imgs.sh b/docs/imgs.sh index c3fc30811..9531161b6 100755 --- a/docs/imgs.sh +++ b/docs/imgs.sh @@ -1,15 +1,15 @@ -#!/bin/bash - -for i in $(egrep -e 'png|gif' released_programs.md); do - IMG=$(echo $i|sed -e 's/^!*\[\([^]]*\)\].*$/\1/') - LIMG=$(echo $IMG| tr A-Z a-z) - DEST=./img/games/$LIMG - (ls $DEST 2>/dev/null) && continue - IMGPATH=$(find ~/migration/client6/web10/web/wiki/ -type f -name "240px-$IMG") - if [[ "$IMGPATH" == "" ]]; then - continue - fi - #echo "Image: $IMG => $IMGPATH" - cp -v $IMGPATH $DEST -done - +#!/bin/bash + +for i in $(egrep -e 'png|gif' released_programs.md); do + IMG=$(echo $i|sed -e 's/^!*\[\([^]]*\)\].*$/\1/') + LIMG=$(echo $IMG| tr A-Z a-z) + DEST=./img/games/$LIMG + (ls $DEST 2>/dev/null) && continue + IMGPATH=$(find ~/migration/client6/web10/web/wiki/ -type f -name "240px-$IMG") + if [[ "$IMGPATH" == "" ]]; then + continue + fi + #echo "Image: $IMG => $IMGPATH" + cp -v $IMGPATH $DEST +done + diff --git a/docs/index.md b/docs/index.md index 3eb16e062..0b5f2be27 100644 --- a/docs/index.md +++ b/docs/index.md @@ -50,8 +50,8 @@ Get the latest version of Boriel BASIC from the [archive](archive.md). ## Help and Support -* [Community Forum](http://www.boriel.com/forum) -
Have a question? Need help or comment a report a bug? Go to the [Boriel BASIC forum](http://www.boriel.com/forum) +* [Community Forum](https://forum.boriel.com/) +
Have a question? Need help or comment a report a bug? Go to the [Boriel BASIC forum](https://forum.boriel.com/) ## External resources diff --git a/docs/installation.md b/docs/installation.md index 5e15cae81..5fd70dac4 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -8,7 +8,7 @@ ZX Basic SDK comes two several flavours: * Multiplatform python scripts (will require Python 3.8+ to be installed in your system) ### Prerequisites -For the _Multi-platform_ bundle, you will need a [python](http://www.python.org) interpreter **version 3.8** or +For the _Multi-platform_ bundle, you will need a [python](http://www.python.org) interpreter **version 3.11** or higher installed in your system (Linux and Mac OS X users will probably have it already). ### Downloading diff --git a/docs/library/attr.md b/docs/library/attr.md index 481eedf3f..51bcbf5b5 100644 --- a/docs/library/attr.md +++ b/docs/library/attr.md @@ -31,5 +31,5 @@ PRINT AT 0, 0; "The attribute of screen position 9, 10 is "; s ## See also * [ CSRLIN ](csrlin.md) -* [ POS](pos.md) +* [ POS ](pos.md) * [ AT ](../at.md) diff --git a/docs/library/csrlin.md b/docs/library/csrlin.md index d0725bbad..c31d13479 100644 --- a/docs/library/csrlin.md +++ b/docs/library/csrlin.md @@ -2,6 +2,7 @@ ## Syntax + ``` CSRLIN() ``` @@ -14,6 +15,7 @@ Returns the row (line) position of the text cursor (0 means the top line). CSRLIN is a library function to be included with the following command: + ``` # include ``` diff --git a/docs/library/doublesizeprint.bas.md b/docs/library/doublesizeprint.bas.md index 0a881cf6c..3c5736a08 100644 --- a/docs/library/doublesizeprint.bas.md +++ b/docs/library/doublesizeprint.bas.md @@ -57,13 +57,13 @@ EX DE,HL ;' Save it in DE LD A,(IX+9) ;'Character -CP 164 ;' > UDG "U" ? +CP 165 ;' >= RND , > UDG "U" ? JP NC, doubleSizePrintCharEnd -CP 32 ;' < space+1? +CP 32 ;' < space? JP C, doubleSizePrintCharEnd -CP 144 ;' >144? +CP 144 ;' >= UDG "A" ? JP NC, doubleSizePrintCharUDGAddress LD L,A diff --git a/docs/library/hex.md b/docs/library/hex.md new file mode 100644 index 000000000..e04868855 --- /dev/null +++ b/docs/library/hex.md @@ -0,0 +1,37 @@ +# HEX/HEX16/HEX8 + +## Syntax + + +```basic +A$ = hex(n32) +B$ = hex16(n16) +C$ = hex8(n8) +``` +Where `n32` is a 32-bit ULONG, `n16` is a 16-bit UINTEGER and `n8` is an 8-bit UBYTE. + +## Description + +* HEX: +Takes one _32_ bit unsigned integer number and returns an 8 chars str containing the HEX string representation. +* HEX16: +Takes one _16_ bit unsigned integer number and returns a 4 chars str containing the HEX string representation. +* HEX8: +Takes one _8_ bit unsigned integer number and returns a 2 chars str containing the HEX string representation. + +## Requirements + +HEX, HEX16 and HEX8 can be included with the following command: + +``` +#include +``` + +## Remarks + +* This function is not available in Sinclair BASIC. +* Avoid recursive / multiple inclusion when calling this function. +* HEX16 ad HEX8 both call HEX to perform conversion, but differ in the size of the string they return. + +## See also + diff --git a/docs/library/input42.md b/docs/library/input42.md new file mode 100644 index 000000000..59a6debe6 --- /dev/null +++ b/docs/library/input42.md @@ -0,0 +1,35 @@ +# Input42.bas + +## INPUT42 + +Simple INPUT routine (not as powerful as Sinclair BASIC's), but this one uses the PRINT42 routine. +Issues a cursor to the screen, waits for the user to type and returns the user's input through PRINT42 when the user presses ENTER. + +## Syntax + +```basic +A$ = INPUT42(MaxChars) +``` +MaxChars is the number of characters the INPUT42 function will accept as a maximum. It is a UINTEGER and thus has a maximum value of 65535. + +## Requirements + +INPUT42 is a library function that must be included before it can be used. Use the following directive: + +``` +# include +``` + +## Remarks + +* Note that this function ALWAYS RETURNS A STRING, which is very different from Sinclair BASIC's INPUT statement. +* This function places the Input cursor at the last print position, not at the bottom of the screen. Remember that ZX Basic allows access to all 24 screen lines, so PRINT AT 24,0; sets the PRINT cursor to the bottom of the screen. +* Avoid recursive / multiple inclusion +* The input subroutine DOES NOT act like ZX Spectrum INPUT command +* Uses ZX SPECTRUM ROM + +## See also + +* [ INKEY ](../inkey.md) +* [ INPUT ](../input.md) +* [ PRINT42 ](print42.bas.md) diff --git a/docs/library/print42.bas.md b/docs/library/print42.bas.md index b6d96a6b0..372dccca8 100644 --- a/docs/library/print42.bas.md +++ b/docs/library/print42.bas.md @@ -19,7 +19,7 @@ Moves the print42 system's print cursor to row Y, column X. Note that `0 <= x <= for X can be up to 41. The range of values for Y is the normal 0-23. ``` -printat42(STRING) +print42(STRING) ``` Prints the string to the screen at the current Print42 co-ordinates. It does so in the current permanent colours. diff --git a/docs/library/print64x32.bas.md b/docs/library/print64x32.bas.md index eb25adcce..4fff115fe 100644 --- a/docs/library/print64x32.bas.md +++ b/docs/library/print64x32.bas.md @@ -6,7 +6,7 @@ This further enhances the screen by allowing 32 rows of 6 pixels instead of 24 r This allows 2048 character positions on the screen. Note that the screen tables file, needed for inclusion, can be downloaded from the forum -thread http://www.boriel.com/forum/wishlist/64-char-print-32-lines-version-t680.html +thread https://forum.boriel.com/wishlist/64-char-print-32-lines-version-t680.html ## Usage diff --git a/docs/operators.md b/docs/operators.md index c66889333..0e5106ed7 100644 --- a/docs/operators.md +++ b/docs/operators.md @@ -39,10 +39,8 @@ which is _wrong_. If in doubt, use always parenthesis to enforce the desired eva ## Logical Operators -Logicals operators are like in ZX Spectrum Basic. Their result can be either _False_ (which is represented with 0) -or _True_, which might be any other value. Don't expect _True_ value number to be always **1**. -If you need 0/1 values for boolean evaluations, use `--strict-boolean` [compiler option](zxb.md#Command Line Options). -This might add a little overhead to boolean evaluations, tough. +Logicals operators are like in ZX Spectrum Basic. Their result can be either False +(which is represented with 0) or True (represented by 1). Operator arguments must be numbers and the result is an unsigned byte value. For binary operators, if arguments are of different types they are [converted](cast.md) to a common type before being evaluated: diff --git a/docs/overrides/main.html b/docs/overrides/main.html index 86d46c0a3..c3e7f2261 100644 --- a/docs/overrides/main.html +++ b/docs/overrides/main.html @@ -1,6 +1,6 @@ -{% extends "base.html" %} - -{% block extrahead %} - - +{% extends "base.html" %} + +{% block extrahead %} + + {% endblock %} \ No newline at end of file diff --git a/docs/overrides/stylesheets/extra.css b/docs/overrides/stylesheets/extra.css index 3a81f3722..89ae1f4b5 100644 --- a/docs/overrides/stylesheets/extra.css +++ b/docs/overrides/stylesheets/extra.css @@ -1,148 +1,148 @@ -/* Light mode - very light yellow header */ -[data-md-color-scheme="default"] .md-header { - background-color: #d0d6da; /* Very light yellow background color */ - height: 4rem; -} - -[data-md-color-scheme="default"] .md-header__title { - color: #23231c; - padding-left: 2rem; -} - -/* Dark mode - dark gray header */ -[data-md-color-scheme="slate"] .md-header { - background-color: #333333; /* Dark gray background color */ - /*background-image: url('../img/boriel-basic-sinclair.png');*/ - /*background-repeat: no-repeat;*/ - /*background-position: center center;*/ - /*background-size: auto 90%;*/ - height: 4rem; -} - -[data-md-color-scheme="slate"] .md-header__title { - color: #af0d0d; - padding-left: 2rem; -} - -.md-header__title { - font-family: 'Press Start 2P', cursive; - } - -/* Make the site name responsive */ -.md-header__title .md-header__ellipsis { - font-size: 1.2rem; -} - -/* Responsive adjustments for smaller screens */ -@media screen and (max-width: 1220px) { - [data-md-color-scheme="default"] .md-header { - height: 3.5rem; - } - - [data-md-color-scheme="slate"] .md-header { - height: 3.5rem; - } - - [data-md-color-scheme="default"] .md-header__title { - padding-left: 3.0rem; - } - - [data-md-color-scheme="slate"] .md-header__title { - padding-left: 3.0rem; - } - - .md-header__title .md-header__ellipsis { - font-size: 1rem; - } - - .md-header__inner { - display: flex; - } - - .md-header__button { - margin: 0; - padding: 0.5rem; - } - - .md-header__button[for="__drawer"] { - order: 3; - } - - label[for="__search"] { - order: 2; - } -} - -.md-header__inner { - background-image: url('../img/zxbasic_logo.png'); - background-repeat: no-repeat; - background-position: left center; - background-size: auto 100%; - height: 100%; -} - -@media screen and (max-width: 480px) { - [data-md-color-scheme="default"] .md-header { - height: 3rem; - } - - [data-md-color-scheme="slate"] .md-header { - height: 3rem; - } - - [data-md-color-scheme="default"] .md-header__title { - padding-left: 48px; - } - - [data-md-color-scheme="slate"] .md-header__title { - padding-left: 48px; - } - - .md-header__title .md-header__ellipsis { - font-size: 0.8rem; - } - - .md-header__inner { - display: flex; - } - - .md-header__button { - margin: 2px; - padding: 2px; - } - - .md-header__button[for="__drawer"] { - order: 3; /* Ponlo al final (tras tema y búsqueda) */ - } - - label[for="__search"] { - order: 2; - } -} - -/*!* Make the logo size 100% of the header *!*/ -.md-header__button.md-logo { - color: #ff000000; - height: 100%; -} - -/* Increase font weight for headings */ -h1, h2, h3, h4, h5, h6 { - /* font-weight: 600; /* Bold */ - font-family: "Roboto Mono", sans-serif; -} - -/* Target Material theme specific heading classes if any */ -.md-typeset h1, -.md-typeset h2, -.md-typeset h3, -.md-typeset h4, -.md-typeset h5, -.md-typeset h6 { - font-weight: 600; /* Bold */ -} - -.md-typeset .admonition, -.md-typeset details { - font-size: 0.75rem -} +/* Light mode - very light yellow header */ +[data-md-color-scheme="default"] .md-header { + background-color: #d0d6da; /* Very light yellow background color */ + height: 4rem; +} + +[data-md-color-scheme="default"] .md-header__title { + color: #23231c; + padding-left: 2rem; +} + +/* Dark mode - dark gray header */ +[data-md-color-scheme="slate"] .md-header { + background-color: #333333; /* Dark gray background color */ + /*background-image: url('../img/boriel-basic-sinclair.png');*/ + /*background-repeat: no-repeat;*/ + /*background-position: center center;*/ + /*background-size: auto 90%;*/ + height: 4rem; +} + +[data-md-color-scheme="slate"] .md-header__title { + color: #af0d0d; + padding-left: 2rem; +} + +.md-header__title { + font-family: 'Press Start 2P', cursive; + } + +/* Make the site name responsive */ +.md-header__title .md-header__ellipsis { + font-size: 1.2rem; +} + +/* Responsive adjustments for smaller screens */ +@media screen and (max-width: 1220px) { + [data-md-color-scheme="default"] .md-header { + height: 3.5rem; + } + + [data-md-color-scheme="slate"] .md-header { + height: 3.5rem; + } + + [data-md-color-scheme="default"] .md-header__title { + padding-left: 3.0rem; + } + + [data-md-color-scheme="slate"] .md-header__title { + padding-left: 3.0rem; + } + + .md-header__title .md-header__ellipsis { + font-size: 1rem; + } + + .md-header__inner { + display: flex; + } + + .md-header__button { + margin: 0; + padding: 0.5rem; + } + + .md-header__button[for="__drawer"] { + order: 3; + } + + label[for="__search"] { + order: 2; + } +} + +.md-header__inner { + background-image: url('../img/zxbasic_logo.png'); + background-repeat: no-repeat; + background-position: left center; + background-size: auto 100%; + height: 100%; +} + +@media screen and (max-width: 480px) { + [data-md-color-scheme="default"] .md-header { + height: 3rem; + } + + [data-md-color-scheme="slate"] .md-header { + height: 3rem; + } + + [data-md-color-scheme="default"] .md-header__title { + padding-left: 48px; + } + + [data-md-color-scheme="slate"] .md-header__title { + padding-left: 48px; + } + + .md-header__title .md-header__ellipsis { + font-size: 0.8rem; + } + + .md-header__inner { + display: flex; + } + + .md-header__button { + margin: 2px; + padding: 2px; + } + + .md-header__button[for="__drawer"] { + order: 3; /* Ponlo al final (tras tema y búsqueda) */ + } + + label[for="__search"] { + order: 2; + } +} + +/*!* Make the logo size 100% of the header *!*/ +.md-header__button.md-logo { + color: #ff000000; + height: 100%; +} + +/* Increase font weight for headings */ +h1, h2, h3, h4, h5, h6 { + /* font-weight: 600; /* Bold */ + font-family: "Roboto Mono", sans-serif; +} + +/* Target Material theme specific heading classes if any */ +.md-typeset h1, +.md-typeset h2, +.md-typeset h3, +.md-typeset h4, +.md-typeset h5, +.md-typeset h6 { + font-weight: 600; /* Bold */ +} + +.md-typeset .admonition, +.md-typeset details { + font-size: 0.75rem +} diff --git a/docs/print.md b/docs/print.md index e1bf50424..16c9846de 100644 --- a/docs/print.md +++ b/docs/print.md @@ -67,11 +67,12 @@ PRINT "Variable 'a' contains the value: "; a ``` ## Changing the print position -You can change the current _cursor_ position using the [AT](at.md) modifier: +You can change the current _cursor_ position using the [AT](at.md) or [TAB](tab.md) modifiers: ``` PRINT AT 5, 0; "This message starts at ROW 5" PRINT AT 10, 10; "This message starts at ROW 10, COLUMN 10" +PRINT TAB 5; "This starts at column 5" ``` Again, you can chain all `PRINT` _items_ using semicolon: @@ -115,6 +116,7 @@ NEXT i * [CLS](cls.md) * [AT](at.md) +* [TAB](tab.md) * [PAPER](paper.md) * [BORDER](border.md) * [INVERSE](inverse.md) diff --git a/docs/released_programs.md b/docs/released_programs.md index a24282e87..d459ed3f7 100644 --- a/docs/released_programs.md +++ b/docs/released_programs.md @@ -745,7 +745,7 @@ Year: 2013 Source: Yes -Link: [https://www.boriel.com/forum/showthread.php?tid=410](https://www.boriel.com/forum/showthread.php?tid=410) +Link: [https://forum.boriel.com/showthread.php?tid=410](https://forum.boriel.com/showthread.php?tid=410) ![Speccywars.png](img/games/speccywars.png) @@ -990,7 +990,7 @@ Year: 2016 Source: No -Link: [https://www.boriel.com/forum/showthread.php?tid=764](https://www.boriel.com/forum/showthread.php?tid=764) +Link: [https://forum.boriel.com/showthread.php?tid=764](https://forum.boriel.com/showthread.php?tid=764) ![EmsChristmasCardDemo.png](img/games/emschristmascarddemo.png) @@ -1005,7 +1005,7 @@ Year: 2016 Source: No -Link: [https://www.boriel.com/forum/showthread.php?tid=747](https://www.boriel.com/forum/showthread.php?tid=747) +Link: [https://forum.boriel.com/showthread.php?tid=747](https://forum.boriel.com/showthread.php?tid=747) ![JRPGTest.png](img/games/jrpgtest.png) @@ -1020,7 +1020,7 @@ Year: 2016 Source: No -Link: [https://www.boriel.com/forum/showthread.php?tid=757](https://www.boriel.com/forum/showthread.php?tid=757) +Link: [https://forum.boriel.com/showthread.php?tid=757](https://forum.boriel.com/showthread.php?tid=757) Description: @@ -1137,7 +1137,7 @@ Year: 2013 Source: Yes -Link: [https://www.boriel.com/forum/showthread.php?tid=529](https://www.boriel.com/forum/showthread.php?tid=529) +Link: [https://forum.boriel.com/showthread.php?tid=529](https://forum.boriel.com/showthread.php?tid=529) ![P3efilebrowser.png](img/games/p3efilebrowser.png) @@ -1152,7 +1152,7 @@ Year: 2014 Source: Yes -Link: [https://www.boriel.com/forum/showthread.php?tid=299&pid=4142#pid4142](https://www.boriel.com/forum/showthread.php?tid=299&pid=4142#pid4142) +Link: [https://forum.boriel.com/showthread.php?tid=299&pid=4142#pid4142](https://forum.boriel.com/showthread.php?tid=299&pid=4142#pid4142) ![MultiIOboard.png](img/games/multiioboard.png) @@ -1167,7 +1167,7 @@ Year: 2015 Source: No -Link: [https://www.boriel.com/forum/showthread.php?tid=644](https://www.boriel.com/forum/showthread.php?tid=644) +Link: [https://forum.boriel.com/showthread.php?tid=644](https://forum.boriel.com/showthread.php?tid=644) ![TheSpectrumClient.png](img/games/thespectrumclient.png) diff --git a/docs/released_programs/zx81.md b/docs/released_programs/zx81.md index 8f063ad89..f593f091a 100644 --- a/docs/released_programs/zx81.md +++ b/docs/released_programs/zx81.md @@ -36,7 +36,7 @@ Year: 2016 Source: yes -Link: [https://www.boriel.com/forum/showthread.php?tid=730](https://www.boriel.com/forum/showthread.php?tid=730) +Link: [https://forum.boriel.com/showthread.php?tid=730](https://forum.boriel.com/showthread.php?tid=730) Description: a slideshow. diff --git a/docs/requirements.txt b/docs/requirements.txt index 75220cf0c..97c7eb6e6 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -6,9 +6,9 @@ charset-normalizer==3.4.0 click==8.1.7 colorama==0.4.6 ghp-import==2.1.0 -idna==3.10 +idna==3.15 Jinja2==3.1.6 -Markdown==3.7 +Markdown==3.8.1 MarkupSafe==3.0.2 mergedeep==1.3.4 mkdocs==1.6.1 @@ -19,14 +19,14 @@ packaging==24.2 paginate==0.5.7 pathspec==0.12.1 platformdirs==4.3.6 -Pygments==2.18.0 -pymdown-extensions==10.12 +Pygments==2.20.* +pymdown-extensions==10.21.3 python-dateutil==2.9.0.post0 PyYAML==6.0.2 pyyaml_env_tag==0.1 regex==2024.11.6 -requests==2.32.4 +requests==2.33.0 six==1.16.0 -urllib3==2.5.0 +urllib3==2.7.* watchdog==6.0.0 mkdocs-material diff --git a/docs/sub.md b/docs/sub.md index 4734b8f6b..1905a5df7 100644 --- a/docs/sub.md +++ b/docs/sub.md @@ -1,6 +1,5 @@ # SUB - ZX Basic allows function and subroutines declarations. Sinclair Basic does not allow named subroutines, only calls with the GOSUB command. A subroutine defined with the SUB statement is invoked directly. Unlike a [FUNCTION](function.md), a SUB does not return a value. This is the fundamental difference between code defined with SUB and code defined with FUNCTION. Other than that, the setup for SUB and FUNCTION are almost identical. @@ -71,3 +70,4 @@ It will also issue a warning (perhaps you forgot to call it?), that can be ignor * [ASM](asm.md) * [END](end.md) * [RETURN](return.md) +* [DECLARE](declare.md) diff --git a/docs/tab.md b/docs/tab.md new file mode 100644 index 000000000..adf404154 --- /dev/null +++ b/docs/tab.md @@ -0,0 +1,28 @@ +# TAB + +## Syntax +``` +PRINT TAB ; +``` + +`TAB` is not a statement, but a [PRINT](print.md) modifier. It is used to move the cursor to a specific column. +The ZX Spectrum screen has 32 columns, numbered from 0 to 31. + +When `TAB` is used, the cursor moves to the specified column. If the current position is already past the requested column, the cursor moves to that column on the next line. + + +``` +PRINT TAB 10; "Hello" +``` + +The above example will print "Hello" starting at column 10 of the current row. + +## Remarks +* This modifier is Sinclair BASIC compatible. +* If the value is 32 or more, it is taken modulo 32. + +## See also +* [PRINT](print.md) +* [AT](at.md) +* [POS](library/pos.md) +* [CSRLIN](library/csrlin.md) diff --git a/docs/to.md b/docs/to.md new file mode 100644 index 000000000..eef3c9218 --- /dev/null +++ b/docs/to.md @@ -0,0 +1,10 @@ +# TO + +**TO** is a keyword used to specify a sequence of numbers to be used in a statement. + +a TO b (STEP c) will give the statement a sequence of numbers starting at a and ending at [the closest number less than or equal to] b, +with each number after a being the previous number plus 1, or c if the STEP keyword is used (only supported by FOR). +In the case of a TO b with a and b integers, the sequence will include both a and b. +A, B and C can be expressions, can be floating point numbers [FOR i = 0 TO PI / 2 STEP q - circle.bas.md] +Statements that can be used with TO are FOR, DIM [DIM b(0 TO 10) - dim.md, DIM a(3 TO 5, 1 TO 8) - lbound.md, only dimensions, indexes will only be valid within the declared range], [variable value assignments? NO, not even arrays], NOT array, string indexing [s$(TO N - 1) - left.md, s$(len(s$) - N - 1 TO) - right.md] +GO TO, despite having TO, is a different statement and these rules do not apply to it diff --git a/docs/tutorials.md b/docs/tutorials.md index 0161c463a..6daf018d3 100644 --- a/docs/tutorials.md +++ b/docs/tutorials.md @@ -10,7 +10,7 @@ Link: [http://goo.gl/4jPd5](http://goo.gl/4jPd5) ## Making ZX Basic to work with Tommy Gun Author: Boriel
-Link: [https://www.boriel.com/forum/showthread.php?tid=110](https://www.boriel.com/forum/showthread.php?tid=110) +Link: [https://forum.boriel.com/showthread.php?tid=110](https://forum.boriel.com/showthread.php?tid=110) ## How to Use Inline Assembly @@ -32,4 +32,4 @@ and via [Google](http://translate.google.com/translate?sl=auto&tl=en&js=n&prev=_ ## Using Beepola with ZX BASIC Author: LTee
-Link: [https://www.boriel.com/forum/showthread.php?tid=368](https://www.boriel.com/forum/showthread.php?tid=368) +Link: [https://forum.boriel.com/showthread.php?tid=368](https://forum.boriel.com/showthread.php?tid=368) diff --git a/docs/zxb.md b/docs/zxb.md index 298e936d9..2a2bd2087 100644 --- a/docs/zxb.md +++ b/docs/zxb.md @@ -53,64 +53,82 @@ You can change the default origin using the -S command line parameter. ZXB provides several (and useful) command line options. To see them, just type **_zxb.py -h_**, which outputs: ``` - usage: zxb [-h] [-d] [-O OPTIMIZE] [-o OUTPUT_FILE] [-T] [-t] [-B] [-a] [-A] + usage: zxbc.py [-h] [-d] [-O OPTIMIZE] [-o OUTPUT_FILE] + [-T | -t | -A | -E | --parse-only | -f {asm,bin,ir,sna,tap,tzx,z80}] [-B] [-a] [-S ORG] [-e STDERR] [--array-base ARRAY_BASE] - [--string-base STRING_BASE] [-Z] [-H HEAP_SIZE] [--debug-memory] - [--debug-array] [--strict-bool] [--enable-break] [-E] [--explicit] - [-D DEFINES] [-M MEMORY_MAP] [-i] [-I INCLUDE_PATH] [--strict] + [--string-base STRING_BASE] [-Z] [-H HEAP_SIZE] [--heap-address HEAP_ADDRESS] [--debug-memory] + [--debug-array] [--strict-bool] [--enable-break] [--explicit] + [-D DEFINES] [-M MEMORY_MAP] [-i] [-I INCLUDE_PATH] [--strict] [--headerless] [--version] + [--append-binary APPEND_BINARY] [--append-headless-binary APPEND_HEADLESS_BINARY] + [-N] [--arch ARCH] + [--expect-warnings EXPECT_WARNINGS] [-W DISABLE_WARNING] [+W ENABLE_WARNING] [--hide-warning-codes] + [-F CONFIG_FILE] [--save-config SAVE_CONFIG] [--opt-strategy {size,speed,auto}] PROGRAM positional arguments: PROGRAM BASIC program file - optional arguments: + options: -h, --help show this help message and exit - -d, --debug Enable verbosity/debugging output. Additional -d - increase verbosity/debug level - -O OPTIMIZE, --optimize OPTIMIZE - Sets optimization level. 0 = None (default level is 2) - -o OUTPUT_FILE, --output OUTPUT_FILE - Sets output file. Default is input filename with .bin - extension - -T, --tzx Sets output format to tzx (default is .bin) - -t, --tap Sets output format to tap (default is .bin) - -B, --BASIC Creates a BASIC loader which loads the rest of the - CODE. Requires -T or -t + -d, --debug Enable verbosity/debugging output. Additional -d increase verbosity/debug level + -O, --optimize OPTIMIZE + Sets optimization level. 0 = None (default level is 2 + -o, --output OUTPUT_FILE + Sets output file. Default is input filename with .bin extension + -T, --tzx Sets output format to .tzx (default is .bin). + -t, --tap Sets output format to .tap (default is .bin). + -A, --asm Sets output format to .asm. DEPRECATED. Use -f + -E, --emit-backend Emits backend code (IR) instead of ASM or binary. + --parse-only Only parses to check for syntax and semantic errors + -f, --output-format {asm,bin,ir,sna,tap,tzx,z80} + Output format + -B, --BASIC Creates a BASIC loader which loads the rest of the CODE. Requires -T ot -t -a, --autorun Sets the program to be run once loaded - -A, --asm Sets output format to asm - -S ORG, --org ORG Start of machine code. By default 32768 - -e STDERR, --errmsg STDERR - Error messages file (standard error console by - default) + -S, --org ORG Start of machine code. By default 32768 + -e, --errmsg STDERR Error messages file (standard error console by default) --array-base ARRAY_BASE Default lower index for arrays (0 by default) --string-base STRING_BASE Default lower index for strings (0 by default) - -Z, --sinclair Enable by default some more original ZX Spectrum - Sinclair BASIC features: ATTR, SCREEN$, POINT - -H HEAP_SIZE, --heap-size HEAP_SIZE + -Z, --sinclair Enable by default some more original ZX Spectrum Sinclair BASIC features: ATTR, SCREEN$, POINT + -H, --heap-size HEAP_SIZE Sets heap size in bytes (default 4768 bytes) - --heap-size HEAP_ADDRESS - Sets the start address of the heap + --heap-address HEAP_ADDRESS + Sets the heap address. --debug-memory Enables out-of-memory debug --debug-array Enables array boundary checking - --strict-bool Enforce boolean values to be 0 or 1 + --strict-bool Enforce boolean values to be 0 or 1 (Deprecated) --enable-break Enables program execution BREAK detection - -E, --emit-backend Emits backend code instead of ASM or binary - --explicit Requires all variables and functions to be declared - before used - -D DEFINES, --define DEFINES - Defines de given macro. Eg. -D MYDEBUG or -D - NAME=Value - -M MEMORY_MAP, --mmap MEMORY_MAP + --explicit Requires all variables and functions to be declared before used + -D, --define DEFINES Defines de given macro. Eg. -D MYDEBUG or -D NAME=Value + -M, --mmap MEMORY_MAP Generate label memory map - -i, --ignore-case Ignore case. Makes variable names are case insensitive - -I INCLUDE_PATH, --include-path INCLUDE_PATH - Add colon separated list of directories to add to - include path. e.g. -I dir1:dir2 + -i, --ignore-case Ignore case. Makes variable and function names insensitive + -I, --include-path INCLUDE_PATH + Add colon separated list of directories to add to include path. e.g. -I dir1:dir2 --strict Enables strict mode. Force explicit type declaration + --headerless Header-less mode: omit asm prologue and epilogue --version show program's version number and exit + --append-binary APPEND_BINARY + Appends binary to tape file (only works with -t or -T) + --append-headless-binary APPEND_HEADLESS_BINARY + Appends binary to tape file (only works with -t or -T) + -N, --zxnext Enables ZX Next asm extended opcodes + --arch ARCH Target architecture (defaults is'zx48k'). Available architectures: zx48k,zxnext + --expect-warnings EXPECT_WARNINGS + Expects N warnings: first N warnings will be silenced + -W, --disable-warning DISABLE_WARNING + Disables warning WXXX (i.e. -W100 disables warning with code W100) + +W, --enable-warning ENABLE_WARNING + Enables warning WXXX (i.e. -W100 disables warning with code W100) + --hide-warning-codes Hides WXXX codes + -F, --config-file CONFIG_FILE + Loads config from config file + --save-config SAVE_CONFIG + Save options into a config file + --opt-strategy {size,speed,auto} + Optimization strategy (optimize for speed or size). Default: auto ``` Some options (-h, --version) are quite obvious. Let's focus on the rest: @@ -193,10 +211,11 @@ _Out of Memory_ errors. Setting this flag will raise ROM error Subscript out of Range. This flag will add a little overhead to your program execution, but it's useful to detect Out of Range errors. -* **--strict-bool** +* **--strict-bool** DEPRECATED
By default, ZX BASIC will treat boolean values as 0 = False, Any other value = True. Some programmers expect TRUE = 1 always. Using this option will enforce boolean results to be always 0 or 1. Using this option might add a little overhead to your program. Using `--sinclair` option will also enable this feature. +This option is currently deprecated, and will be removed in a future release. * **--enable-break**
Unlike Sinclair BASIC, Your program, being converted to machine code, won't be affected by BREAK. diff --git a/examples/maskedsprites.bas b/examples/maskedsprites.bas new file mode 100644 index 000000000..dbf80a092 --- /dev/null +++ b/examples/maskedsprites.bas @@ -0,0 +1,784 @@ +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyright (C) 2026 Conrado Badenas +' +' Example for +' Print Masked (AND+OR) Sprites, version 2026.04.05 +' ---------------------------------------------------------------- + +' DEFINEs to control the library cb/maskedsprites.bas + #define NumberofMaskedSprites 10 ' Try 50 here, with #define USE_MSFS + #define MaskedSprites_USE_STACK_TRANSFER +'#include "maskedsprites.bas" + #include + +' DEFINEs to control this example program + #define NUMWAITS -1 + #define BORDER_WAIT 0 + #define USE_MSFS + #define INTS di + +#include + +CONST NUMSPRITES as UByte = NumberofMaskedSprites + +dim i as Byte +dim memoryPaging,numberofscrolls,loops,warnings,modulus as UByte +dim x0(0 to NUMSPRITES-1) as UByte +dim y0(0 to NUMSPRITES-1) as UByte +dim x1(0 to NUMSPRITES-1) as UByte +dim y1(0 to NUMSPRITES-1) as UByte +dim back(0 to NUMSPRITES-1) as UInteger +#ifdef USE_MSFS + dim regs(0 to NUMSPRITES-1) as UInteger + dim regHero0,regFoe00,regFoe20,regFoe21,regFoe22,regFoe23 as UInteger +#endif +dim temp as Integer +dim pointx1,pointx2,pointy1,pointy2 as Integer 'for subtractions + +ASM + INTS +END ASM + +for i=0 to NUMSPRITES-1 + y0(i)=255:y1(i)=255:x1(i)=randomLimit(240) +next i:x1(0)=120 + +'DIM Rosquilla_Sprite(31) AS UByte => { _ +' $00,$07,$18,$20,$23,$44,$48,$48, _ +' $48,$48,$44,$23,$20,$18,$07,$00, _ +' $00,$E0,$18,$04,$C4,$22,$12,$12, _ +' $12,$12,$22,$C4,$04,$18,$E0,$00 _ +' } +DIM Rosquilla0(31) AS UByte => { _ + $00,$07,$19,$21,$23,$44,$48,$48, _ + $48,$58,$7C,$3B,$20,$18,$07,$00, _ + $00,$E0,$98,$84,$C4,$22,$12,$12, _ + $12,$1A,$3E,$DC,$04,$18,$E0,$00 _ + } +DIM Rosquilla1(31) AS UByte => { _ + $00,$07,$1C,$2E,$2F,$44,$48,$48, _ + $48,$48,$44,$2F,$2E,$1C,$07,$00, _ + $00,$E0,$18,$04,$C4,$22,$12,$1E, _ + $1E,$12,$22,$C4,$04,$18,$E0,$00 _ + } +DIM Rosquilla2(31) AS UByte => { _ + $00,$07,$18,$20,$3B,$7C,$58,$48, _ + $48,$48,$44,$23,$21,$19,$07,$00, _ + $00,$E0,$18,$04,$DC,$3E,$1A,$12, _ + $12,$12,$22,$C4,$84,$98,$E0,$00 _ + } +DIM Rosquilla3(31) AS UByte => { _ + $00,$07,$18,$20,$23,$44,$48,$78, _ + $78,$48,$44,$23,$20,$18,$07,$00, _ + $00,$E0,$38,$74,$F4,$22,$12,$12, _ + $12,$12,$22,$F4,$74,$38,$E0,$00 _ + } +memoryPaging = CheckMemoryPaging() +if memoryPaging then SetVisibleScreen(5) '128K +paper 1:ink 7:border 0:cls + +for i=0 to 126 + pointx1=randomLimit(255):pointy1=randomLimit(191) + pointx2=randomLimit(255):pointy2=randomLimit(191) + plot pointx1,pointy1:draw pointx2-pointx1,pointy2-pointy1 +next i + +#ifdef USE_MSFS + print "Init MSFS at ";InitMaskedSpritesFileSystem() + print "Free Blocks in MSFS = ";GetNumberofFreeBlocksInMSFS() + regHero0 = RegisterSpriteImageInMSFS(@hero0):if regHero0=0 then STOP + print "regHero0 = ";regHero0 + regFoe00 = RegisterSpriteImageInMSFS(@foe00):if regFoe00=0 then STOP + print "regFoe00 = ";regFoe00 + regFoe20 = RegisterSpriteGraphAndMaskInMSFS(@Rosquilla0(0),@Rosquilla_Sprite_Mask):if regFoe20=0 then STOP + print "regFoe20 = ";regFoe20 + regFoe21 = RegisterSpriteGraphAndMaskInMSFS(@Rosquilla1(0),@Rosquilla_Sprite_Mask):if regFoe21=0 then STOP + print "regFoe21 = ";regFoe21 + regFoe22 = RegisterSpriteGraphAndMaskInMSFS(@Rosquilla2(0),@Rosquilla_Sprite_Mask):if regFoe22=0 then STOP + print "regFoe22 = ";regFoe22 + regFoe23 = RegisterSpriteGraphAndMaskInMSFS(@Rosquilla3(0),@Rosquilla_Sprite_Mask):if regFoe23=0 then STOP + print "regFoe23 = ";regFoe23 + + for i=0 to NUMSPRITES-1: modulus = i mod 2 + if i=0 then + regs(0)=regHero0 + elseif modulus=1 then + regs(i)=regFoe00 + else ' modulus=0 with i>0 + regs(i)=regFoe20 + end if + next i +' pause 0 +#endif + +for i=0 to NUMSPRITES-1 + back(i)=MaskedSpritesBackground(i) +' print back(i) +next i +' print:pause 0 + +if memoryPaging then '128K + dim back0(0 to NUMSPRITES-1) as UInteger + ChangeMaskedSpritesBackgroundSet + for i=0 to NUMSPRITES-1 + back0(i)=MaskedSpritesBackground(i) +' print back0(i) + next i + ChangeMaskedSpritesBackgroundSet +' print:print MaskedSpritesFileSystemStart:pause 0 + + CopyScreen5ToScreen7() +' print at 11,8;"This is Screen5" + SetDrawingScreen7() 'Bank7 is set at $c000 +' print at 13,8;"This is Screen7" + SetDrawingScreen5() 'Bank7 is still set at $c000 +' print at 15,8;"This is Screen5" +end if + +' We start with VisibleScreen = DrawingScreen = 5 +numberofscrolls=0 +warnings=0 +loops=0 +WaitForNewFrame(0) +do + + ' 128K + if memoryPaging then + ' The brand-new VisibleScreen is ready for the ULA to show it on TV + ' But we wait to the beginning of a frame to avoid tearing + ' (unless wanted time-to-wait has already been spent) + out 254,BORDER_WAIT:WaitForNewFrame(NUMWAITS):out 254,0 +'GetInterruptStatusInBorder() + ToggleVisibleScreen() + ' Now we can modify DrawingScreen because it is not Visible + + ' We restore background in x0,y0 if it is saved in MaskedSpritesBackground + if MaskedSpritesBackgroundSet then + for i=NUMSPRITES-1 to 0 step -1 + if y0(i)<>255 then RestoreBackground(x0(i),y0(i),back0(i)) + next i + else + for i=NUMSPRITES-1 to 0 step -1 + if y0(i)<>255 then RestoreBackground(x0(i),y0(i),back(i)) + next i + end if + end if + + ' After restoring background in a Spectrum 128K, you can use CPU to do something else: + ' + ' 0. Change/scroll background (ONLY NOW!!!) + ' 1. Check collisions + ' 2. Resolve collisions + ' 3. Change images of sprites for: + ' a. Animation of regular motion + ' b. Explosions and other extraordinary events + ' 4. Upgrade the score + ' 5. Play sound/music + ' ... + ' + ' You have plenty of time to do whatever you want in a Spectrum 128K + + ' 0. Change/scroll background (ONLY NOW!!!) + if memoryPaging=1 then '128K + if (in($7ffe) bAND 1)=0 then + if numberofscrolls=1 then + Scroll2() + else + Scroll1() + numberofscrolls=1 + end if + else + if numberofscrolls=1 then + Scroll1() + end if + numberofscrolls=0 + end if + end if + +#ifdef USE_MSFS + ' 3. Change images of sprites for: a. Animation of regular motion + for i=2 to NUMSPRITES-1 step 2: modulus = (i+loops) mod 4 + if modulus=0 then + regs(i)=regFoe20 + elseif modulus=1 then + regs(i)=regFoe21 + elseif modulus=2 then + regs(i)=regFoe22 + else + regs(i)=regFoe23 + end if + next i +#endif + + ' We update x0,y0 and compute new x1,y1 + for i=1 to NUMSPRITES-1 + temp=x1(i): x0(i)=temp + temp=temp-1 + if temp<0 then temp=240 + x1(i)=temp + temp=y1(i): y0(i)=temp + if temp=255 then temp=randomLimit(176) + if randomLimit(3)=0 then temp=temp+randomLimit(2)-1 + if temp<0 then temp=0 + if temp>176 then temp=176 + y1(i)=temp + next i + + 'Special code for the hero + temp=x1(0): x0(0)=temp + temp=temp+1 -2*(in($dffe) bAND 1) +2*((in($dffe)>>1) bAND 1) '-noP+noO + if temp<0 then temp=240 + if temp>240 then temp=0 + x1(0)=temp + temp=y1(0): y0(0)=temp + if temp=255 then temp=randomLimit(176) + temp=temp -(in($fdfe) bAND 1) +(in($fbfe) bAND 1) '-noA+noQ + if temp<0 then temp=0 + if temp>176 then temp=176 + y1(0)=temp + + ' 48K We restore background in x0,y0 if it is saved in MaskedSpritesBackground + if not memoryPaging then + for i=NUMSPRITES-1 to 0 step -1 + if y0(i)<>255 then RestoreBackground(x0(i),y0(i),back(i)) + next i + end if + +#ifdef USE_MSFS + ' Print WARNING (if not scrolling) twice if MSFS has run out of free blocks + if not numberofscrolls and warnings<2 and not GetNumberofFreeBlocksInMSFS() then + print at 23,0;"WARNING! MSFS has NO free blocks"; + warnings=warnings+1 + end if +#endif + + ' After restoring background in a Spectrum 48K, you should do NOTHING + ' ... + ' You have ABSOLUTELY NO TIME to do anything in a Spectrum 48K + + ' We save background in MaskedSpritesBackground and draw sprites in x,y... +#ifdef USE_MSFS + ' ...using SaveBackgroundAndDrawSpriteRegisteredInMSFS(,,,) + if MaskedSpritesBackgroundSet then + for i=0 to NUMSPRITES-1 + SaveBackgroundAndDrawSpriteRegisteredInMSFS(x1(i),y1(i),back0(i),regs(i)) + next i + else + for i=0 to NUMSPRITES-1 + SaveBackgroundAndDrawSpriteRegisteredInMSFS(x1(i),y1(i),back(i),regs(i)) + next i + end if +#else + ' ...using SaveBackgroundAndDrawSprite(,,,) + if MaskedSpritesBackgroundSet then + SaveBackgroundAndDrawSprite(x1(0),y1(0),back0(0),@hero0) + for i=1 to NUMSPRITES-1 + SaveBackgroundAndDrawSprite(x1(i),y1(i),back0(i),@foe00) + next i + else + SaveBackgroundAndDrawSprite(x1(0),y1(0),back(0),@hero0) + for i=1 to NUMSPRITES-1 + SaveBackgroundAndDrawSprite(x1(i),y1(i),back(i),@foe00) + next i + end if +#endif + + ' After drawing sprites, you can use CPU to do something else: + ' + ' 1. Check collisions + ' 2. Resolve collisions + ' 3. Change images of sprites for: + ' a. Animation of regular motion + ' b. Explosions and other extraordinary events + ' 4. Upgrade the score + ' 5. Play sound/music + ' ... + ' + ' You have plenty of time to do whatever you want in ANY Spectrum + + ' We change the Set of Backgrounds (128K) for next iteration + if memoryPaging then ChangeMaskedSpritesBackgroundSet() + + ' We have finished drawing + if memoryPaging then + ' We change DrawingScreen (128K) for next iteration + ToggleDrawingScreen() + else + ' ULA shows DrawingScreen (48K) on TV for some frames before going on + out 254,BORDER_WAIT:WaitForNewFrame(NUMWAITS):out 254,0 + end if +'GetInterruptStatusInBorder() + ' Now, VisibleScreen = DrawingScreen, and + ' we could see anything we draw whilst it is drawn + loops=loops+1 +loop + +' SUB/FUNCTIONs that are not used, are "used" here to check compilation is OK + SetBankPreservingINTs(0) +print GetBankPreservingRegs() +print CheckMemoryPaging() + SetVisibleScreen(0) +print GetVisibleScreen() + ToggleVisibleScreen() + CopyScreen5ToScreen7() + CopyScreen7ToScreen5() + SetDrawingScreen5() +print SetDrawingScreen7() + ToggleDrawingScreen() +print ChangeMaskedSpritesBackgroundSet() + SaveBackgroundAndDrawSprite(0,0,0,0) + RestoreBackground(0,0,0) +print InitMaskedSpritesFileSystem() +print GetNumberofFreeBlocksInMSFS() +print RegisterSpriteImageInMSFS(0) +print RegisterSpriteGraphAndMaskInMSFS(0,0) + SaveBackgroundAndDrawSpriteRegisteredInMSFS(0,0,0,0) +GetInterruptStatusInBorder() +Scroll() +Scroll1() +Scroll2() +' Delete/Comment all these "uses" when used, or compilation checking is not needed + +hero0: +ASM + defb %11000000,%00000000,%00001111,%00000000;1 + defb %10000000,%00011111,%00000000,%11100000;2 + defb %00000000,%00100000,%00000000,%00111110;3 + defb %00000000,%01001110,%00000000,%11111000;4 + defb %00000000,%01011001,%00000001,%00001100;5 + defb %00000000,%01011010,%00000000,%00010100;6 + defb %00000000,%00000011,%00000000,%11100010;7 + defb %00000000,%01111100,%00000000,%00100010;8 + defb %00000000,%01111100,%00000000,%00100010;8 + defb %00000000,%00000011,%00000000,%11100010;7 + defb %00000000,%01011010,%00000000,%00010100;6 + defb %00000000,%01011001,%00000001,%00001100;5 + defb %00000000,%01001110,%00000000,%11111000;4 + defb %00000000,%00100000,%00000000,%00111110;3 + defb %10000000,%00011111,%00000000,%11100000;2 + defb %11000000,%00000000,%00001111,%00000000;1 +END ASM + +foe00: +ASM + defb %00000011,%00000000,%11000000,%00000000;1 + defb %00000011,%01111000,%11000000,%00011110;2 + defb %00000011,%01000000,%11000000,%00010000;3 + defb %00000001,%01010000,%10000000,%00010100;4 + defb %00000000,%01001000,%00000000,%00100010;5 + defb %00000000,%00000010,%00000000,%01110000;6 + defb %11100000,%00000111,%00000111,%11100000;7 + defb %11110000,%00000010,%00001111,%01000000;8 + defb %11110000,%00000010,%00001111,%01000000;8 + defb %11100000,%00000111,%00000111,%11100000;7 + defb %00000000,%00001110,%00000000,%01000000;6' + defb %00000000,%01110100,%00000000,%00011110;5' + defb %00000001,%01000000,%10000000,%00010000;4' + defb %00000011,%01010000,%11000000,%00010100;3' + defb %00000011,%01001000,%11000000,%00010010;2' + defb %00000011,%00000000,%11000000,%00000000;1 +END ASM + +Rosquilla_Sprite_Mask: +ASM + db $FF,$F8,$E0,$C0,$C0,$83,$87,$87 + db $87,$87,$83,$C0,$C0,$E0,$F8,$FF + db $FF,$1F,$07,$03,$03,$C1,$E1,$E1 + db $E1,$E1,$C1,$03,$03,$07,$1F,$FF +END ASM + + +' ---------------------------------------------------------------- +' READ_IFF2 is a MACRO that reads correctly the IFF2 flip-flop, +' avoiding a "bug" reported in the Z80 User Manual, +' which Pedro Picapiedra aka ProgramadorHedonista kindly pointed +' me to: "If an interrupt occurs during execution of this +' instruction [LD A,I or LD A,R], the Parity flag contains a 0." +' ---------------------------------------------------------------- +#define READ_IFF2 \ + ld a,i ; IFF2=0/1=DI/EI is saved in PF=0/1=Odd/Even \ + jp pe,1f ; if PF=Even=1, it is sure that IFF2=1=EI \ + ld a,i ; read IFF2 again to ensure that IFF2=0=DI \ +1: + + +' ---------------------------------------------------------------- +' Wait for New Frame +' This SUB uses a hack to ensure a good reading of the IFF2 flip-flop. +' Hack was found thanks to Pedro Picapiedra aka ProgramadorHedonista. +' Parameters: +' Byte: if =0, you want just one HALT +' if >0, minimum number of frames spent since last wait +' if <0, return without waiting +' ---------------------------------------------------------------- +SUB FASTCALL WaitForNewFrame(minimumNumberofFramesToWaitSinceLastWait AS Byte) +ASM + PROC + LOCAL wait,temp + and a + ret m ; return if minimumNumberofFramesToWaitSinceLastWait < 0 + ld hl,temp + ld de,23672 + ld c,a ; A = C = minimumNumberofFramesToWaitSinceLastWait + READ_IFF2 + ex af,af' + ei ; interrupts MUST be enabled before HALT + halt +wait: + ld a,(de) + sub (hl) ; A = number of frames since last RET + cp c ; is A >= C? Yes: NoCarry. No: Carry + jr c,wait ; repeat if A < minimumNumberofFramesToWaitSinceLastWait + ld a,(de) + ld (hl),a + ex af,af' + ret pe ; Return with EI if IFF2=1=EI at the beginning + di ; Return with DI if IFF2=0=DI at the beginning + RET +temp: + defb 0 + ENDP +END ASM +END SUB + + +' ---------------------------------------------------------------- +' Get Interrupt Status in Border +' This SUB uses a hack to ensure a good reading of the IFF2 flip-flop. +' Hack was found thanks to Pedro Picapiedra aka ProgramadorHedonista. +' Border results: 2 Red if Interrupts are Disabled IFF2=0 +' 4 Green if Interrupts are Enabled IFF2=1 +' ---------------------------------------------------------------- +SUB FASTCALL GetInterruptStatusInBorder() +ASM + READ_IFF2 + push af + pop bc + ld a,c + and 4 ; A=0/4 iff IFF2=0/1 + rra ; A=0/2 iff IFF2=0/1 + add a,2 ; A=2/4 iff IFF2=0/1 + out (254),a +END ASM +END SUB + + +SUB FASTCALL Scroll() +ASM + PROC + LOCAL loop + ld hl,(.core.SCREEN_ADDR) + ld de,31 + ld b,192 +loop: +; Here, HL = screen address for first column + add hl,de; column 32 + xor a + rl (hl) ; 32 + dec l + rl (hl) ; 31 + dec l + rl (hl) ; 30 + dec l + rl (hl) ; 29 + dec l + rl (hl) ; 28 + dec l + rl (hl) ; 27 + dec l + rl (hl) ; 26 + dec l + rl (hl) ; 25 + dec l + rl (hl) ; 24 + dec l + rl (hl) ; 23 + dec l + rl (hl) ; 22 + dec l + rl (hl) ; 21 + dec l + rl (hl) ; 20 + dec l + rl (hl) ; 19 + dec l + rl (hl) ; 18 + dec l + rl (hl) ; 17 + dec l + rl (hl) ; 16 + dec l + rl (hl) ; 15 + dec l + rl (hl) ; 14 + dec l + rl (hl) ; 13 + dec l + rl (hl) ; 12 + dec l + rl (hl) ; 11 + dec l + rl (hl) ; 10 + dec l + rl (hl) ; 09 + dec l + rl (hl) ; 08 + dec l + rl (hl) ; 07 + dec l + rl (hl) ; 06 + dec l + rl (hl) ; 05 + dec l + rl (hl) ; 04 + dec l + rl (hl) ; 03 + dec l + rl (hl) ; 02 + dec l + rl (hl) ; 01 + rla ; save CF in A + add hl,de; column 32 + or (hl) + ld (hl),a + inc hl ; first column + djnz loop + ENDP +END ASM +END SUB + + +' ---------------------------------------------------------------- +' NEXT_ROW is a MACRO of ASM code, based on code from +' https://zonadepruebas.com/viewtopic.php?f=15&t=8372&start=40#p81507 +' and found by Joaquin Ferrero +' ---------------------------------------------------------------- +#define NEXT_ROW \ + sub 224 ; 7 A was L, now A = L + 32 (SUB 224 is similar to +32) \ + ; CF = 0/1 iff E >=/< 224 iff a third is/isn't crossed \ + ld l,a ; 4 \ + sbc a,a ; 4 A = 0/255 \ + and 248 ; 7 A = 0/248 (248 = -8) \ + add a,h ; 4 A = H/H-8 iff a third is/isn't crossed \ + ld h,a ; 4 += 30 Ts + + +' ---------------------------------------------------------------- +' Scroll 1 pixel up +' ---------------------------------------------------------------- +SUB FASTCALL Scroll1() +ASM + PROC + LOCAL loop1,loop2,exit + push ix + xor a + ld hl,(.core.SCREEN_ADDR); HL = $4000 or $c000 + ld de,buffer0 + call ldi32 ; scanline0 + ; DEC HL is not needed because row = 0 <> 7, 15, 23 + ld ixh,24 ; 24 rows (rows 0-23) +loop1: + ld l,a ; A = low byte of screen addresses for this row +; HL = scanline0 for this row + ld d,h + ld e,l ; DE = scanline0 of this row + inc h ; HL = scanline1 of this row + ld ixl,7 ; first 7 scanlines of one row +loop2: +; HL,DE = origin,destination screen address + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi ; BC-=32 DE+=32 HL+=32 (if E,L was 224, then D,H changes) + dec hl ; 6 in case H changes (because L was 224: rows 7,15,23) + ld l,a ; +4 = 10 Ts < 21 Ts (PUSH+POP) + ld d,h + ld e,l ; scanline_N+0 of this row + inc h ; scanline_N+1 of this row + dec ixl + jp nz,loop2 + dec ixh + jr z,exit ; this JR Z here (instead of an JP NZ later) adds +7 Ts/row = 161 Ts for 23 rows +; last scanline of first 23 rows: DE is OK, HL must change + NEXT_ROW + ld a,l + call ldi32 ; 539 Ts needed for each row except last row + dec hl ; with JR Z,EXIT before, we save 539-161 = 378 Ts for last row + jp loop1 +exit: + ld hl,buffer0 + call ldi32 ; DE = scanline7 of last row + pop ix + ENDP +END ASM +END SUB + + +' ---------------------------------------------------------------- +' Scroll 2 pixels up +' ---------------------------------------------------------------- +SUB FASTCALL Scroll2() +ASM + PROC + LOCAL loop1,loop2,exit + push ix + xor a + ld hl,(.core.SCREEN_ADDR); HL = $4000 or $c000 + ld de,buffer0 + call ldi32 ; scanline0 + ; DEC HL is not needed because row = 0 <> 7, 15, 23 + ld l,a + inc h + call ldi32 ; scanline1 + ; DEC HL is not needed because row = 0 <> 7, 15, 23 + ld ixh,24 ; 24 rows (rows 0-23) +loop1: + ld l,a ; A = low byte of screen addresses for this row +; HL = scanline1 for this row + ld d,h + ld e,l + dec d ; DE = scanline0 of this row + inc h ; HL = scanline2 of this row + ld ixl,6 ; first 6 scanlines of one row +loop2: +; HL,DE = origin,destination screen address + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi ; BC-=32 DE+=32 HL+=32 (if E,L was 224, then D,H changes) + dec hl ; 6 in case H changes (because L was 224: rows 7,15,23) + ld l,a ; +4 = 10 Ts < 21 Ts (PUSH+POP) + ld d,h + ld e,l + dec d ; DE = scanline_N+0 of this row + inc h ; HL = scanline_N+2 of this row + dec ixl + jp nz,loop2 + dec ixh + jr z,exit ; this JR Z here (instead of an JP NZ later) adds +7 Ts/row = 161 Ts for 23 rows +; last scanline of first 23 rows: DE is OK, HL must change + NEXT_ROW + ld c,h ; C=H is larger than 32, so B does not change when BC-=32 + ld b,e ; DE = scanline6 of this row SAVED in B + ld a,l ; HL = scanline0' of next row SAVED in A + call ldi32 ; 539 Ts needed for each row except last row + dec hl + ld l,a + dec de ; LD C,H + LD B,E + LD A,L + DEC HL + LD L,A + DEC DE + LD E,B + ld e,b ; = 4+4+4+6+4+6+4 = 32 Ts < 42 Ts 2*(PUSH+POP) + inc d ; DE = scanline7 of this row + inc h ; HL = scanline1' of next row + call ldi32 ; 539 Ts needed for each row except last row + dec hl ; with JR Z,EXIT before, we save 2*539-161 = 917 Ts for last row + jp loop1 +exit: + ld hl,buffer0 + call ldi32 ; DE = scanline6 of last row + dec de + ld e,a + inc d ; DE = scanline7 of last row + call ldi32 + pop ix + ret + ENDP + +buffer0: + defs 32 + +buffer1: + defs 32 + +ldi32: + ldi ; 32 LDI = 32*16 = 512 Ts + ldi ; CALL + RET = 17+10 = 27 Ts + ldi ; call ldi32 (+ret) = 512+27 = 539 Ts + ldi + ldi ; LD BC,32 + LDIR = 10 + 21*31+16 = 677 Ts + ldi + ldi ; 677 - 539 = 138 Ts + ldi + ldi ; then, "call ldi32 (+ret)" is 138 Ts faster than "ld bc,32 + ldir" + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi ; BC-=32 DE+=32 HL+=32 (if E,L was >=224, then D,H changes) +END ASM +END SUB + diff --git a/examples/play/chaos.bas b/examples/play/chaos.bas new file mode 100644 index 000000000..7df5c978a --- /dev/null +++ b/examples/play/chaos.bas @@ -0,0 +1,24 @@ +rem Chaos +rem (C) 2026 by Ollibony + +#include + +cls + +let x$ = "1 a&&C&&a&&&g&a&&& f&&C&&a&&&f&a&&& g&&D&&b&&&a&b&&& a&&C&&D&&&C&b&g& " +let y$ = "1 f&&C&&a&&&f&g&&& a&&C&&a&&&g&a&&& g&&D&&b&&&a&b&&& g&&b&&D&&&C&b&g& " + +let z$ = "O5 V8 5eV7eV6eV5e V8cV7cV6cV5c (V8dV7dV6dV5d) (V8cV7cV6cV5c) (V8dV7dV6dV5d)" + +let a$ = x$ + y$ +let b$ = z$ +let c$ = "V10 1&&& " + x$ + y$ + +print ink 1; "Channel A" +print a$: print +print ink 1; "Channel B" +print b$: print +print ink 1; "Channel C" +print c$: print + +Play a$, b$, c$ diff --git a/examples/play/evolution.bas b/examples/play/evolution.bas new file mode 100644 index 000000000..1da411d8d --- /dev/null +++ b/examples/play/evolution.bas @@ -0,0 +1,31 @@ +rem Evolution +rem (C) 2026 by Ollibony + +#include + +cls + +let t$ = "T160 W0 X4000 " + +let w$ = "V15 O3 1e&&Ee&(e&&&)e& 1d&&Dd&(d&&&)d& 1c&&Cc&(c&&&)c& O2 1a&&Aa&(a&&&)a& " +let x$ = "O3 1e&&Ee&(e&&&)e& 1g&&Gg&g&&&d&4d1& 1c&&Cc&(c&&&)c& O2 1a&&Aa&a&&&a&a&a& " +let y$ = "U O4 3GbG1b3G1b3GbG 3BDB1D3B1D4B1BGD 3ECE1C3E1C3EC1EC 3DaD1a6#C1#CE#C " +let z$ = "3GbG1b3G1b3G O5 1b&D& 3bdb1d3b1d1bdDbgd O4 3ECE1C3E1C3E1CgCb 3ae1aCDE5D#C)" + +let i$ = "V15 O5 ((1gab&&&ab&&e&&&d&) 1abC&&&bC&&e&&&d& 1ega&5&&&) " +let j$ = "U O4 3E&E1&3E1&3E&E 3G&G1&3G1&4G& 3C&C1&3C1&3C&& 3a&a1&6a4& " +let k$ = "3E&E1&3E1&3E1G&B& 3G&G1&3G1&4&& 3C&C1&3C1&3C&& 3e&1e4&5ee)" + +let q$ = "V0 9&&&&& V15 O5 1bCD&&&CD&&b&&&g& 1CDE&&&DE&&C&&&g& 1Cba&5&&& " +let r$ = "U O4 3b&b1&3b1&3b&b 3D&D1&3D1&4D& 3g&g1&3g1&3g&& 3$F&$F1&6$F4& " +let s$ = "3b&b1&3b1&3b1E&G& 3D&D1&3D1&4&& 3g&g1&3g1&3g&& 3c&1c4&5dd)" + +let a$ = t$ + w$ + x$ + y$ + z$ +let b$ = i$ + j$ + k$ +let c$ = q$ + r$ + s$ + +print ink 1; a$ +print ink 2; b$ +print ink 3; c$ + +Play a$, b$, c$ diff --git a/examples/play/readme.md b/examples/play/readme.md new file mode 100644 index 000000000..a6a3a9c29 --- /dev/null +++ b/examples/play/readme.md @@ -0,0 +1,4 @@ +This folder contains examples of using the 'Play' library. + +Note that these examples are compatible with ZX Spectrum 128K Basic interpreter, +if processed with the 'zmakebas' utility (https://github.com/ohnosec/zmakebas) in auto-line-numbering mode. diff --git a/examples/sd81/block7test.bas b/examples/sd81/block7test.bas new file mode 100644 index 000000000..7c0104d68 --- /dev/null +++ b/examples/sd81/block7test.bas @@ -0,0 +1,63 @@ +' BLOCK7TEST -- verifica si el mapeador ($E7) mapea de verdad la +' pagina 20 al bloque 7 ($E000-$FFFF), sin nada de MSFS por medio. +' Escribe un patron dependiente de direccion, cambia a otra pagina y +' vuelve, y comprueba que el contenido sigue ahi (paginas distintas = +' contenido independiente) y que el patron se lee correctamente. + +#include + +DIM addr AS UINTEGER +DIM v, expected AS UBYTE +DIM errors AS UINTEGER + +CLS +PRINT "BLOCK7TEST" +PRINT + +' Paso 1: mapear pagina 20 y rellenar con patron +Map(7, 20) +PRINT "Escribiendo pagina 20..." +FOR addr = $E000 TO $E0FF + v = addr BAND 255 + POKE addr, v +NEXT addr + +' Paso 2: cambiar a pagina 63 (la "libre") y rellenar con OTRO patron +Map(7, 63) +PRINT "Escribiendo pagina 63..." +FOR addr = $E000 TO $E0FF + v = 255 - (addr BAND 255) + POKE addr, v +NEXT addr + +' Paso 3: releer pagina 63 (deberia tener el segundo patron) +errors = 0 +FOR addr = $E000 TO $E0FF + expected = 255 - (addr BAND 255) + v = PEEK(addr) + IF v <> expected THEN + errors = errors + 1 + IF errors <= 3 THEN PRINT "pagina 63 mal en "; addr; ": "; v; "<>"; expected + END IF +NEXT addr +PRINT "Pagina 63 tras releer: "; +IF errors = 0 THEN PRINT "OK" ELSE PRINT errors; " errores" + +' Paso 4: volver a pagina 20 y comprobar que SIGUE el primer patron +' (si esto falla con "0", el bloque 7 no esta guardando datos por +' pagina de verdad -- todas las paginas comparten la misma RAM) +Map(7, 20) +errors = 0 +FOR addr = $E000 TO $E0FF + expected = addr BAND 255 + v = PEEK(addr) + IF v <> expected THEN + errors = errors + 1 + IF errors <= 3 THEN PRINT "pagina 20 mal en "; addr; ": "; v; "<>"; expected + END IF +NEXT addr +PRINT "Pagina 20 tras volver: "; +IF errors = 0 THEN PRINT "OK" ELSE PRINT errors; " errores" + +PRINT +PRINT "FIN" diff --git a/examples/sd81/bounce_sd81.bas b/examples/sd81/bounce_sd81.bas new file mode 100644 index 000000000..8a60112fc --- /dev/null +++ b/examples/sd81/bounce_sd81.bas @@ -0,0 +1,191 @@ +' bounce_sd81.bas — SD81 Booster double buffer (dbuf) demo, port $E7 +' +' ZX BASIC port of bounce.asm (SD81-Booster/EXAMPLES/DBUF), reworked for +' the zxbasic runtime: +' - the screen ($C000), Superfast HiRes Spectrum mode and Chroma81 +' colour are already set up by tools/boot1.asm for every zx81sd +' program -- no need to POKE 2043/2044/2045 or write to $7FEF here. +' - screen + attributes are already cleared at program start by +' runtime/bootstrap.asm -- no need to clear them here either. +' - memory-mapped IO (POKE 2056/2057) is disabled by boot1.asm before +' the compiled program starts, so the double buffer is controlled +' through port $E7's pseudo-block-8 path (SD81DBufOn/SD81DBufOff, +' see stdlib/dbuf.bas) instead of the classic POKE 2057. +' +' A 16x16 ball bounces around the screen. Each frame: wait for VSYNC, +' erase the ball, waste ~6ms on purpose (simulates heavy drawing that +' crosses the beam), move, redraw. +' - dbuf ON (green border): solid image, no flicker. +' - dbuf OFF (red border): visible flicker/tearing. +' SPACE toggles the double buffer live; M freezes/unfreezes movement; +' Q quits. +' +' This is also the first real test of the port $E7 pseudo-block-8 dbuf +' path (only the POKE 2057 path had been validated on real hardware +' before this): see stdlib/dbuf.bas for the front-buffer-block notes. + +#include + +dim bally as ubyte +dim ballx as ubyte +dim dy as byte +dim dx as byte +dim moven as ubyte +dim dbufst as ubyte +dim k$ as string + +bally = 80 +ballx = 14 +dy = 2 +dx = 1 +moven = 1 +dbufst = 1 + +' PaintBall(y, x, fillVal): fills 16 rows x 2 bytes at screen row y, +' byte-column x, with fillVal (0 = erase, 255 = draw). Same pixel +' address formula as the Spectrum, but with $C0 as the screen's base +' high byte instead of Spectrum's $40 (zx81sd's screen is fixed at +' $C000, set once by boot1.asm). +sub PaintBall(y as ubyte, x as ubyte, fillVal as ubyte) + asm + PROC + LOCAL PB_LOOP, PB_PIXAD, PB_DONE + + ld a, (ix + 5) ; y + ld d, a + ld a, (ix + 7) ; x + ld e, a + ld a, (ix + 9) ; fillVal + ld c, a + ld b, 16 + +PB_LOOP: + push bc + push de + call PB_PIXAD + ld (hl), c + inc l + ld (hl), c + pop de + pop bc + inc d + djnz PB_LOOP + jr PB_DONE + +PB_PIXAD: + ld a, d + and $C0 + rrca + rrca + rrca + ld h, a + ld a, d + and 7 + or h + or $C0 ; screen base high byte ($C000) + ld h, a + ld a, d + and $38 + add a, a + add a, a + or e + ld l, a + ret + +PB_DONE: + ENDP + end asm +end sub + +' ~6ms busy loop at 3.25MHz: simulates a heavy redraw that crosses the +' video beam, so the difference between dbuf ON/OFF is visible. +sub Delay + asm + PROC + LOCAL DL_LOOP + + ld bc, 1024 +DL_LOOP: + dec bc + ld a, b + or c + jr nz, DL_LOOP + + ENDP + end asm +end sub + +' On bounce, the position is left unchanged for this frame (matching +' bounce.asm) and only the direction flips; it moves next frame. +' +' Kept entirely in ubyte (8-bit, mod-256) arithmetic on purpose, same +' as bounce.asm's "add a,b / cp 177": mixing ubyte+byte and widening to +' integer sign-extends the 8-bit sum based on its own bit 7 (bally=126, +' dy=2 -> 8-bit sum 128 gets read as -128, not +128), which is wrong +' here since bally is genuinely unsigned. Comparing unsigned in ubyte +' space sidesteps that entirely. +sub Move + dim newY as ubyte + dim newX as ubyte + + if moven <> 0 then + newY = bally + dy + if newY >= 177 then + dy = -dy + else + bally = newY + end if + + newX = ballx + dx + if newX >= 31 then + dx = -dx + else + ballx = newX + end if + end if +end sub + +' Returns 0 if Q was pressed (caller should quit), 1 otherwise. +' SPACE toggles dbuf live; M freezes/unfreezes movement. +function Keys() as ubyte + k$ = INKEY$ + + if k$ = " " then + dbufst = 1 - dbufst + if dbufst <> 0 then + SD81DBufOn(5) + border 4 + else + SD81DBufOff() + border 2 + end if + do + loop while INKEY$ <> "" + end if + + if k$ = "m" or k$ = "M" then + moven = 1 - moven + do + loop while INKEY$ <> "" + end if + + if k$ = "q" or k$ = "Q" then + return 0 + end if + + return 1 +end function + +SD81DBufOn(5) +border 4 + +do + SD81WaitVSync() + PaintBall(bally, ballx, 0) + Delay + Move + PaintBall(bally, ballx, 255) +loop while Keys() <> 0 + +SD81DBufOff() +border 7 diff --git a/examples/sd81/filesystem.bas b/examples/sd81/filesystem.bas new file mode 100644 index 000000000..b21451717 --- /dev/null +++ b/examples/sd81/filesystem.bas @@ -0,0 +1,55 @@ +' ---------------------------------------------------------------------- +' filesystem.bas — SD81 Booster MCU file/directory commands +' +' Demonstrates the filesystem-management wrappers in mcu.bas: PWD, CD, +' DIR, MKDIR/RMDIR, DEL, COPY/MOVE and FREE. All of them return the +' MCU's status byte (0 = success); see the manual's error table for +' the rest of the codes. +' ---------------------------------------------------------------------- + +#include +#include + +dim st as ubyte +dim buf(15) as ubyte +dim i as ubyte + +print "SD81 MCU version: "; McuVersion() +print + +print "Current dir: "; McuPwd() + +print "CD /: "; McuCd("/") +print "DIR *.*:" +McuDirPrint("*.*") +print + +print "MKDIR /SD81DEMO: "; McuMkdir("/SD81DEMO") +print "CD /SD81DEMO: "; McuCd("/SD81DEMO") +print "Current dir: "; McuPwd() + +' a tiny 16-byte file, just to have something to copy/move/delete +for i = 0 to 15 + poke @buf(0) + i, i +next i +st = McuSave("DEMO.BIN", @buf(0), 16) +print "SAVE DEMO.BIN: "; st + +print "COPY DEMO.BIN -> COPY.BIN: "; McuCopy("DEMO.BIN", "COPY.BIN") +print "MOVE COPY.BIN -> RENAMED.BIN: "; McuMove("COPY.BIN", "RENAMED.BIN") + +print "DIR *.*:" +McuDirPrint("*.*") + +print "DEL DEMO.BIN: "; McuDel("DEMO.BIN") +print "DEL RENAMED.BIN: "; McuDel("RENAMED.BIN") + +print "CD /: "; McuCd("/") +print "RMDIR /SD81DEMO: "; McuRmdir("/SD81DEMO") + +st = McuFree() +print "FREE: "; McuFreeFreeKb; "/"; McuFreeTotalKb; " KB, status "; st + +print +print "Press a key..." +a$ = input(20) diff --git a/examples/sd81/flights_sd81.bas b/examples/sd81/flights_sd81.bas new file mode 100644 index 000000000..90bb1f693 --- /dev/null +++ b/examples/sd81/flights_sd81.bas @@ -0,0 +1,129 @@ +#include + +0 DIM kk, c, wx, wy, gz, gy, gx, rw, y1, y2, y3, y4, pow, gc, rb, ll, yc, ad, st, rl, bc, nc, pt, px, vz, vy, vx, n AS Float +1 BORDER 1: PAPER 1: INK 7: CLS +2 LET wy=0: LET wx=0: LET gz=0: LET gy=0: LET gx=0 +5 LET rw=0: LET y1=120: LET y2=120: LET y3=40: LET y4=40: LET pow=0: LET gc=0: LET rb=0: LET ll=0: LET yc=0: LET ad=0: LET st=0: LET rl=0: LET bc=0: LET nc=0: LET pt=0: LET px=0: LET vz=0: LET vy=0: LET vx=0 +110 GO TO 5000 +500 LET ra=ad*c: LET vx=as0*SIN ra +510 LET vy=as0*COS ra: RETURN +1000 LET pz=pz+gz: LET py=py+gy: LET px=px+gx +1025 IF st=1 THEN PRINT OVER 1;AT 4,12;"S T A L L": LET st=0: GO TO 1040: END IF +1030 IF as0<30 THEN GO SUB 1500: END IF +1040 LET ad=ad+rl: IF ad<0 THEN LET ad=ad+360: END IF +1050 IF ad>359 THEN LET ad=ad-360: END IF +1060 LET vz=as0*SIN (pt*c)-10+as0/15 +1070 LET gz=vz: LET gy=vy+wy: LET gx=vx+wx +1080 IF vy=0 THEN LET gd=-PI/2: GO TO 1100: END IF +1090 LET gd=-ATN (vx/vy)/c +1100 GO SUB 500 +1110 RETURN +1500 LET st=1: PRINT OVER 1;AT 4,12;"S T A L L": FOR m=1 TO 4: FOR n=20 TO -20 STEP -4: BEEP .01,n: NEXT n: NEXT m +1510 LET rl=INT (RND*21)-9: LET pt=-21-INT (RND*5) +1520 RETURN +2000 IF gc<>0 THEN GO SUB 2200: END IF +2190 LET as0=as0+16*(tc*30-as0-8*pt)/as0: GO SUB 2200: GO TO 2205 +2200 PLOT 35,50+16: DRAW OVER 1;15*SIN (as0*PI/200),15*COS (as0*PI/200): RETURN +2205 IF gc<>0 THEN PLOT 155,50+16: DRAW OVER 1;10*SIN (tn*PI/5),10*COS (tn*PI/5): PLOT 155,50+16: DRAW OVER 1;15*SIN (un*PI/500),15*COS (un*PI/500): END IF +2210 LET tn=pz/1000: LET un=pz-1000*INT tn: PLOT 155,50+16: DRAW OVER 1;10*SIN (tn*PI/5),10*COS (tn*PI/5): PLOT 155,50+16: DRAW OVER 1;15*SIN (un*PI/500),15*COS (un*PI/500) +2220 IF gc<>0 THEN GO SUB 2230: END IF +2225 IF pow=-1 AND tc>.2 THEN LET tc=tc-.2: END IF +2226 IF pow=1 AND tc<8.8 THEN LET tc=tc+.2: END IF +2228 GO SUB 2230: GO TO 2240 +2230 PLOT 215,50+16: DRAW OVER 1;15*SIN (tc*PI/5),15*COS (tc*PI/5): RETURN +2240 PRINT AT 21,2;ABS INT ad;" " +2250 IF py=0 THEN LET rb=0: GO TO 2260: END IF +2255 LET rb=ATN (px/py)/c: IF py>0 THEN LET rb=rb+180: END IF +2260 IF rb<0 THEN LET rb=rb+360: END IF +2270 PRINT AT 21,10;INT rb;" ";AT 21,18;ABS INT px;" " +2280 PRINT AT 21,25;INT (SQR (py*py+px*px));" " +2290 IF (y1<=110 AND y2<=110) OR (y1>=130 AND y2>=130) THEN GO TO 2300: END IF +2295 IF gc<>0 THEN PLOT OVER 1;x1,168+16-y1: DRAW OVER 1;x2-PEEK $8004,168+16-y2-PEEK $8005: END IF +2300 LET yc=120+(pt/3): LET x1=80: LET x2=110: LET y1=yc+17*TAN (rl*2*c): LET y2=yc-17*TAN (rl*2*c) +2310 IF (yc<110 OR yc>130) AND rl=0 THEN GO TO 2376: END IF +2320 IF y1<110 THEN LET x1=95-(95-x1)*(110-yc)/(y1-yc): LET y1=110: GO TO 2340: END IF +2330 IF y1>130 THEN LET x1=95-(95-x1)*(130-yc)/(y1-yc): LET y1=130: END IF +2340 IF y2<110 THEN LET x2=95-(95-x2)*(110-yc)/(y2-yc): LET y2=110: GO TO 2360: END IF +2350 IF y2>130 THEN LET x2=95-(95-x2)*(130-yc)/(y2-yc): LET y2=130: END IF +2360 IF x1<80 OR x2>110 THEN GO TO 2376: END IF +2370 PLOT OVER 1;x1,168+16-y1: DRAW OVER 1;x2-PEEK $8004,168+16-y2-PEEK $8005 +2376 IF (rl=rr AND pp=pt) THEN GO TO 2500: END IF +2377 IF (y3<=2 AND y4<=2) OR (y3>=90 AND y4>=90) THEN GO TO 2380: END IF +2378 IF gc<>0 THEN PLOT OVER 1;x3,176+16-y3: DRAW OVER 1;x4-PEEK $8004,(176+16-y4)-PEEK $8005: END IF +2380 LET yc=33+pt*4: LET x3=11: LET x4=244: LET y3=yc+118*TAN (rl*2*c): LET y4=yc-118*TAN (rl*2*c) +2390 IF (yc<2 OR yc>90) AND rl=0 THEN GO TO 2450: END IF +2400 IF y3<2 THEN LET x3=128-(128-x3)*(2-yc)/(y3-yc): LET y3=2: GO TO 2420: END IF +2410 IF y3>90 THEN LET x3=128-(128-x3)*(90-yc)/(y3-yc): LET y3=90: END IF +2420 IF y4<2 THEN LET x4=128-(128-x4)*(2-yc)/(y4-yc): LET y4=2: GO TO 2440: END IF +2430 IF y4>90 THEN LET x4=128-(128-x4)*(90-yc)/(y4-yc): LET y4=90: END IF +2440 IF x3<11 OR x4>244 THEN GO TO 2500: END IF +2445 OVER 1: PLOT x3,176+16-y3: DRAW x4-PEEK $8004,(176+16-y4)-PEEK $8005: OVER 0 +2450 REM +2500 GO SUB 8000 +2505 IF gc=0 THEN LET gc=1: END IF +2510 LET rr=rl: LET pp=pt: RETURN +3000 LET pow=0: LET k$=INKEY$: IF k$="" THEN RETURN: END IF +3010 IF k$="s" THEN LET pow=pow-1: END IF +3020 IF k$="f" THEN LET pow=pow+1: END IF +3030 IF k$="q" THEN LET pt=pt+1: END IF +3040 IF k$="a" THEN LET pt=pt-1: END IF +3050 IF k$="o" AND rl>-30 THEN LET rl=rl-1: END IF +3060 IF k$="p" AND rl<30 THEN LET rl=rl+1: END IF +3070 RETURN +5000 LET pp=-1: LET rr=-1 +5010 LET c=PI/180: LET py=-20000: LET pz=2000: LET as0=150 +5020 PRINT AT 22,0; "INPUT WIND SPEED (1-50) MPH": LET xx0$=INPUT(10): LET x0=VAL(xx0$): PRINT AT 22,0; TAB 30; +5025 IF x0>50 OR x0<1 THEN GO TO 5020: END IF +5030 PRINT AT 22,0; "WIND DIRECTION (0-359) DEGREES": LET xx1$=INPUT(10): LET x1=VAL(xx1$): PRINT AT 22,0; TAB 30; +5035 IF x1>359 OR x1<0 THEN GO TO 5030: END IF +5040 LET x0=x0/3 +5050 PRINT "WIND SPEED= ";3*x0;" M/S": PRINT "DIRECTION = ";x1;" DEGREES" +5055 PAUSE 100: CLS +5060 LET wy=-x0*COS (x1*c) +5070 LET wx=-x0*SIN (x1*c) +5080 LET gz=vz: LET gy=vy+wy: LET gx=vx+wx +5090 LET tc=5 +5100 LET rt=3: LET tp=5: LET wr=50 +5110 PLOT 10,175+16: DRAW 235,0: DRAW 0,-90: DRAW -235,0: DRAW 0,90 +5120 FOR kk=0 TO 3: CIRCLE 35+kk*60,50+16,20: NEXT kk +5130 PRINT AT 12,2;"SPEED HORZN ALT RPM" +5150 PRINT AT 20,0;"BEARING RUNWAY DRIFT DISTANCE" +5170 PLOT 87,50+16: DRAW 5,0: DRAW 3,-3: DRAW 3,3: DRAW 5,0 +5180 LET x=35: LET y=50: GO SUB 7000: LET x=155: GO SUB 7000: LET x=215: GO SUB 7000 +5500 GO SUB 3000: GO SUB 1000 +5510 IF pz<=0 THEN GO TO 6000: END IF +5520 GO SUB 2000 +5530 GO TO 5500 +6000 IF ABS rl>rt OR pt>tp OR pt<0 OR as0>80 THEN GO TO 6030: END IF +6010 IF ABS px>wr OR ABS py>1000 THEN GO TO 6060: END IF +6020 CLS : PRINT "CONGRATULATIONS ON A SUCCESSFUL LANDING!": GO TO 6100 +6030 FOR n=0 TO 20 STEP .5: PLOT 127,130+16: DRAW 120-INT (RND*240),45-INT (RND*90): BEEP .005,20-n: NEXT n +6040 PAUSE 50 +6050 CLS : PRINT "A CRASH LIKE THAT HAS WRECKED THE AIRCRAFT!": GO TO 6100 +6060 CLS : PRINT "YOU LANDED OFF THE RUNWAY" +6070 IF as0<40 THEN PRINT "FORTUNATELY YOU WEREN'T GOING FAST ENOUGH TO DO MUCH DAMAGE": GO TO 6100: END IF +6080 IF as0<80 THEN PRINT "AT THAT SPEED YOU GOT AWAY WITH LIGHT DAMAGE AND A FEW BRUISES": GO TO 6100: END IF +6090 PRINT "MISSING THE RUNWAY AT THAT SPEED HAS LEFT NO SURVIVORS!" +6100 PRINT : PRINT : PRINT "FINAL FLIGHT DETAILS:": PRINT +6110 PRINT "AIRSPEED= ";INT as0;" M/S": PRINT "DISTANCE= ";INT (SQR (py*py+px*px)): PRINT "PITCH = ";pt +6120 PRINT "ROLL = ";rl: PRINT "RPM = ";INT (10*tc)/10;" X 1000" +6130 PRINT "DRIFT = ";INT ABS px;" MTRS": PRINT "BEARING = ";ad;" DEGREES" +6140 PRINT : PRINT "DO YOU WANT ANOTHER GO (Y/N)?" +6150 LET a$=INKEY$: IF a$<>"y" AND a$<>"n" THEN GO TO 6150: END IF +6160 IF a$="n" THEN GO TO 6900: END IF +6170 CLS : GO TO 1 +6900 STOP +7000 FOR kk=0 TO 2*PI STEP PI/5: PLOT x+17*SIN kk,16+y+17*COS kk: DRAW 2*SIN kk,2*COS kk: NEXT kk: RETURN +8000 IF gc<>0 THEN PLOT 127,174+16: DRAW OVER 1;ox,oy: END IF +8010 LET ox=16*SIN (rb*(PI/180)): LET oy=-(16*ABS COS (rb*(PI/180))) +8020 PLOT 127,174+16: DRAW OVER 1;ox,oy +8025 LET wb=ad: IF ad>180 THEN LET wb=wb-360: END IF +8026 IF rb>180 THEN LET wb=wb+360-rb: GO TO 8040: END IF +8030 LET wb=wb-rb +8040 IF rw=1 THEN PLOT OVER 1;rdx,175+16-rdy: END IF +8050 LET rw=0: IF ABS wb>57 THEN RETURN: END IF +8060 LET rdx=x3+INT (((x4-x3)/2)-SIN (wb*(PI/180))*(x4-x3)*.6) +8070 LET rdy=y3+((y4-y3)*((rdx-x3)/(x4-x3))+2) +8080 IF rdy<2 OR rdy>90 OR rdx<11 OR rdx>244 THEN RETURN: END IF +8090 LET rw=1: PLOT OVER 1;rdx,175+16-rdy +8100 RETURN diff --git a/examples/sd81/joystick.bas b/examples/sd81/joystick.bas new file mode 100644 index 000000000..7f521b32a --- /dev/null +++ b/examples/sd81/joystick.bas @@ -0,0 +1,29 @@ +' ---------------------------------------------------------------------- +' joystick.bas — Programmable joystick via the SD81 Booster MCU +' +' The SD81 Booster maps a physical joystick to keypresses. Joy(keys) +' (joy.bas, cmd 21) configures that mapping: keys must be exactly 5 +' characters, in order UP, DOWN, LEFT, RIGHT, FIRE. Letters (any case), +' digits and space are valid; space means "no key" for that direction. +' Returns the MCU's status byte (0 = OK, 14 = invalid parameter). +' +' Once configured, the joystick just behaves like a keyboard: read it +' with INKEY$/INPUT as usual. +' ---------------------------------------------------------------------- + +#include + +dim st as ubyte +dim k$ as string + +' classic "QAOP + space" layout +st = Joy("QAOPM") +print "Joy(QAOPM) -> status "; st + +print "Move the joystick (BREAK to exit)" +do + k$ = inkey$ + if k$ <> "" then + print k$; + end if +loop diff --git a/examples/sd81/maskedsprites_sd81.bas b/examples/sd81/maskedsprites_sd81.bas new file mode 100644 index 000000000..8c1e3a910 --- /dev/null +++ b/examples/sd81/maskedsprites_sd81.bas @@ -0,0 +1,810 @@ +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyright (C) 2026 Conrado Badenas +' +' Example for +' Print Masked (AND+OR) Sprites, version 2026.04.05 +' +' Version zx81sd: WaitForNewFrame usaba EI+HALT esperando la interrupcion +' de 50Hz del Spectrum (via ROM, contador FRAMES en 23672). En zx81sd las +' interrupciones estan permanentemente deshabilitadas (el vector $0038 es +' solo una trampa DI;HALT, no un manejador real) — ese HALT no despertaria +' nunca. Sustituido por VSYNC_TICK (runtime/vsync.asm), que sondea el +' contador de pulsos VSYNC del SD81 Booster por puerto ($AFh) sin usar +' interrupciones, igual que hace ya PAUSE. Resto del fichero sin cambios. +' ---------------------------------------------------------------- + +' DEFINEs to control the library cb/maskedsprites.bas + #define NumberofMaskedSprites 10 ' Try 50 here, with #define USE_MSFS + #define MaskedSprites_USE_STACK_TRANSFER +'#include "maskedsprites.bas" + #include + +' DEFINEs to control this example program + #define NUMWAITS -1 + #define BORDER_WAIT 0 + #define USE_MSFS + #define INTS di + +#include + +CONST NUMSPRITES as UByte = NumberofMaskedSprites + +dim i as Byte +dim memoryPaging,numberofscrolls,loops,warnings,modulus as UByte +dim x0(0 to NUMSPRITES-1) as UByte +dim y0(0 to NUMSPRITES-1) as UByte +dim x1(0 to NUMSPRITES-1) as UByte +dim y1(0 to NUMSPRITES-1) as UByte +dim back(0 to NUMSPRITES-1) as UInteger +#ifdef USE_MSFS + dim regs(0 to NUMSPRITES-1) as UInteger + dim regHero0,regFoe00,regFoe20,regFoe21,regFoe22,regFoe23 as UInteger +#endif +dim temp as Integer +dim pointx1,pointx2,pointy1,pointy2 as Integer 'for subtractions + +ASM + INTS +END ASM + +for i=0 to NUMSPRITES-1 + y0(i)=255:y1(i)=255:x1(i)=randomLimit(240) +next i:x1(0)=120 + +'DIM Rosquilla_Sprite(31) AS UByte => { _ +' $00,$07,$18,$20,$23,$44,$48,$48, _ +' $48,$48,$44,$23,$20,$18,$07,$00, _ +' $00,$E0,$18,$04,$C4,$22,$12,$12, _ +' $12,$12,$22,$C4,$04,$18,$E0,$00 _ +' } +DIM Rosquilla0(31) AS UByte => { _ + $00,$07,$19,$21,$23,$44,$48,$48, _ + $48,$58,$7C,$3B,$20,$18,$07,$00, _ + $00,$E0,$98,$84,$C4,$22,$12,$12, _ + $12,$1A,$3E,$DC,$04,$18,$E0,$00 _ + } +DIM Rosquilla1(31) AS UByte => { _ + $00,$07,$1C,$2E,$2F,$44,$48,$48, _ + $48,$48,$44,$2F,$2E,$1C,$07,$00, _ + $00,$E0,$18,$04,$C4,$22,$12,$1E, _ + $1E,$12,$22,$C4,$04,$18,$E0,$00 _ + } +DIM Rosquilla2(31) AS UByte => { _ + $00,$07,$18,$20,$3B,$7C,$58,$48, _ + $48,$48,$44,$23,$21,$19,$07,$00, _ + $00,$E0,$18,$04,$DC,$3E,$1A,$12, _ + $12,$12,$22,$C4,$84,$98,$E0,$00 _ + } +DIM Rosquilla3(31) AS UByte => { _ + $00,$07,$18,$20,$23,$44,$48,$78, _ + $78,$48,$44,$23,$20,$18,$07,$00, _ + $00,$E0,$38,$74,$F4,$22,$12,$12, _ + $12,$12,$22,$F4,$74,$38,$E0,$00 _ + } +memoryPaging = CheckMemoryPaging() +if memoryPaging then SetVisibleScreen(5) '128K +paper 1:ink 7:border 0:cls + +for i=0 to 126 + pointx1=randomLimit(255):pointy1=randomLimit(191) + pointx2=randomLimit(255):pointy2=randomLimit(191) + plot pointx1,pointy1:draw pointx2-pointx1,pointy2-pointy1 +next i + +#ifdef USE_MSFS + print "Init MSFS at ";InitMaskedSpritesFileSystem() + print "Free Blocks in MSFS = ";GetNumberofFreeBlocksInMSFS() + regHero0 = RegisterSpriteImageInMSFS(@hero0):if regHero0=0 then STOP + print "regHero0 = ";regHero0 + regFoe00 = RegisterSpriteImageInMSFS(@foe00):if regFoe00=0 then STOP + print "regFoe00 = ";regFoe00 + regFoe20 = RegisterSpriteGraphAndMaskInMSFS(@Rosquilla0(0),@Rosquilla_Sprite_Mask):if regFoe20=0 then STOP + print "regFoe20 = ";regFoe20 + regFoe21 = RegisterSpriteGraphAndMaskInMSFS(@Rosquilla1(0),@Rosquilla_Sprite_Mask):if regFoe21=0 then STOP + print "regFoe21 = ";regFoe21 + regFoe22 = RegisterSpriteGraphAndMaskInMSFS(@Rosquilla2(0),@Rosquilla_Sprite_Mask):if regFoe22=0 then STOP + print "regFoe22 = ";regFoe22 + regFoe23 = RegisterSpriteGraphAndMaskInMSFS(@Rosquilla3(0),@Rosquilla_Sprite_Mask):if regFoe23=0 then STOP + print "regFoe23 = ";regFoe23 + + for i=0 to NUMSPRITES-1: modulus = i mod 2 + if i=0 then + regs(0)=regHero0 + elseif modulus=1 then + regs(i)=regFoe00 + else ' modulus=0 with i>0 + regs(i)=regFoe20 + end if + next i +' pause 0 +#endif + +for i=0 to NUMSPRITES-1 + back(i)=MaskedSpritesBackground(i) +' print back(i) +next i +' print:pause 0 + +if memoryPaging then '128K + dim back0(0 to NUMSPRITES-1) as UInteger + ChangeMaskedSpritesBackgroundSet + for i=0 to NUMSPRITES-1 + back0(i)=MaskedSpritesBackground(i) +' print back0(i) + next i + ChangeMaskedSpritesBackgroundSet +' print:print MaskedSpritesFileSystemStart:pause 0 + + CopyScreen5ToScreen7() +' print at 11,8;"This is Screen5" + SetDrawingScreen7() 'Bank7 is set at $c000 +' print at 13,8;"This is Screen7" + SetDrawingScreen5() 'Bank7 is still set at $c000 +' print at 15,8;"This is Screen5" +end if + +' We start with VisibleScreen = DrawingScreen = 5 +numberofscrolls=0 +warnings=0 +loops=0 +WaitForNewFrame(0) +do + + ' 128K + if memoryPaging then + ' The brand-new VisibleScreen is ready for the ULA to show it on TV + ' But we wait to the beginning of a frame to avoid tearing + ' (unless wanted time-to-wait has already been spent) + out 254,BORDER_WAIT:WaitForNewFrame(NUMWAITS):out 254,0 +'GetInterruptStatusInBorder() + ToggleVisibleScreen() + ' Now we can modify DrawingScreen because it is not Visible + + ' We restore background in x0,y0 if it is saved in MaskedSpritesBackground + if MaskedSpritesBackgroundSet then + for i=NUMSPRITES-1 to 0 step -1 + if y0(i)<>255 then RestoreBackground(x0(i),y0(i),back0(i)) + next i + else + for i=NUMSPRITES-1 to 0 step -1 + if y0(i)<>255 then RestoreBackground(x0(i),y0(i),back(i)) + next i + end if + end if + + ' After restoring background in a Spectrum 128K, you can use CPU to do something else: + ' + ' 0. Change/scroll background (ONLY NOW!!!) + ' 1. Check collisions + ' 2. Resolve collisions + ' 3. Change images of sprites for: + ' a. Animation of regular motion + ' b. Explosions and other extraordinary events + ' 4. Upgrade the score + ' 5. Play sound/music + ' ... + ' + ' You have plenty of time to do whatever you want in a Spectrum 128K + + ' 0. Change/scroll background (ONLY NOW!!!) + if memoryPaging=1 then '128K + if (in($7ffe) bAND 1)=0 then + if numberofscrolls=1 then + Scroll2() + else + Scroll1() + numberofscrolls=1 + end if + else + if numberofscrolls=1 then + Scroll1() + end if + numberofscrolls=0 + end if + end if + +#ifdef USE_MSFS + ' 3. Change images of sprites for: a. Animation of regular motion + for i=2 to NUMSPRITES-1 step 2: modulus = (i+loops) mod 4 + if modulus=0 then + regs(i)=regFoe20 + elseif modulus=1 then + regs(i)=regFoe21 + elseif modulus=2 then + regs(i)=regFoe22 + else + regs(i)=regFoe23 + end if + next i +#endif + + ' We update x0,y0 and compute new x1,y1 + for i=1 to NUMSPRITES-1 + temp=x1(i): x0(i)=temp + temp=temp-1 + if temp<0 then temp=240 + x1(i)=temp + temp=y1(i): y0(i)=temp + if temp=255 then temp=randomLimit(176) + if randomLimit(3)=0 then temp=temp+randomLimit(2)-1 + if temp<0 then temp=0 + if temp>176 then temp=176 + y1(i)=temp + next i + + 'Special code for the hero + temp=x1(0): x0(0)=temp + temp=temp+1 -2*(in($dffe) bAND 1) +2*((in($dffe)>>1) bAND 1) '-noP+noO + if temp<0 then temp=240 + if temp>240 then temp=0 + x1(0)=temp + temp=y1(0): y0(0)=temp + if temp=255 then temp=randomLimit(176) + temp=temp -(in($fdfe) bAND 1) +(in($fbfe) bAND 1) '-noA+noQ + if temp<0 then temp=0 + if temp>176 then temp=176 + y1(0)=temp + + ' 48K We restore background in x0,y0 if it is saved in MaskedSpritesBackground + if not memoryPaging then + for i=NUMSPRITES-1 to 0 step -1 + if y0(i)<>255 then RestoreBackground(x0(i),y0(i),back(i)) + next i + end if + +#ifdef USE_MSFS + ' Print WARNING (if not scrolling) twice if MSFS has run out of free blocks + if not numberofscrolls and warnings<2 and not GetNumberofFreeBlocksInMSFS() then + print at 23,0;"WARNING! MSFS has NO free blocks"; + warnings=warnings+1 + end if +#endif + + ' After restoring background in a Spectrum 48K, you should do NOTHING + ' ... + ' You have ABSOLUTELY NO TIME to do anything in a Spectrum 48K + + ' We save background in MaskedSpritesBackground and draw sprites in x,y... +#ifdef USE_MSFS + ' ...using SaveBackgroundAndDrawSpriteRegisteredInMSFS(,,,) + if MaskedSpritesBackgroundSet then + for i=0 to NUMSPRITES-1 + SaveBackgroundAndDrawSpriteRegisteredInMSFS(x1(i),y1(i),back0(i),regs(i)) + next i + else + for i=0 to NUMSPRITES-1 + SaveBackgroundAndDrawSpriteRegisteredInMSFS(x1(i),y1(i),back(i),regs(i)) + next i + end if +#else + ' ...using SaveBackgroundAndDrawSprite(,,,) + if MaskedSpritesBackgroundSet then + SaveBackgroundAndDrawSprite(x1(0),y1(0),back0(0),@hero0) + for i=1 to NUMSPRITES-1 + SaveBackgroundAndDrawSprite(x1(i),y1(i),back0(i),@foe00) + next i + else + SaveBackgroundAndDrawSprite(x1(0),y1(0),back(0),@hero0) + for i=1 to NUMSPRITES-1 + SaveBackgroundAndDrawSprite(x1(i),y1(i),back(i),@foe00) + next i + end if +#endif + + ' After drawing sprites, you can use CPU to do something else: + ' + ' 1. Check collisions + ' 2. Resolve collisions + ' 3. Change images of sprites for: + ' a. Animation of regular motion + ' b. Explosions and other extraordinary events + ' 4. Upgrade the score + ' 5. Play sound/music + ' ... + ' + ' You have plenty of time to do whatever you want in ANY Spectrum + + ' We change the Set of Backgrounds (128K) for next iteration + if memoryPaging then ChangeMaskedSpritesBackgroundSet() + + ' We have finished drawing + if memoryPaging then + ' We change DrawingScreen (128K) for next iteration + ToggleDrawingScreen() + else + ' ULA shows DrawingScreen (48K) on TV for some frames before going on + out 254,BORDER_WAIT:WaitForNewFrame(NUMWAITS):out 254,0 + end if +'GetInterruptStatusInBorder() + ' Now, VisibleScreen = DrawingScreen, and + ' we could see anything we draw whilst it is drawn + loops=loops+1 +loop + +' SUB/FUNCTIONs that are not used, are "used" here to check compilation is OK + SetBankPreservingINTs(0) +print GetBankPreservingRegs() +print CheckMemoryPaging() + SetVisibleScreen(0) +print GetVisibleScreen() + ToggleVisibleScreen() + CopyScreen5ToScreen7() + CopyScreen7ToScreen5() + SetDrawingScreen5() +print SetDrawingScreen7() + ToggleDrawingScreen() +print ChangeMaskedSpritesBackgroundSet() + SaveBackgroundAndDrawSprite(0,0,0,0) + RestoreBackground(0,0,0) +print InitMaskedSpritesFileSystem() +print GetNumberofFreeBlocksInMSFS() +print RegisterSpriteImageInMSFS(0) +print RegisterSpriteGraphAndMaskInMSFS(0,0) + SaveBackgroundAndDrawSpriteRegisteredInMSFS(0,0,0,0) +GetInterruptStatusInBorder() +Scroll() +Scroll1() +Scroll2() +' Delete/Comment all these "uses" when used, or compilation checking is not needed + +hero0: +ASM + defb %11000000,%00000000,%00001111,%00000000;1 + defb %10000000,%00011111,%00000000,%11100000;2 + defb %00000000,%00100000,%00000000,%00111110;3 + defb %00000000,%01001110,%00000000,%11111000;4 + defb %00000000,%01011001,%00000001,%00001100;5 + defb %00000000,%01011010,%00000000,%00010100;6 + defb %00000000,%00000011,%00000000,%11100010;7 + defb %00000000,%01111100,%00000000,%00100010;8 + defb %00000000,%01111100,%00000000,%00100010;8 + defb %00000000,%00000011,%00000000,%11100010;7 + defb %00000000,%01011010,%00000000,%00010100;6 + defb %00000000,%01011001,%00000001,%00001100;5 + defb %00000000,%01001110,%00000000,%11111000;4 + defb %00000000,%00100000,%00000000,%00111110;3 + defb %10000000,%00011111,%00000000,%11100000;2 + defb %11000000,%00000000,%00001111,%00000000;1 +END ASM + +foe00: +ASM + defb %00000011,%00000000,%11000000,%00000000;1 + defb %00000011,%01111000,%11000000,%00011110;2 + defb %00000011,%01000000,%11000000,%00010000;3 + defb %00000001,%01010000,%10000000,%00010100;4 + defb %00000000,%01001000,%00000000,%00100010;5 + defb %00000000,%00000010,%00000000,%01110000;6 + defb %11100000,%00000111,%00000111,%11100000;7 + defb %11110000,%00000010,%00001111,%01000000;8 + defb %11110000,%00000010,%00001111,%01000000;8 + defb %11100000,%00000111,%00000111,%11100000;7 + defb %00000000,%00001110,%00000000,%01000000;6' + defb %00000000,%01110100,%00000000,%00011110;5' + defb %00000001,%01000000,%10000000,%00010000;4' + defb %00000011,%01010000,%11000000,%00010100;3' + defb %00000011,%01001000,%11000000,%00010010;2' + defb %00000011,%00000000,%11000000,%00000000;1 +END ASM + +Rosquilla_Sprite_Mask: +ASM + db $FF,$F8,$E0,$C0,$C0,$83,$87,$87 + db $87,$87,$83,$C0,$C0,$E0,$F8,$FF + db $FF,$1F,$07,$03,$03,$C1,$E1,$E1 + db $E1,$E1,$C1,$03,$03,$07,$1F,$FF +END ASM + + +' ---------------------------------------------------------------- +' READ_IFF2 is a MACRO that reads correctly the IFF2 flip-flop, +' avoiding a "bug" reported in the Z80 User Manual, +' which Pedro Picapiedra aka ProgramadorHedonista kindly pointed +' me to: "If an interrupt occurs during execution of this +' instruction [LD A,I or LD A,R], the Parity flag contains a 0." +' ---------------------------------------------------------------- +#define READ_IFF2 \ + ld a,i ; IFF2=0/1=DI/EI is saved in PF=0/1=Odd/Even \ + jp pe,1f ; if PF=Even=1, it is sure that IFF2=1=EI \ + ld a,i ; read IFF2 again to ensure that IFF2=0=DI \ +1: + + +' ---------------------------------------------------------------- +' Wait for New Frame — version zx81sd +' +' El original usaba EI+HALT esperando la interrupcion IM1 de 50Hz de la +' ROM del Spectrum, y comparaba contra el contador FRAMES de la ROM en +' la direccion absoluta 23672. En zx81sd no hay interrupciones (todo el +' runtime corre con DI permanente; el vector $0038 es una trampa +' DI;HALT, no un manejador real) ni existe esa direccion de sysvar. +' Sustituido por VSYNC_TICK (runtime/vsync.asm): sondea por puerto el +' contador de pulsos VSYNC del SD81 Booster (hardware, sin interrupciones) +' e incrementa nuestro propio sysvar FRAMES. Se llama una vez por cada +' frame que haya que esperar, en vez de un unico HALT seguido de un +' bucle que confiaba en que la interrupcion siguiera incrementando FRAMES +' en segundo plano mientras el bucle solo comprobaba sin esperar. +' Parameters: +' Byte: if =0, you want just one frame wait +' if >0, minimum number of frames spent since last wait +' if <0, return without waiting +' ---------------------------------------------------------------- +SUB FASTCALL WaitForNewFrame(minimumNumberofFramesToWaitSinceLastWait AS Byte) +ASM + push namespace core + PROC + LOCAL wait,enough,temp + + and a + ret m ; return if minimumNumberofFramesToWaitSinceLastWait < 0 + ld hl,temp + ld de, FRAMES + ld c,a ; C = minimumNumberofFramesToWaitSinceLastWait + + call VSYNC_TICK ; garantiza al menos un frame esperado (sustituye EI+HALT) + +wait: + ld a,(de) + sub (hl) ; A = number of frames since last RET + cp c ; is A >= C? Yes: NoCarry. No: Carry + jr nc,enough + call VSYNC_TICK ; espera otro frame (FRAMES lo incrementa VSYNC_TICK) + jr wait +enough: + ld a,(de) + ld (hl),a + ret +temp: + defb 0 + ENDP + pop namespace +END ASM +END SUB +#require "vsync.asm" + + +' ---------------------------------------------------------------- +' Get Interrupt Status in Border +' This SUB uses a hack to ensure a good reading of the IFF2 flip-flop. +' Hack was found thanks to Pedro Picapiedra aka ProgramadorHedonista. +' Border results: 2 Red if Interrupts are Disabled IFF2=0 +' 4 Green if Interrupts are Enabled IFF2=1 +' +' Version zx81sd: se deja sin cambios (no se llama nunca en el bucle +' principal, solo aparece comentada mas abajo; se mantiene unicamente +' para que la comprobacion de compilacion del final del fichero no falle +' por "funcion no usada"). En zx81sd siempre mostraria rojo, ya que las +' interrupciones estan permanentemente deshabilitadas. +' ---------------------------------------------------------------- +SUB FASTCALL GetInterruptStatusInBorder() +ASM + READ_IFF2 + push af + pop bc + ld a,c + and 4 ; A=0/4 iff IFF2=0/1 + rra ; A=0/2 iff IFF2=0/1 + add a,2 ; A=2/4 iff IFF2=0/1 + out (254),a +END ASM +END SUB + + +SUB FASTCALL Scroll() +ASM + PROC + LOCAL loop + ld hl,(.core.SCREEN_ADDR) + ld de,31 + ld b,192 +loop: +; Here, HL = screen address for first column + add hl,de; column 32 + xor a + rl (hl) ; 32 + dec l + rl (hl) ; 31 + dec l + rl (hl) ; 30 + dec l + rl (hl) ; 29 + dec l + rl (hl) ; 28 + dec l + rl (hl) ; 27 + dec l + rl (hl) ; 26 + dec l + rl (hl) ; 25 + dec l + rl (hl) ; 24 + dec l + rl (hl) ; 23 + dec l + rl (hl) ; 22 + dec l + rl (hl) ; 21 + dec l + rl (hl) ; 20 + dec l + rl (hl) ; 19 + dec l + rl (hl) ; 18 + dec l + rl (hl) ; 17 + dec l + rl (hl) ; 16 + dec l + rl (hl) ; 15 + dec l + rl (hl) ; 14 + dec l + rl (hl) ; 13 + dec l + rl (hl) ; 12 + dec l + rl (hl) ; 11 + dec l + rl (hl) ; 10 + dec l + rl (hl) ; 09 + dec l + rl (hl) ; 08 + dec l + rl (hl) ; 07 + dec l + rl (hl) ; 06 + dec l + rl (hl) ; 05 + dec l + rl (hl) ; 04 + dec l + rl (hl) ; 03 + dec l + rl (hl) ; 02 + dec l + rl (hl) ; 01 + rla ; save CF in A + add hl,de; column 32 + or (hl) + ld (hl),a + inc hl ; first column + djnz loop + ENDP +END ASM +END SUB + + +' ---------------------------------------------------------------- +' NEXT_ROW is a MACRO of ASM code, based on code from +' https://zonadepruebas.com/viewtopic.php?f=15&t=8372&start=40#p81507 +' and found by Joaquin Ferrero +' ---------------------------------------------------------------- +#define NEXT_ROW \ + sub 224 ; 7 A was L, now A = L + 32 (SUB 224 is similar to +32) \ + ; CF = 0/1 iff E >=/< 224 iff a third is/isn't crossed \ + ld l,a ; 4 \ + sbc a,a ; 4 A = 0/255 \ + and 248 ; 7 A = 0/248 (248 = -8) \ + add a,h ; 4 A = H/H-8 iff a third is/isn't crossed \ + ld h,a ; 4 += 30 Ts + + +' ---------------------------------------------------------------- +' Scroll 1 pixel up +' ---------------------------------------------------------------- +SUB FASTCALL Scroll1() +ASM + PROC + LOCAL loop1,loop2,exit + push ix + xor a + ld hl,(.core.SCREEN_ADDR); HL = $4000 or $c000 + ld de,buffer0 + call ldi32 ; scanline0 + ; DEC HL is not needed because row = 0 <> 7, 15, 23 + ld ixh,24 ; 24 rows (rows 0-23) +loop1: + ld l,a ; A = low byte of screen addresses for this row +; HL = scanline0 for this row + ld d,h + ld e,l ; DE = scanline0 of this row + inc h ; HL = scanline1 of this row + ld ixl,7 ; first 7 scanlines of one row +loop2: +; HL,DE = origin,destination screen address + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi ; BC-=32 DE+=32 HL+=32 (if E,L was 224, then D,H changes) + dec hl ; 6 in case H changes (because L was 224: rows 7,15,23) + ld l,a ; +4 = 10 Ts < 21 Ts (PUSH+POP) + ld d,h + ld e,l ; scanline_N+0 of this row + inc h ; scanline_N+1 of this row + dec ixl + jp nz,loop2 + dec ixh + jr z,exit ; this JR Z here (instead of an JP NZ later) adds +7 Ts/row = 161 Ts for 23 rows +; last scanline of first 23 rows: DE is OK, HL must change + NEXT_ROW + ld a,l + call ldi32 ; 539 Ts needed for each row except last row + dec hl ; with JR Z,EXIT before, we save 539-161 = 378 Ts for last row + jp loop1 +exit: + ld hl,buffer0 + call ldi32 ; DE = scanline7 of last row + pop ix + ENDP +END ASM +END SUB + + +' ---------------------------------------------------------------- +' Scroll 2 pixels up +' ---------------------------------------------------------------- +SUB FASTCALL Scroll2() +ASM + PROC + LOCAL loop1,loop2,exit + push ix + xor a + ld hl,(.core.SCREEN_ADDR); HL = $4000 or $c000 + ld de,buffer0 + call ldi32 ; scanline0 + ; DEC HL is not needed because row = 0 <> 7, 15, 23 + ld l,a + inc h + call ldi32 ; scanline1 + ; DEC HL is not needed because row = 0 <> 7, 15, 23 + ld ixh,24 ; 24 rows (rows 0-23) +loop1: + ld l,a ; A = low byte of screen addresses for this row +; HL = scanline1 for this row + ld d,h + ld e,l + dec d ; DE = scanline0 of this row + inc h ; HL = scanline2 of this row + ld ixl,6 ; first 6 scanlines of one row +loop2: +; HL,DE = origin,destination screen address + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi ; BC-=32 DE+=32 HL+=32 (if E,L was 224, then D,H changes) + dec hl ; 6 in case H changes (because L was 224: rows 7,15,23) + ld l,a ; +4 = 10 Ts < 21 Ts (PUSH+POP) + ld d,h + ld e,l + dec d ; DE = scanline_N+0 of this row + inc h ; HL = scanline_N+2 of this row + dec ixl + jp nz,loop2 + dec ixh + jr z,exit ; this JR Z here (instead of an JP NZ later) adds +7 Ts/row = 161 Ts for 23 rows +; last scanline of first 23 rows: DE is OK, HL must change + NEXT_ROW + ld c,h ; C=H is larger than 32, so B does not change when BC-=32 + ld b,e ; DE = scanline6 of this row SAVED in B + ld a,l ; HL = scanline0' of next row SAVED in A + call ldi32 ; 539 Ts needed for each row except last row + dec hl + ld l,a + dec de ; LD C,H + LD B,E + LD A,L + DEC HL + LD L,A + DEC DE + LD E,B + ld e,b ; = 4+4+4+6+4+6+4 = 32 Ts < 42 Ts 2*(PUSH+POP) + inc d ; DE = scanline7 of this row + inc h ; HL = scanline1' of next row + call ldi32 ; 539 Ts needed for each row except last row + dec hl ; with JR Z,EXIT before, we save 2*539-161 = 917 Ts for last row + jp loop1 +exit: + ld hl,buffer0 + call ldi32 ; DE = scanline6 of last row + dec de + ld e,a + inc d ; DE = scanline7 of last row + call ldi32 + pop ix + ret + ENDP + +buffer0: + defs 32 + +buffer1: + defs 32 + +ldi32: + ldi ; 32 LDI = 32*16 = 512 Ts + ldi ; CALL + RET = 17+10 = 27 Ts + ldi ; call ldi32 (+ret) = 512+27 = 539 Ts + ldi + ldi ; LD BC,32 + LDIR = 10 + 21*31+16 = 677 Ts + ldi + ldi ; 677 - 539 = 138 Ts + ldi + ldi ; then, "call ldi32 (+ret)" is 138 Ts faster than "ld bc,32 + ldir" + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi + ldi ; BC-=32 DE+=32 HL+=32 (if E,L was >=224, then D,H changes) +END ASM +END SUB diff --git a/examples/sd81/pegdemo.bas b/examples/sd81/pegdemo.bas new file mode 100644 index 000000000..83d43f820 --- /dev/null +++ b/examples/sd81/pegdemo.bas @@ -0,0 +1,54 @@ +' ---------------------------------------------------------------------- +' pegdemo.bas — PEG (Programmable Effects Generator) demo +' +' The PEG is a small virtual machine inside the SD81 Booster's MCU: it +' runs sound-effect programs on its own, driving the AY registers +' directly, with zero Z80 CPU cost. Up to 3 threads (0-2) can run at +' once. See the SD81 Booster manual, Appendix B, for the instruction +' set; McuLoadPeg's raw byte format is documented in mcu.bas ("2 bytes +' per instruction, little-endian"). +' +' This program below is a simple one-shot "beep": set AY channel A's +' tone period, enable tone on the mixer, set full volume, hold for +' ~200ms, then silence and halt. +' LD R0, $30 ; tone period A, low byte +' LD R1, $00 ; tone period A, high byte +' LD R7, $3E ; mixer: tone A enabled, everything else disabled +' LD R8, $0F ; channel A volume = max, no envelope +' WAIT 200 ; hold for 200 ms +' LD R8, $00 ; silence +' HALT +' +' NOTE: this exact byte encoding hasn't been confirmed on real hardware +' yet -- if it doesn't produce sound, double check the byte order +' against a working .PEB file or the peg.py assembler mentioned in the +' manual. +' ---------------------------------------------------------------------- + +#include +#include + +dim beep$ as string + +beep$ = chr($30) + chr($00) + _ + chr($00) + chr($01) + _ + chr($3E) + chr($07) + _ + chr($0F) + chr($08) + _ + chr($C8) + chr($90) + _ + chr($00) + chr($08) + _ + chr($10) + chr($A0) + +print "Loading PEG program at address 0..." +McuLoadPeg(0, beep$) + +print "Playing on thread 0..." +McuPlayPeg(0, 0) + +pause 100 + +print "Stopping thread 0." +McuStopPeg(0) + +print +print "Press a key..." +a$ = input(20) diff --git a/examples/sd81/pong.bas b/examples/sd81/pong.bas new file mode 100644 index 000000000..cc11a2888 --- /dev/null +++ b/examples/sd81/pong.bas @@ -0,0 +1,237 @@ + +10 REM Pong! Game + +14 REM Digit data + REM 128 => "\ " + REM 131 => "\''" + REM 138 => "\: " + REM 133 => "\ :" + REM 139 => "\:'" + REM 141 => "\.:" + REM 140 => "\.." + REM 135 => "\':" + REM 142 => "\:." + +15 DIM numbers(9, 2, 1) as Ubyte = { _ + {{139, 135}, {138, 133}, {142, 141}}, _ ' Number 0 + {{128, 133}, {128, 133}, {128, 133}}, _ ' Number 1 + {{131, 135}, {139, 131}, {142, 140}}, _ ' Number 2 + {{131, 135}, {131, 135}, {140, 141}}, _ ' Number 3 + {{138, 133}, {131, 135}, {128, 133}}, _ ' Number 4 + {{139, 131}, {131, 135}, {140, 141}}, _ ' Number 5 + {{139, 131}, {139, 135}, {142, 141}}, _ ' Number 6 + {{131, 135}, {128, 133}, {128, 133}}, _ ' Number 7 + {{139, 135}, {139, 135}, {142, 141}}, _ ' Number 8 + {{139, 135}, {131, 135}, {140, 141}} _ ' Number 9 + } +20 BORDER 0: PAPER 0: INK 7: BRIGHT 1: OVER 1: CLS + PRINT AT 10, 5; BOLD 1; "PRESS Q or 4 TO MOVE UP" + PRINT AT 11, 4; BOLD 1; "PRESS A or 3 TO MOVE DOWN": PAUSE 0 + DIM h, xx, yy, p, oldX, oldY As Byte + DIM x, y, dx, dy as Fixed + DIM px, py, NUM as Byte + DIM i, delay as Uinteger + DIM diff as Float = 0.5 : REM difficulty level + CONST minX As Byte = 0 + CONST minY As Byte = 0 + CONST maxX As Byte = 61 + CONST maxY As Byte = 47 + CONST comp As Byte = 0: REM Computer player is left + CONST user As Byte = 1: REM User player is left + CONST startX as Byte = maxX / 2: REM Ball initial X coord + CONST startY as Byte = maxY / 2: REM Ball initial Y coord + CONST dFast as UInteger = 400 + CONST dSlow as UInteger = 1000 + CONST diffRate As Float = 1.125: REM difficulty rate + CONST pointsToWin As Byte = 10: REM Points to Win + + DIM score(1) As Byte: REM Scores + +30 CLS: LET xx = 31: FOR yy = minY TO maxY STEP 2: GOSUB 2000: NEXT yy +40 DIM coords(1, 1) As Byte: REM Player 0 (Left) and 1 (Right) coordinates +50 LET h = 5: REM players height (in "points"). A "point" is 4 pixels +60 LET x = startX: LET y = startY: REM Screen resolution is 64 +70 LET coords(0, 0) = minX: LET coords(1, 0) = maxX +80 LET coords(0, 1) = minY + (maxY - minY) / 2: LET coords(1, 1) = coords(0, 1) +90 LET dx = 1: LET dy = 1: LET delay = dSlow: REM Ball speed +91 LET score(0) = 0: LET score(1) = 0: REM Initializes scores + +93 FOR p = 0 TO 1: GOSUB 1000: NEXT p: REM Draw players +94 GOSUB 3000: REM Print scores + +95 REM Draws Ball. Begin of GAME LOOP +96 LET xx = x: LET yy = y: LET oldX = x: LET oldY = y: GOSUB 2000 +100 LET x = x + dx +110 IF x < minX THEN + LET x = minX: LET dx = -dx: BEEP 0.02,20 + LET py = coords(0, 1) + IF py > y OR py + h <= y THEN + REM Player 2 wins 1 Point + LET score(1) = score(1) + 1 + GOSUB 3000 + LET y = startY + LET x = coords(1, 0) + LET dx = -dx + LET delay = dSlow + IF comp <> 1 THEN + LET diff = diff / diffRate + ELSE + LET diff = diff * diffRate + END IF + ELSE + IF y = py OR y = py + h - 1 THEN + delay = dFast + ELSE + delay = dSlow + END IF + END IF + ELSEIF x > maxX THEN + LET x = maxX: LET dx = -dx: BEEP 0.02,20 + LET py = coords(1, 1) + IF py > y OR py + h <= y THEN + REM Player 1 wins 1 Point + LET score(0) = score(0) + 1 + GOSUB 3000 + LET y = startY + LET x = coords(0, 0) + LET dx = -dx + LET delay = dSlow + IF comp <> 0 THEN + LET diff = diff / diffRate + ELSE + LET diff = diff * diffRate + END IF + ELSE + IF y = py OR y = py + h - 1 THEN + delay = dFast + ELSE + delay = dSlow + END IF + END IF + END IF + +120 LET y = y + dy +130 IF y < minY THEN + LET y = minY: BEEP 0.02,10: LET dy = -dy + ELSEIF y > maxY THEN + LET y = maxY: BEEP 0.02,10: LET dy = -dy + END IF + +140 REM Checks if computer must move +150 LET px = coords(comp, 0): LET py = coords(comp, 1) +160 IF py > y AND RND > diff THEN REM Must go up + LET p = comp + GOSUB 1500: REM Updates player padel (up) + ELSEIF py + h - 1 < y AND RND > diff THEN + LET p = comp + GOSUB 1600: REM Updates player padel (down) + END IF + +200 REM Checks if Player moves ("4", "3") +210 LET px = coords(user, 0): LET py = coords(user, 1) +220 if py > minY AND (INKEY$ = "4" OR INKEY$ = "q") THEN REM Must go up + LET p = user + GOSUB 1500: REM Updates player padel (up) + ELSEIF py + h < maxY AND (INKEY$ = "3" OR INKEY$ = "a") THEN REM Must go down + LET p = user + GOSUB 1600: REM Updates player padel (down) + END IF + +250 FOR i = 0 TO delay: NEXT i + +500 LET xx = oldX: LET yy = oldY + ASM + call .core.VSYNC_TICK ; garantiza al menos un frame esperado (sustituye EI+HALT) + ; halt ; Avoids screen flickering + END ASM + GOSUB 2000: REM erases the ball + GOTO 95: REM Game loop + +0999 END +1000 REM Draws player p at p(p, 0), p(p, 1) +1010 LET xx = coords(p, 0) +1020 LET yy = coords(p, 1) +1030 FOR i = 1 TO h: GOSUB 2000: LET yy = yy + 1: NEXT i +1040 RETURN + +1500 REM Moves up the padel for user p (p = 0 => Left, p = 1 = Right) +1510 LET xx = px +1520 FOR i = 0 TO 1: IF py <= minY THEN EXIT FOR: END IF +1530 LET py = py - 1 +1540 LET yy = py: GOSUB 2000: REM Draws padel top +1550 LET yy = py + h: GOSUB 2000: REM Erases padel bottom +1560 NEXT i +1570 LET coords(p, 1) = py: RETURN + +1600 REM Moves down the padel for user p (p = 0 => Left, p = 1 = Right) +1610 LET xx = px: LET yy = py +1620 FOR i = 0 TO 1: IF py + h > maxY THEN EXIT FOR: END IF +1630 LET yy = py: GOSUB 2000: REM Erases padel top +1640 LET yy = py + h: GOSUB 2000: REM Draws padel bottom +1650 LET py = py + 1 +1660 NEXT i +1670 LET coords(p, 1) = py: RETURN + +2000 REM Draws a "Point" at xx,yy +2010 LET cx1 = xx bAND 1: LET cy1 = yy bAND 1 +2020 LET cx2 = xx SHR 1: LET cy2 = yy SHR 1 +2040 IF cx1 THEN + IF cy1 THEN + LET c$ = "\ ." + ELSE + LET c$ = "\ '" + END IF + ELSE + IF cy1 THEN + LET c$ = "\. " + ELSE + LET c$ = "\' " + END IF + END IF + +2050 PRINT AT cy2, cx2; c$; +2060 RETURN + +3000 REM Prints SCORES +3010 FOR player = 0 TO 1 +3020 LET atY = minY + 1: LET atX = minX + 4 + player * (maxX - minX) / 4: LET sc = score(player) +3030 IF sc < 10 THEN REM erases left digit + FOR i = 0 TO 2: PRINT OVER 0; AT atY + i, atX; " ";:NEXT i + ELSE + LET NUM = sc / 10: REM Left digit + GOSUB 5000 + END IF +3040 LET atX = atX + 3 +3050 LET NUM = sc Mod 10 +3060 GOSUB 5000 +3070 NEXT player +3080 GOSUB 6000 +3090 RETURN + +5000 REM Prints Number NUM at atY, atX +5010 FOR ny = 0 TO 2: FOR nx = 0 TO 1 +5020 PRINT OVER 0; AT atY + ny, atX + nx; CHR$(numbers(NUM, ny, nx)); +5030 NEXT nx: NEXT ny +5040 RETURN + +6000 REM Check scores +6010 FOR player = 0 TO 1 +6020 IF score(player) >= pointsToWin THEN + CLS + PRINT AT 10, 10; + IF player = comp THEN + PRINT BOLD 1; "I WIN!" + ELSE + PRINT BOLD 1; "YOU WIN!" + END IF + DO LOOP WHILE INKEY$ <> "" + PAUSE 500 + PRINT AT 21, 3; BOLD 1; "PRESS ANY KEY TO CONTINUE"; + PAUSE 0 + GOTO 30 + END IF +6030 NEXT player +6040 RETURN + +#require "vsync.asm" + diff --git a/examples/sd81/print4264.bas b/examples/sd81/print4264.bas new file mode 100644 index 000000000..99c60d5f0 --- /dev/null +++ b/examples/sd81/print4264.bas @@ -0,0 +1,13 @@ +#include +#include + +CLS +PRINT AT 0, 0; "print42/print64 test" + +printat42(3, 0) +print42("HELLO 42 COLUMNS TEST 0123456789") + +printat64(10, 0) +print64("HELLO 64 COLUMNS TEST 0123456789") + +PAUSE 0 diff --git a/examples/sd81/putcharstile.bas b/examples/sd81/putcharstile.bas new file mode 100644 index 000000000..3ed652596 --- /dev/null +++ b/examples/sd81/putcharstile.bas @@ -0,0 +1,48 @@ +' ---------------------------------------------------------------------- +' putcharstile.bas — putchars.bas (putChars/paint) and puttile.bas (putTile) +' +' putchars.bas: audited, no ROM dependency (everything reads +' SCREEN_ADDR/SCREEN_ATTR_ADDR dynamically) -- no zx81sd override +' needed, used as-is from the shared zx48k stdlib. +' +' puttile.bas: DID need a zx81sd override -- the original hardcodes the +' screen/attribute base as immediate constants (add a,64 / add a,88, +' the Spectrum ROM's fixed $4000/$5800 high bytes) instead of reading +' SCREEN_ADDR/SCREEN_ATTR_ADDR. See src/lib/arch/zx81sd/stdlib/puttile.bas. +' ---------------------------------------------------------------------- + +#include +#include +#include + +dim i as ubyte +dim charData(95) as ubyte +dim tileData(35) as ubyte + +' 12 character cells (4 wide x 3 tall), 8 bytes each: a houndstooth fill +for i = 0 to 95 + poke @charData(0) + i, 170 +next i + +' 16x16 tile: 16 pixel-rows (2 bytes each, one per column) + top/bottom attrs +for i = 0 to 15 + poke @tileData(0) + i * 2, 255 + poke @tileData(0) + i * 2 + 1, 129 +next i +poke @tileData(0) + 32, 56 +poke @tileData(0) + 33, 56 +poke @tileData(0) + 34, 63 +poke @tileData(0) + 35, 63 + +ink 7: paper 1: cls + +putChars(1, 1, 4, 3, @charData(0)) +paint(6, 1, 4, 3, 71) + +putTile(10, 10, @tileData(0)) + +' fixed-row PRINT away from the last screen row: avoids triggering a +' scroll (input()'s own cursor handling can push past row 23) that +' would shift the drawing above right before you get to look at it +print at 20, 0; "putChars/paint/putTile - press a key"; +a$ = input(20) diff --git a/examples/sd81/randomaccess.bas b/examples/sd81/randomaccess.bas new file mode 100644 index 000000000..e2ee44e97 --- /dev/null +++ b/examples/sd81/randomaccess.bas @@ -0,0 +1,70 @@ +' ---------------------------------------------------------------------- +' randomaccess.bas — Random file access via the SD81 Booster MCU +' +' Demonstrates the F_* handle-based commands in mcu.bas: F_OPEN, +' F_SEEK, F_READ, F_WRITE and F_CLOSE. Unlike McuLoad/McuSave (which +' always transfer the whole file), these let a program seek to an +' arbitrary offset and read or write just part of a file -- useful for +' things like save-game slots, tile/level data files, or databases too +' big to keep fully in RAM. +' ---------------------------------------------------------------------- + +#include +#include + +dim h as ubyte +dim st as ubyte +dim buf(63) as ubyte +dim i as uinteger +dim ok as ubyte + +print "Creating a 64-byte test file..." +for i = 0 to 63 + poke @buf(0) + i, cast(ubyte, 200 - i) +next i +st = McuSave("RNDACC.BIN", @buf(0), 64) +print "SAVE: status "; st + +print "Opening it..." +h = McuFOpenZx("RNDACC.BIN") +print "F_OPEN: handle "; h + +print "Seeking to offset 32 and reading 8 bytes..." +st = McuFSeek(h, 32) +for i = 0 to 7 + poke @buf(0) + i, 0 +next i +st = McuFRead(h, @buf(0), 8) +ok = 1 +for i = 0 to 7 + if peek(@buf(0) + i) <> 200 - (32 + i) then + ok = 0 + end if +next i +if ok = 1 then + print "Read back correctly." +else + print "Read verification FAILED." +end if + +print "Overwriting the first 4 bytes and reading them back..." +st = McuFSeek(h, 0) +poke @buf(32), 11 +poke @buf(33), 22 +poke @buf(34), 33 +poke @buf(35), 44 +st = McuFWrite(h, @buf(32), 4) +st = McuFSeek(h, 0) +st = McuFRead(h, @buf(40), 4) +if peek(@buf(40)) = 11 and peek(@buf(43)) = 44 then + print "Overwrite verified." +else + print "Overwrite verification FAILED." +end if + +print "F_CLOSE: status "; McuFClose(h) +print "Deleting test file: status "; McuDel("RNDACC.BIN") + +print +print "Press a key..." +a$ = input(20) diff --git a/examples/sd81/snake_sd81.bas b/examples/sd81/snake_sd81.bas new file mode 100644 index 000000000..8b8231953 --- /dev/null +++ b/examples/sd81/snake_sd81.bas @@ -0,0 +1,165 @@ +1 REM ******************************************************************** +2 REM ZXSnake by Federico J. Alvarez Valero (05-02-2003) +10 REM This program is free software; you can redistribute it and/or modify +11 REM it under the terms of the GNU General Public License as published by +12 REM the Free Software Foundation; either version 2 of the License, or +13 REM (at your option) any later version. +14 REM This program is distributed in the hope that it will be useful, +15 REM but WITHOUT ANY WARRANTY; without even the implied warranty of +16 REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +17 REM GNU General Public License for more details. +18 REM You should have received a copy of the GNU General Public License +19 REM along with this program; if not, write to the Free Software +20 REM Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +30 REM ENGLISH VERSION +40 REM ******************************************************************** + +50 BORDER 7 : PAPER 7 : INK 0 : CLS +51 PRINT AT 3,13 ; PAPER 1 ; INK 7 ; "ZXSnake" +52 PRINT AT 5,9 ; PAPER 7 ; INK 0 ; "Q - Up" +53 PRINT AT 6,9 ; PAPER 7 ; INK 0 ; "A - Down" +54 PRINT AT 7,9 ; PAPER 7 ; INK 0 ; "O - Left" +55 PRINT AT 8,9 ; PAPER 7 ; INK 0 ; "P - Right" +56 PRINT AT 10,3 ; PAPER 7 ; INK 0 ; "You have to pick up every" +57 PRINT AT 11,3 ; PAPER 7 ; INK 0 ; "fruit on the screen while" +58 PRINT AT 12,3 ; PAPER 7 ; INK 0 ; "growing up more and more..." +59 PRINT AT 15,3 ; PAPER 7 ; INK 0 ; "Press any key to start" +60 LET j$ = INKEY$ +61 IF j$ = "" THEN GOTO 60: END IF + +70 REM UDG +71 DIM udg(1, 7) AS uByte => { {60, 66, 129, 129, 129, 129, 66, 60}, _ + {24, 60, 60, 60, 126, 251, 247, 126}} +73 POKE UINTEGER $8002, @udg(0, 0): REM sysvar UDG de zx81sd (en Spectrum era 23675) +74 LET S$ = CHR$(144): LET F$ = CHR$(145) + +75 REM Variable declaration +76 DIM p(23,34) AS UBYTE: REM Screen +77 DIM x(23,34) AS UBYTE: REM Xorientations +78 DIM y(23,34) AS UBYTE: REM Yorientations + +79 DIM c, f AS UBYTE +80 DIM headx, heady AS UBYTE : REM Head coordinates +81 DIM tailx, taily AS UBYTE : REM Tail coordinates +90 DIM score, eaten as ULONG +95 DIM maxx, maxy, minx, miny as UByte + +100 REM Variable definition +110 LET headx = 11 : REM head x coordinate +120 LET heady = 5 : REM head y coordinate +130 LET tailx = 5 : REM tail x coordinate +140 LET taily = 5 : REM tail y coordinate +150 LET orientationx = 1 +160 LET orientationy = 0 + +165 REM Clear arrays p, x and y +170 FOR c = 1 to 23: FOR f = 1 to 34 +180 LET p(c, f) = 0: LET x(c, f) = 0: LET y(c, f) = 0 +190 NEXT f: NEXT c + +200 LET score = 0 +210 LET eaten = 0 +220 LET maxx = 33 +230 LET maxy = 22 +240 LET minx = 0 +250 LET miny = 0 + +1000 REM Screen Initialization +1010 BORDER 1 +1015 CLS +1020 PRINT AT 21,0 ; PAPER 1 ; INK 7 ; " SCORE : " +1030 FOR c = minx TO maxx +1040 LET p(miny+1,c+1) = 4 +1050 LET p(maxy+1,c+1) = 4 +1060 NEXT c +1070 FOR f = miny TO maxy +1080 LET p(f+1,minx+1) = 4 +1090 LET p(f+1,maxx+1) = 4 +1100 NEXT f + +1500 GOSUB 8000 : REM Place first fruit + +2000 REM Draw snake in its initial position +2001 PAPER 7 : INK 0 +2005 REM Draw the body +2010 FOR c = tailx TO headx-1 +2020 PRINT AT taily,c ; INK 0 ; "O" +2025 LET p(taily+2,c+2) = 3 +2026 LET x(taily+2,c+2) = 1 +2027 LET y(taily+2,c+2) = 0 +2030 NEXT c +2040 REM Draw the Head +2050 PRINT AT heady,headx ; INK 0 ; S$; +2055 LET p(heady+2,headx+2) = 2 +2056 LET x(heady+2,headx+2) = 1 +2057 LET y(heady+2,headx+2) = 0 + +3000 REM Update snake position +3005 INK 0 +3010 REM Change the orientation if needed +3015 LET x(heady+2,headx+2) = orientationx +3020 LET y(heady+2,headx+2) = orientationy +3025 REM Erase previous head +3030 PRINT AT heady,headx ; "O" +3035 LET p(heady+2,headx+2) = 3 +3040 LET headx = headx + orientationx +3045 LET heady = heady + orientationy +3050 IF p(heady+2,headx+2) > 1 THEN GOTO 9900: END IF +3051 IF p(heady+2,headx+2) = 1 THEN + LET score = score + 10 : PRINT AT 21,10 ; _ + PAPER 1 ; INK 7 ; score : LET eaten = 1 : GOSUB 8000: END IF +3055 REM Print the new head +3060 PRINT AT heady,headx ; S$; +3065 LET p(heady+2,headx+2) = 2 +3070 IF eaten = 0 THEN GOSUB 8100: END IF +3080 LET eaten = 0 + +3200 REM Read the keyboard +3210 LET a$ = INKEY$ +3220 IF orientationx < 1 AND (a$ = "O" OR a$ = "o") THEN + LET orientationx = -1 : LET orientationy = 0: END IF +3230 IF orientationx > -1 AND (a$ = "P" OR a$ = "p") THEN + LET orientationx = 1 : LET orientationy = 0: END IF +3240 IF orientationy < 1 AND (a$ = "Q" OR a$ = "q") THEN + LET orientationx = 0 : LET orientationy = -1: END IF +3250 IF orientationy > -1 AND (a$ = "A" OR a$ = "a") THEN + LET orientationx = 0 : LET orientationy = 1: END IF + +3500 REM Pausa / Delay +3505 BEEP 0.005, 0 +3510 PAUSE 5 + +7998 GOTO 3000 + +8000 REM Fruit placement +8010 LET fruitx = INT(RND*30)+1 +8020 LET fruity = INT(RND*20)+1 +8030 IF p(fruity+2,fruitx+2) = 0 THEN GOTO 8050: END IF +8040 GOTO 8010 +8050 PRINT AT fruity,fruitx ; INK 2 ; F$; +8060 LET p(fruity+2,fruitx+2) = 1 +8070 RETURN + +8100 REM Erase tail +8110 PRINT AT taily,tailx ; " " +8120 LET newtailx = tailx + x(taily+2,tailx+2) +8130 LET newtaily = taily + y(taily+2,tailx+2) +8140 LET p(taily+2,tailx+2) = 0 +8150 LET x(taily+2,tailx+2) = 0 +8160 LET y(taily+2,tailx+2) = 0 +8170 LET tailx = newtailx +8180 LET taily = newtaily +8190 RETURN + +9900 REM Game over +9910 PRINT AT 10,12 ; INK 2 ; "GAME OVER..." +9920 PRINT AT 11,10 ; INK 2 ; "SCORE : " ; score +9930 PRINT AT 13,10 ; INK 0 ; "Press any key" +9931 REM Mondatory pause so the msg. can be read +9932 FOR i = 0 TO 100 + +9933 NEXT i +9940 LET j$ = INKEY$ +9950 IF j$ <> "" THEN GOTO 100: END IF +9960 GOTO 9940 + diff --git a/examples/sd81/vgmplayer.bas b/examples/sd81/vgmplayer.bas new file mode 100644 index 000000000..cddb821ae --- /dev/null +++ b/examples/sd81/vgmplayer.bas @@ -0,0 +1,50 @@ +' ---------------------------------------------------------------------- +' vgmplayer.bas — VGM music playback via the SD81 Booster MCU +' +' The MCU's built-in VGM player runs entirely in the background (only +' AY-3-8910/8912 data is supported): the Z80 program keeps running +' while the tune plays. McuPlayVgm() opens/prepares the file (adding +' .vgm if no extension is given); playback only actually starts once +' McuContVgm() is called. +' +' Change VGM_NAME below to a file that actually exists on your SD card. +' ---------------------------------------------------------------------- + +#include +#include + +const VGM_NAME as string = "MUSIC.VGM" + +dim st as ubyte +dim i as ubyte + +print "Loading "; VGM_NAME; "..." +st = McuPlayVgm(VGM_NAME) +print "MCU status: "; st + +print "Starting playback..." +McuContVgm() + +' let it play for a few seconds while the Z80 keeps doing its own thing +for i = 1 to 5 + print "playing... "; i + pause 50 +next i + +print "Pausing 2 seconds..." +McuPauseVgm() +pause 100 +print "Resuming..." +McuContVgm() + +for i = 1 to 5 + print "playing... "; i + pause 50 +next i + +print "Stopping." +McuStopVgm() + +print +print "Press a key..." +a$ = input(20) diff --git a/examples/sd81/wavplayer.bas b/examples/sd81/wavplayer.bas new file mode 100644 index 000000000..c4dc197aa --- /dev/null +++ b/examples/sd81/wavplayer.bas @@ -0,0 +1,25 @@ +' ---------------------------------------------------------------------- +' wavplayer.bas — WAV audio playback via the SD81 Booster MCU +' +' The MCU's LOAD command (cmd 9, wrapped here as McuLoad) recognises +' the .WAV extension automatically: instead of loading the file into +' memory it plays it directly (uncompressed PCM, 8-bit mono, 11025 Hz +' per the manual). McuLoad returns 0 bytes in that case; the real +' result is in McuStatus. +' +' Change WAV_NAME below to a file that actually exists on your SD card +' (with or without the .WAV extension). +' ---------------------------------------------------------------------- + +#include +#include + +const WAV_NAME as string = "DEMO.WAV" + +print "Playing "; WAV_NAME; " ..." +McuLoad(WAV_NAME, 0) +print "Done. MCU status: "; McuStatus + +print +print "Press a key..." +a$ = input(20) diff --git a/poetry.lock b/poetry.lock index ffab4fdd8..8c580b3b3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,844 +1,1102 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. - -[[package]] -name = "bump2version" -version = "1.0.1" -description = "Version-bump your software with a single command!" -optional = false -python-versions = ">=3.5" -files = [ - {file = "bump2version-1.0.1-py2.py3-none-any.whl", hash = "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410"}, - {file = "bump2version-1.0.1.tar.gz", hash = "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6"}, -] - -[[package]] -name = "cfgv" -version = "3.4.0" -description = "Validate configuration and produce human readable error messages." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, - {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, -] - -[[package]] -name = "click" -version = "8.2.1" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.10" -files = [ - {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, - {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "coverage" -version = "7.9.2" -description = "Code coverage measurement for Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "coverage-7.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:66283a192a14a3854b2e7f3418d7db05cdf411012ab7ff5db98ff3b181e1f912"}, - {file = "coverage-7.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4e01d138540ef34fcf35c1aa24d06c3de2a4cffa349e29a10056544f35cca15f"}, - {file = "coverage-7.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f22627c1fe2745ee98d3ab87679ca73a97e75ca75eb5faee48660d060875465f"}, - {file = "coverage-7.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b1c2d8363247b46bd51f393f86c94096e64a1cf6906803fa8d5a9d03784bdbf"}, - {file = "coverage-7.9.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c10c882b114faf82dbd33e876d0cbd5e1d1ebc0d2a74ceef642c6152f3f4d547"}, - {file = "coverage-7.9.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:de3c0378bdf7066c3988d66cd5232d161e933b87103b014ab1b0b4676098fa45"}, - {file = "coverage-7.9.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1e2f097eae0e5991e7623958a24ced3282676c93c013dde41399ff63e230fcf2"}, - {file = "coverage-7.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28dc1f67e83a14e7079b6cea4d314bc8b24d1aed42d3582ff89c0295f09b181e"}, - {file = "coverage-7.9.2-cp310-cp310-win32.whl", hash = "sha256:bf7d773da6af9e10dbddacbf4e5cab13d06d0ed93561d44dae0188a42c65be7e"}, - {file = "coverage-7.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:0c0378ba787681ab1897f7c89b415bd56b0b2d9a47e5a3d8dc0ea55aac118d6c"}, - {file = "coverage-7.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a7a56a2964a9687b6aba5b5ced6971af308ef6f79a91043c05dd4ee3ebc3e9ba"}, - {file = "coverage-7.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:123d589f32c11d9be7fe2e66d823a236fe759b0096f5db3fb1b75b2fa414a4fa"}, - {file = "coverage-7.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:333b2e0ca576a7dbd66e85ab402e35c03b0b22f525eed82681c4b866e2e2653a"}, - {file = "coverage-7.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:326802760da234baf9f2f85a39e4a4b5861b94f6c8d95251f699e4f73b1835dc"}, - {file = "coverage-7.9.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19e7be4cfec248df38ce40968c95d3952fbffd57b400d4b9bb580f28179556d2"}, - {file = "coverage-7.9.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0b4a4cb73b9f2b891c1788711408ef9707666501ba23684387277ededab1097c"}, - {file = "coverage-7.9.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2c8937fa16c8c9fbbd9f118588756e7bcdc7e16a470766a9aef912dd3f117dbd"}, - {file = "coverage-7.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:42da2280c4d30c57a9b578bafd1d4494fa6c056d4c419d9689e66d775539be74"}, - {file = "coverage-7.9.2-cp311-cp311-win32.whl", hash = "sha256:14fa8d3da147f5fdf9d298cacc18791818f3f1a9f542c8958b80c228320e90c6"}, - {file = "coverage-7.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:549cab4892fc82004f9739963163fd3aac7a7b0df430669b75b86d293d2df2a7"}, - {file = "coverage-7.9.2-cp311-cp311-win_arm64.whl", hash = "sha256:c2667a2b913e307f06aa4e5677f01a9746cd08e4b35e14ebcde6420a9ebb4c62"}, - {file = "coverage-7.9.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae9eb07f1cfacd9cfe8eaee6f4ff4b8a289a668c39c165cd0c8548484920ffc0"}, - {file = "coverage-7.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ce85551f9a1119f02adc46d3014b5ee3f765deac166acf20dbb851ceb79b6f3"}, - {file = "coverage-7.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8f6389ac977c5fb322e0e38885fbbf901743f79d47f50db706e7644dcdcb6e1"}, - {file = "coverage-7.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d9eae8cdfcd58fe7893b88993723583a6ce4dfbfd9f29e001922544f95615"}, - {file = "coverage-7.9.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae939811e14e53ed8a9818dad51d434a41ee09df9305663735f2e2d2d7d959b"}, - {file = "coverage-7.9.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:31991156251ec202c798501e0a42bbdf2169dcb0f137b1f5c0f4267f3fc68ef9"}, - {file = "coverage-7.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d0d67963f9cbfc7c7f96d4ac74ed60ecbebd2ea6eeb51887af0f8dce205e545f"}, - {file = "coverage-7.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:49b752a2858b10580969ec6af6f090a9a440a64a301ac1528d7ca5f7ed497f4d"}, - {file = "coverage-7.9.2-cp312-cp312-win32.whl", hash = "sha256:88d7598b8ee130f32f8a43198ee02edd16d7f77692fa056cb779616bbea1b355"}, - {file = "coverage-7.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:9dfb070f830739ee49d7c83e4941cc767e503e4394fdecb3b54bfdac1d7662c0"}, - {file = "coverage-7.9.2-cp312-cp312-win_arm64.whl", hash = "sha256:4e2c058aef613e79df00e86b6d42a641c877211384ce5bd07585ed7ba71ab31b"}, - {file = "coverage-7.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:985abe7f242e0d7bba228ab01070fde1d6c8fa12f142e43debe9ed1dde686038"}, - {file = "coverage-7.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c3939264a76d44fde7f213924021ed31f55ef28111a19649fec90c0f109e6d"}, - {file = "coverage-7.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae5d563e970dbe04382f736ec214ef48103d1b875967c89d83c6e3f21706d5b3"}, - {file = "coverage-7.9.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdd612e59baed2a93c8843c9a7cb902260f181370f1d772f4842987535071d14"}, - {file = "coverage-7.9.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:256ea87cb2a1ed992bcdfc349d8042dcea1b80436f4ddf6e246d6bee4b5d73b6"}, - {file = "coverage-7.9.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f44ae036b63c8ea432f610534a2668b0c3aee810e7037ab9d8ff6883de480f5b"}, - {file = "coverage-7.9.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82d76ad87c932935417a19b10cfe7abb15fd3f923cfe47dbdaa74ef4e503752d"}, - {file = "coverage-7.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:619317bb86de4193debc712b9e59d5cffd91dc1d178627ab2a77b9870deb2868"}, - {file = "coverage-7.9.2-cp313-cp313-win32.whl", hash = "sha256:0a07757de9feb1dfafd16ab651e0f628fd7ce551604d1bf23e47e1ddca93f08a"}, - {file = "coverage-7.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:115db3d1f4d3f35f5bb021e270edd85011934ff97c8797216b62f461dd69374b"}, - {file = "coverage-7.9.2-cp313-cp313-win_arm64.whl", hash = "sha256:48f82f889c80af8b2a7bb6e158d95a3fbec6a3453a1004d04e4f3b5945a02694"}, - {file = "coverage-7.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:55a28954545f9d2f96870b40f6c3386a59ba8ed50caf2d949676dac3ecab99f5"}, - {file = "coverage-7.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cdef6504637731a63c133bb2e6f0f0214e2748495ec15fe42d1e219d1b133f0b"}, - {file = "coverage-7.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd5ebe66c7a97273d5d2ddd4ad0ed2e706b39630ed4b53e713d360626c3dbb3"}, - {file = "coverage-7.9.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9303aed20872d7a3c9cb39c5d2b9bdbe44e3a9a1aecb52920f7e7495410dfab8"}, - {file = "coverage-7.9.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc18ea9e417a04d1920a9a76fe9ebd2f43ca505b81994598482f938d5c315f46"}, - {file = "coverage-7.9.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6406cff19880aaaadc932152242523e892faff224da29e241ce2fca329866584"}, - {file = "coverage-7.9.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d0d4f6ecdf37fcc19c88fec3e2277d5dee740fb51ffdd69b9579b8c31e4232e"}, - {file = "coverage-7.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c33624f50cf8de418ab2b4d6ca9eda96dc45b2c4231336bac91454520e8d1fac"}, - {file = "coverage-7.9.2-cp313-cp313t-win32.whl", hash = "sha256:1df6b76e737c6a92210eebcb2390af59a141f9e9430210595251fbaf02d46926"}, - {file = "coverage-7.9.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f5fd54310b92741ebe00d9c0d1d7b2b27463952c022da6d47c175d246a98d1bd"}, - {file = "coverage-7.9.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c48c2375287108c887ee87d13b4070a381c6537d30e8487b24ec721bf2a781cb"}, - {file = "coverage-7.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ddc39510ac922a5c4c27849b739f875d3e1d9e590d1e7b64c98dadf037a16cce"}, - {file = "coverage-7.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a535c0c7364acd55229749c2b3e5eebf141865de3a8f697076a3291985f02d30"}, - {file = "coverage-7.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df0f9ef28e0f20c767ccdccfc5ae5f83a6f4a2fbdfbcbcc8487a8a78771168c8"}, - {file = "coverage-7.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f3da12e0ccbcb348969221d29441ac714bbddc4d74e13923d3d5a7a0bebef7a"}, - {file = "coverage-7.9.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a17eaf46f56ae0f870f14a3cbc2e4632fe3771eab7f687eda1ee59b73d09fe4"}, - {file = "coverage-7.9.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:669135a9d25df55d1ed56a11bf555f37c922cf08d80799d4f65d77d7d6123fcf"}, - {file = "coverage-7.9.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9d3a700304d01a627df9db4322dc082a0ce1e8fc74ac238e2af39ced4c083193"}, - {file = "coverage-7.9.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:71ae8b53855644a0b1579d4041304ddc9995c7b21c8a1f16753c4d8903b4dfed"}, - {file = "coverage-7.9.2-cp39-cp39-win32.whl", hash = "sha256:dd7a57b33b5cf27acb491e890720af45db05589a80c1ffc798462a765be6d4d7"}, - {file = "coverage-7.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:f65bb452e579d5540c8b37ec105dd54d8b9307b07bcaa186818c104ffda22441"}, - {file = "coverage-7.9.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:8a1166db2fb62473285bcb092f586e081e92656c7dfa8e9f62b4d39d7e6b5050"}, - {file = "coverage-7.9.2-py3-none-any.whl", hash = "sha256:e425cd5b00f6fc0ed7cdbd766c70be8baab4b7839e4d4fe5fac48581dd968ea4"}, - {file = "coverage-7.9.2.tar.gz", hash = "sha256:997024fa51e3290264ffd7492ec97d0690293ccd2b45a6cd7d82d945a4a80c8b"}, -] - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "distlib" -version = "0.4.0" -description = "Distribution utilities" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"}, - {file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"}, -] - -[[package]] -name = "execnet" -version = "2.1.1" -description = "execnet: rapid multi-Python deployment" -optional = false -python-versions = ">=3.8" -files = [ - {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, - {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, -] - -[package.extras] -testing = ["hatch", "pre-commit", "pytest", "tox"] - -[[package]] -name = "filelock" -version = "3.18.0" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.9" -files = [ - {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"}, - {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] -typing = ["typing-extensions (>=4.12.2)"] - -[[package]] -name = "ghp-import" -version = "2.1.0" -description = "Copy your docs directly to the gh-pages branch." -optional = false -python-versions = "*" -files = [ - {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, - {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, -] - -[package.dependencies] -python-dateutil = ">=2.8.1" - -[package.extras] -dev = ["flake8", "markdown", "twine", "wheel"] - -[[package]] -name = "identify" -version = "2.6.12" -description = "File identification library for Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2"}, - {file = "identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6"}, -] - -[package.extras] -license = ["ukkonen"] - -[[package]] -name = "iniconfig" -version = "2.1.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.8" -files = [ - {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, - {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -description = "A very fast and expressive template engine." -optional = false -python-versions = ">=3.7" -files = [ - {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, - {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, -] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "markdown" -version = "3.8.2" -description = "Python implementation of John Gruber's Markdown." -optional = false -python-versions = ">=3.9" -files = [ - {file = "markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24"}, - {file = "markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45"}, -] - -[package.extras] -docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] -testing = ["coverage", "pyyaml"] - -[[package]] -name = "markupsafe" -version = "3.0.2" -description = "Safely add untrusted strings to HTML/XML markup." -optional = false -python-versions = ">=3.9" -files = [ - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, - {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, -] - -[[package]] -name = "mergedeep" -version = "1.3.4" -description = "A deep merge function for 🐍." -optional = false -python-versions = ">=3.6" -files = [ - {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, - {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, -] - -[[package]] -name = "mkdocs" -version = "1.6.1" -description = "Project documentation with Markdown." -optional = false -python-versions = ">=3.8" -files = [ - {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, - {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, -] - -[package.dependencies] -click = ">=7.0" -colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} -ghp-import = ">=1.0" -jinja2 = ">=2.11.1" -markdown = ">=3.3.6" -markupsafe = ">=2.0.1" -mergedeep = ">=1.3.4" -mkdocs-get-deps = ">=0.2.0" -packaging = ">=20.5" -pathspec = ">=0.11.1" -pyyaml = ">=5.1" -pyyaml-env-tag = ">=0.1" -watchdog = ">=2.0" - -[package.extras] -i18n = ["babel (>=2.9.0)"] -min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] - -[[package]] -name = "mkdocs-get-deps" -version = "0.2.0" -description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" -optional = false -python-versions = ">=3.8" -files = [ - {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, - {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, -] - -[package.dependencies] -mergedeep = ">=1.3.4" -platformdirs = ">=2.2.0" -pyyaml = ">=5.1" - -[[package]] -name = "mypy" -version = "1.17.0" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "mypy-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8e08de6138043108b3b18f09d3f817a4783912e48828ab397ecf183135d84d6"}, - {file = "mypy-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce4a17920ec144647d448fc43725b5873548b1aae6c603225626747ededf582d"}, - {file = "mypy-1.17.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ff25d151cc057fdddb1cb1881ef36e9c41fa2a5e78d8dd71bee6e4dcd2bc05b"}, - {file = "mypy-1.17.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93468cf29aa9a132bceb103bd8475f78cacde2b1b9a94fd978d50d4bdf616c9a"}, - {file = "mypy-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:98189382b310f16343151f65dd7e6867386d3e35f7878c45cfa11383d175d91f"}, - {file = "mypy-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:c004135a300ab06a045c1c0d8e3f10215e71d7b4f5bb9a42ab80236364429937"}, - {file = "mypy-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d4fe5c72fd262d9c2c91c1117d16aac555e05f5beb2bae6a755274c6eec42be"}, - {file = "mypy-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96b196e5c16f41b4f7736840e8455958e832871990c7ba26bf58175e357ed61"}, - {file = "mypy-1.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73a0ff2dd10337ceb521c080d4147755ee302dcde6e1a913babd59473904615f"}, - {file = "mypy-1.17.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cfcc1179c4447854e9e406d3af0f77736d631ec87d31c6281ecd5025df625d"}, - {file = "mypy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56f180ff6430e6373db7a1d569317675b0a451caf5fef6ce4ab365f5f2f6c3"}, - {file = "mypy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:eafaf8b9252734400f9b77df98b4eee3d2eecab16104680d51341c75702cad70"}, - {file = "mypy-1.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f986f1cab8dbec39ba6e0eaa42d4d3ac6686516a5d3dccd64be095db05ebc6bb"}, - {file = "mypy-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:51e455a54d199dd6e931cd7ea987d061c2afbaf0960f7f66deef47c90d1b304d"}, - {file = "mypy-1.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3204d773bab5ff4ebbd1f8efa11b498027cd57017c003ae970f310e5b96be8d8"}, - {file = "mypy-1.17.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1051df7ec0886fa246a530ae917c473491e9a0ba6938cfd0ec2abc1076495c3e"}, - {file = "mypy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f773c6d14dcc108a5b141b4456b0871df638eb411a89cd1c0c001fc4a9d08fc8"}, - {file = "mypy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1619a485fd0e9c959b943c7b519ed26b712de3002d7de43154a489a2d0fd817d"}, - {file = "mypy-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c41aa59211e49d717d92b3bb1238c06d387c9325d3122085113c79118bebb06"}, - {file = "mypy-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e69db1fb65b3114f98c753e3930a00514f5b68794ba80590eb02090d54a5d4a"}, - {file = "mypy-1.17.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03ba330b76710f83d6ac500053f7727270b6b8553b0423348ffb3af6f2f7b889"}, - {file = "mypy-1.17.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:037bc0f0b124ce46bfde955c647f3e395c6174476a968c0f22c95a8d2f589bba"}, - {file = "mypy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38876106cb6132259683632b287238858bd58de267d80defb6f418e9ee50658"}, - {file = "mypy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:d30ba01c0f151998f367506fab31c2ac4527e6a7b2690107c7a7f9e3cb419a9c"}, - {file = "mypy-1.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:63e751f1b5ab51d6f3d219fe3a2fe4523eaa387d854ad06906c63883fde5b1ab"}, - {file = "mypy-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f7fb09d05e0f1c329a36dcd30e27564a3555717cde87301fae4fb542402ddfad"}, - {file = "mypy-1.17.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b72c34ce05ac3a1361ae2ebb50757fb6e3624032d91488d93544e9f82db0ed6c"}, - {file = "mypy-1.17.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:434ad499ad8dde8b2f6391ddfa982f41cb07ccda8e3c67781b1bfd4e5f9450a8"}, - {file = "mypy-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f105f61a5eff52e137fd73bee32958b2add9d9f0a856f17314018646af838e97"}, - {file = "mypy-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:ba06254a5a22729853209550d80f94e28690d5530c661f9416a68ac097b13fc4"}, - {file = "mypy-1.17.0-py3-none-any.whl", hash = "sha256:15d9d0018237ab058e5de3d8fce61b6fa72cc59cc78fd91f1b474bce12abf496"}, - {file = "mypy-1.17.0.tar.gz", hash = "sha256:e5d7ccc08ba089c06e2f5629c660388ef1fee708444f1dee0b9203fa031dee03"}, -] - -[package.dependencies] -mypy_extensions = ">=1.0.0" -pathspec = ">=0.9.0" -typing_extensions = ">=4.6.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -faster-cache = ["orjson"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.8" -files = [ - {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, - {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, -] - -[[package]] -name = "nodeenv" -version = "1.9.1" -description = "Node.js virtual environment builder" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, - {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, -] - -[[package]] -name = "packaging" -version = "25.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, -] - -[[package]] -name = "pastel" -version = "0.2.1" -description = "Bring colors to your terminal." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, - {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "platformdirs" -version = "4.3.8" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.9" -files = [ - {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, - {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.14.1)"] - -[[package]] -name = "pluggy" -version = "1.6.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, - {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["coverage", "pytest", "pytest-benchmark"] - -[[package]] -name = "poethepoet" -version = "0.36.0" -description = "A task runner that works well with poetry and uv." -optional = false -python-versions = ">=3.9" -files = [ - {file = "poethepoet-0.36.0-py3-none-any.whl", hash = "sha256:693e3c1eae9f6731d3613c3c0c40f747d3c5c68a375beda42e590a63c5623308"}, - {file = "poethepoet-0.36.0.tar.gz", hash = "sha256:2217b49cb4e4c64af0b42ff8c4814b17f02e107d38bc461542517348ede25663"}, -] - -[package.dependencies] -pastel = ">=0.2.1,<0.3.0" -pyyaml = ">=6.0.2,<7.0" - -[package.extras] -poetry-plugin = ["poetry (>=1.2.0,<3.0.0)"] - -[[package]] -name = "pre-commit" -version = "4.2.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -optional = false -python-versions = ">=3.9" -files = [ - {file = "pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd"}, - {file = "pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - -[[package]] -name = "pygments" -version = "2.19.2" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, - {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, -] - -[package.extras] -windows-terminal = ["colorama (>=0.4.6)"] - -[[package]] -name = "pytest" -version = "8.4.1" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, - {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, -] - -[package.dependencies] -colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} -iniconfig = ">=1" -packaging = ">=20" -pluggy = ">=1.5,<2" -pygments = ">=2.7.2" - -[package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-cov" -version = "6.2.1" -description = "Pytest plugin for measuring coverage." -optional = false -python-versions = ">=3.9" -files = [ - {file = "pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5"}, - {file = "pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2"}, -] - -[package.dependencies] -coverage = {version = ">=7.5", extras = ["toml"]} -pluggy = ">=1.2" -pytest = ">=6.2.5" - -[package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] - -[[package]] -name = "pytest-xdist" -version = "3.8.0" -description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" -optional = false -python-versions = ">=3.9" -files = [ - {file = "pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88"}, - {file = "pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1"}, -] - -[package.dependencies] -execnet = ">=2.1" -pytest = ">=7.0.0" - -[package.extras] -psutil = ["psutil (>=3.0)"] -setproctitle = ["setproctitle"] -testing = ["filelock"] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pyyaml" -version = "6.0.2" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, - {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, - {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, - {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, - {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, -] - -[[package]] -name = "pyyaml-env-tag" -version = "1.1" -description = "A custom YAML tag for referencing environment variables in YAML files." -optional = false -python-versions = ">=3.9" -files = [ - {file = "pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04"}, - {file = "pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff"}, -] - -[package.dependencies] -pyyaml = "*" - -[[package]] -name = "ruff" -version = "0.12.4" -description = "An extremely fast Python linter and code formatter, written in Rust." -optional = false -python-versions = ">=3.7" -files = [ - {file = "ruff-0.12.4-py3-none-linux_armv6l.whl", hash = "sha256:cb0d261dac457ab939aeb247e804125a5d521b21adf27e721895b0d3f83a0d0a"}, - {file = "ruff-0.12.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:55c0f4ca9769408d9b9bac530c30d3e66490bd2beb2d3dae3e4128a1f05c7442"}, - {file = "ruff-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a8224cc3722c9ad9044da7f89c4c1ec452aef2cfe3904365025dd2f51daeae0e"}, - {file = "ruff-0.12.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9949d01d64fa3672449a51ddb5d7548b33e130240ad418884ee6efa7a229586"}, - {file = "ruff-0.12.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:be0593c69df9ad1465e8a2d10e3defd111fdb62dcd5be23ae2c06da77e8fcffb"}, - {file = "ruff-0.12.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7dea966bcb55d4ecc4cc3270bccb6f87a337326c9dcd3c07d5b97000dbff41c"}, - {file = "ruff-0.12.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afcfa3ab5ab5dd0e1c39bf286d829e042a15e966b3726eea79528e2e24d8371a"}, - {file = "ruff-0.12.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c057ce464b1413c926cdb203a0f858cd52f3e73dcb3270a3318d1630f6395bb3"}, - {file = "ruff-0.12.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64b90d1122dc2713330350626b10d60818930819623abbb56535c6466cce045"}, - {file = "ruff-0.12.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2abc48f3d9667fdc74022380b5c745873499ff827393a636f7a59da1515e7c57"}, - {file = "ruff-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2b2449dc0c138d877d629bea151bee8c0ae3b8e9c43f5fcaafcd0c0d0726b184"}, - {file = "ruff-0.12.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:56e45bb11f625db55f9b70477062e6a1a04d53628eda7784dce6e0f55fd549eb"}, - {file = "ruff-0.12.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:478fccdb82ca148a98a9ff43658944f7ab5ec41c3c49d77cd99d44da019371a1"}, - {file = "ruff-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0fc426bec2e4e5f4c4f182b9d2ce6a75c85ba9bcdbe5c6f2a74fcb8df437df4b"}, - {file = "ruff-0.12.4-py3-none-win32.whl", hash = "sha256:4de27977827893cdfb1211d42d84bc180fceb7b72471104671c59be37041cf93"}, - {file = "ruff-0.12.4-py3-none-win_amd64.whl", hash = "sha256:fe0b9e9eb23736b453143d72d2ceca5db323963330d5b7859d60d101147d461a"}, - {file = "ruff-0.12.4-py3-none-win_arm64.whl", hash = "sha256:0618ec4442a83ab545e5b71202a5c0ed7791e8471435b94e655b570a5031a98e"}, - {file = "ruff-0.12.4.tar.gz", hash = "sha256:13efa16df6c6eeb7d0f091abae50f58e9522f3843edb40d56ad52a5a4a4b6873"}, -] - -[[package]] -name = "setuptools" -version = "78.1.1" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.9" -files = [ - {file = "setuptools-78.1.1-py3-none-any.whl", hash = "sha256:c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561"}, - {file = "setuptools-78.1.1.tar.gz", hash = "sha256:fcc17fd9cd898242f6b4adfaca46137a9edef687f43e6f78469692a5e70d851d"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] -core = ["importlib_metadata (>=6)", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] - -[[package]] -name = "six" -version = "1.17.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, - {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, -] - -[[package]] -name = "typing-extensions" -version = "4.14.1" -description = "Backported and Experimental Type Hints for Python 3.9+" -optional = false -python-versions = ">=3.9" -files = [ - {file = "typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"}, - {file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"}, -] - -[[package]] -name = "virtualenv" -version = "20.31.2" -description = "Virtual Python Environment builder" -optional = false -python-versions = ">=3.8" -files = [ - {file = "virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11"}, - {file = "virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af"}, -] - -[package.dependencies] -distlib = ">=0.3.7,<1" -filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<5" - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] - -[[package]] -name = "watchdog" -version = "6.0.0" -description = "Filesystem events monitoring" -optional = false -python-versions = ">=3.9" -files = [ - {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, - {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, - {file = "watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3"}, - {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c"}, - {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2"}, - {file = "watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c"}, - {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948"}, - {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860"}, - {file = "watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0"}, - {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c"}, - {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134"}, - {file = "watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b"}, - {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8"}, - {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a"}, - {file = "watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c"}, - {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881"}, - {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11"}, - {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa"}, - {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2"}, - {file = "watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a"}, - {file = "watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680"}, - {file = "watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f"}, - {file = "watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282"}, -] - -[package.extras] -watchmedo = ["PyYAML (>=3.10)"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.11" -content-hash = "f58f04f170bd72f913d33992224112b9fc24a3bd06cd2dc2120e99d29e043955" +# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. + +[[package]] +name = "bump2version" +version = "1.0.1" +description = "Version-bump your software with a single command!" +optional = false +python-versions = ">=3.5" +groups = ["dev"] +files = [ + {file = "bump2version-1.0.1-py2.py3-none-any.whl", hash = "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410"}, + {file = "bump2version-1.0.1.tar.gz", hash = "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6"}, +] + +[[package]] +name = "cfgv" +version = "3.5.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0"}, + {file = "cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132"}, +] + +[[package]] +name = "click" +version = "8.3.3" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613"}, + {file = "click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.13.5" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "coverage-7.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0723d2c96324561b9aa76fb982406e11d93cdb388a7a7da2b16e04719cf7ca5"}, + {file = "coverage-7.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52f444e86475992506b32d4e5ca55c24fc88d73bcbda0e9745095b28ef4dc0cf"}, + {file = "coverage-7.13.5-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:704de6328e3d612a8f6c07000a878ff38181ec3263d5a11da1db294fa6a9bdf8"}, + {file = "coverage-7.13.5-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a1a6d79a14e1ec1832cabc833898636ad5f3754a678ef8bb4908515208bf84f4"}, + {file = "coverage-7.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79060214983769c7ba3f0cee10b54c97609dca4d478fa1aa32b914480fd5738d"}, + {file = "coverage-7.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:356e76b46783a98c2a2fe81ec79df4883a1e62895ea952968fb253c114e7f930"}, + {file = "coverage-7.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0cef0cdec915d11254a7f549c1170afecce708d30610c6abdded1f74e581666d"}, + {file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dc022073d063b25a402454e5712ef9e007113e3a676b96c5f29b2bda29352f40"}, + {file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9b74db26dfea4f4e50d48a4602207cd1e78be33182bc9cbf22da94f332f99878"}, + {file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ad146744ca4fd09b50c482650e3c1b1f4dfa1d4792e0a04a369c7f23336f0400"}, + {file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c555b48be1853fe3997c11c4bd521cdd9a9612352de01fa4508f16ec341e6fe0"}, + {file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7034b5c56a58ae5e85f23949d52c14aca2cfc6848a31764995b7de88f13a1ea0"}, + {file = "coverage-7.13.5-cp310-cp310-win32.whl", hash = "sha256:eb7fdf1ef130660e7415e0253a01a7d5a88c9c4d158bcf75cbbd922fd65a5b58"}, + {file = "coverage-7.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:3e1bb5f6c78feeb1be3475789b14a0f0a5b47d505bfc7267126ccbd50289999e"}, + {file = "coverage-7.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d"}, + {file = "coverage-7.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587"}, + {file = "coverage-7.13.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642"}, + {file = "coverage-7.13.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b"}, + {file = "coverage-7.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686"}, + {file = "coverage-7.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743"}, + {file = "coverage-7.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75"}, + {file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209"}, + {file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a"}, + {file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e"}, + {file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd"}, + {file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8"}, + {file = "coverage-7.13.5-cp311-cp311-win32.whl", hash = "sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf"}, + {file = "coverage-7.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9"}, + {file = "coverage-7.13.5-cp311-cp311-win_arm64.whl", hash = "sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028"}, + {file = "coverage-7.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01"}, + {file = "coverage-7.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422"}, + {file = "coverage-7.13.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f"}, + {file = "coverage-7.13.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5"}, + {file = "coverage-7.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376"}, + {file = "coverage-7.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256"}, + {file = "coverage-7.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c"}, + {file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5"}, + {file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09"}, + {file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9"}, + {file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf"}, + {file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c"}, + {file = "coverage-7.13.5-cp312-cp312-win32.whl", hash = "sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf"}, + {file = "coverage-7.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810"}, + {file = "coverage-7.13.5-cp312-cp312-win_arm64.whl", hash = "sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de"}, + {file = "coverage-7.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1"}, + {file = "coverage-7.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3"}, + {file = "coverage-7.13.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26"}, + {file = "coverage-7.13.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3"}, + {file = "coverage-7.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b"}, + {file = "coverage-7.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a"}, + {file = "coverage-7.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969"}, + {file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161"}, + {file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15"}, + {file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1"}, + {file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6"}, + {file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17"}, + {file = "coverage-7.13.5-cp313-cp313-win32.whl", hash = "sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85"}, + {file = "coverage-7.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b"}, + {file = "coverage-7.13.5-cp313-cp313-win_arm64.whl", hash = "sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664"}, + {file = "coverage-7.13.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d"}, + {file = "coverage-7.13.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0"}, + {file = "coverage-7.13.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806"}, + {file = "coverage-7.13.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3"}, + {file = "coverage-7.13.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9"}, + {file = "coverage-7.13.5-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd"}, + {file = "coverage-7.13.5-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606"}, + {file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e"}, + {file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0"}, + {file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87"}, + {file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479"}, + {file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2"}, + {file = "coverage-7.13.5-cp313-cp313t-win32.whl", hash = "sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a"}, + {file = "coverage-7.13.5-cp313-cp313t-win_amd64.whl", hash = "sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819"}, + {file = "coverage-7.13.5-cp313-cp313t-win_arm64.whl", hash = "sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911"}, + {file = "coverage-7.13.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f"}, + {file = "coverage-7.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e"}, + {file = "coverage-7.13.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a"}, + {file = "coverage-7.13.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510"}, + {file = "coverage-7.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247"}, + {file = "coverage-7.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6"}, + {file = "coverage-7.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0"}, + {file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882"}, + {file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740"}, + {file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16"}, + {file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0"}, + {file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0"}, + {file = "coverage-7.13.5-cp314-cp314-win32.whl", hash = "sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc"}, + {file = "coverage-7.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633"}, + {file = "coverage-7.13.5-cp314-cp314-win_arm64.whl", hash = "sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8"}, + {file = "coverage-7.13.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b"}, + {file = "coverage-7.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c"}, + {file = "coverage-7.13.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9"}, + {file = "coverage-7.13.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29"}, + {file = "coverage-7.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607"}, + {file = "coverage-7.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90"}, + {file = "coverage-7.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3"}, + {file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab"}, + {file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562"}, + {file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2"}, + {file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea"}, + {file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a"}, + {file = "coverage-7.13.5-cp314-cp314t-win32.whl", hash = "sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215"}, + {file = "coverage-7.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43"}, + {file = "coverage-7.13.5-cp314-cp314t-win_arm64.whl", hash = "sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45"}, + {file = "coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61"}, + {file = "coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179"}, +] + +[package.extras] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] + +[[package]] +name = "distlib" +version = "0.4.0" +description = "Distribution utilities" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"}, + {file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"}, +] + +[[package]] +name = "execnet" +version = "2.1.2" +description = "execnet: rapid multi-Python deployment" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"}, + {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"}, +] + +[package.extras] +testing = ["hatch", "pre-commit", "pytest", "tox"] + +[[package]] +name = "filelock" +version = "3.29.0" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258"}, + {file = "filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90"}, +] + +[[package]] +name = "ghp-import" +version = "2.1.0" +description = "Copy your docs directly to the gh-pages branch." +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["flake8", "markdown", "twine", "wheel"] + +[[package]] +name = "identify" +version = "2.6.19" +description = "File identification library for Python" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a"}, + {file = "identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "iniconfig" +version = "2.3.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, + {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "librt" +version = "0.10.0" +description = "Mypyc runtime library" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "librt-0.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7dc99f9642100b86e5f6bb14cdc9970009e31a9ef7d64df6704b7018451524a3"}, + {file = "librt-0.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8298cedfcfaff3790000bd057aaaa3df1b0ab54cf7b48eeab16184cbb1bc66b9"}, + {file = "librt-0.10.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee7dbe312dbf76468255b79a7ba311236fde620f2f7055fc09d421e31340314e"}, + {file = "librt-0.10.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:56ed90c48c19249012dadfd79a1bc13bd5168ea60a70722d330a3a600c0b1852"}, + {file = "librt-0.10.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d74ca0f4b2b09c117f913d4df01f6b934dff8a271096b35167d5264a31649f0"}, + {file = "librt-0.10.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8eb2daa9375f93c0e55ff5e44a4bbe98f39e5fe52e1abf9c97acb67743b61bf8"}, + {file = "librt-0.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7b09b90e634e6dff57978cd358070046071e2b120501f10787aeb35425f504f6"}, + {file = "librt-0.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2cf22fd379d60c739b800d4295ed34045f8b04aa8df9c12bd2f8f43f7fe672b7"}, + {file = "librt-0.10.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:74c798793fcf29a84d442278ebe0bb1fff79fe58ac4106eeff7019cbba861423"}, + {file = "librt-0.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dc4f1573401e8dbe6c26511fe027620b0fb30ae9a7ab814e02e510626b8b5f9c"}, + {file = "librt-0.10.0-cp310-cp310-win32.whl", hash = "sha256:e1428275f5fe3d4db6822e58d8b005a5b28ffca55e8433ebc051247fbe46429f"}, + {file = "librt-0.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:0708e9408f585b0f065081680583a577652099680ccf820c7538904322b679c3"}, + {file = "librt-0.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01b4500ca3a625450c032a9142a8e843923ce263fa8a92ad1b38927cabe2fe72"}, + {file = "librt-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b7e42d1b3e300d20bfc87e72ffd62f0a92a2cb3c35f7bf90df90c9d2a49f74c"}, + {file = "librt-0.10.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8ef7b8c61ce3a1b597cd3e15348ff1574325165c2e7ce09a718154cde2a7950"}, + {file = "librt-0.10.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:e73c84f72d1fa0d6eaa7a1930b436ba8d2c90c58d77bfabb09995a69ad35f6c0"}, + {file = "librt-0.10.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9728cb98713bd862fb8f4fd6a642d1896c86058a41d77c70f3d5cee75e725275"}, + {file = "librt-0.10.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:648b7e941d20acd72f9652115e0e53facd98156d61f9ebf7a812bdef8bdccea9"}, + {file = "librt-0.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c3e33747c068e86a9007c20fdb777eb5ba8d3d19136d7812f88e69a713041b6f"}, + {file = "librt-0.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d509c745bf7e77d1107cf05e6abb249dc03fad13eb39f2286a49deedaeb2bcd7"}, + {file = "librt-0.10.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:786ad5a15e99d0e0e74f3adbeecc198a5ac58f340be07e984723d1e0074838de"}, + {file = "librt-0.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:075582d877a97ee3d8e77bda3689dbe617b14f6469224a2d80b4b6c38e3951aa"}, + {file = "librt-0.10.0-cp311-cp311-win32.whl", hash = "sha256:75ecdc3f5a90065aa2af2e574706c5495adc392520762dcf10b1aa716f0b8090"}, + {file = "librt-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:b6f6084884131d8a52cb9d7095ff2aa52c1e786d9fdaefab1fb4515415e9e083"}, + {file = "librt-0.10.0-cp311-cp311-win_arm64.whl", hash = "sha256:0140bd62151160047e89b2730cb6f8506cdac5127baa1afb9231e4dd3fe7f681"}, + {file = "librt-0.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b4b58a44b407e91f633dafee008de9ddea6aa2a555ed94929c099260910bd0ba"}, + {file = "librt-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:950b79b11762531bdf45a9df909d2f9a2a8445c70c88665c01d14c8511a27dc5"}, + {file = "librt-0.10.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4538453f51be197633b425912c150e25b0667252d3741c53e8368176d98d9d37"}, + {file = "librt-0.10.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:70b955f091beac93e994a0b7ec616934f63b3ea5c3d6d7af847562f935aceca7"}, + {file = "librt-0.10.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:483e685e06b6163728ba6c85d74315176be7190f432ec2a41226e5e14355d5f0"}, + {file = "librt-0.10.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ac53d946a009d1a38c44a60812708c9458fb2a239a5f630d8e625571386650f"}, + {file = "librt-0.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc8771c9fcf0ea894ca41fdc2abd83572c2fbda221f232d86e718614e57ff513"}, + {file = "librt-0.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:70805dbc5257892ac572f86290a61e3c8d90224ecce1a8b2d1f7ed51965417f4"}, + {file = "librt-0.10.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d3b4f300f7bcba6e2ff73fb8bef1898479e9772bfa2682998c636391633ec826"}, + {file = "librt-0.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:943bc943f92f4fb3408fae62485c6a3ad68ce4f2ee205643a39641525c19a276"}, + {file = "librt-0.10.0-cp312-cp312-win32.whl", hash = "sha256:6065c1a758fba1010b41401013903d3d5d2750eab425ddedd584abac31d0630e"}, + {file = "librt-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:d788ecbe208ab352dab0e105cc06057bf9a2fc7e58cabb0d751ad9e30062b9e2"}, + {file = "librt-0.10.0-cp312-cp312-win_arm64.whl", hash = "sha256:6003d1f295bdba02656dc81308208fc060d0a51d8c0d0a6db70f7f3c57b9ba0a"}, + {file = "librt-0.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f0ede79d682e73f91c1b599a76d78b7464b9b5d213754cedb13372d9df36e596"}, + {file = "librt-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0ba0b131fdb336c8b9c948e397f4a7e649d0f783b529f07b647bf4961df392e"}, + {file = "librt-0.10.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2728117da2afb96fb957768725ee43dc9a2d73b031e02da424b818a3cdd3a275"}, + {file = "librt-0.10.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:723ba80594c49cdf0584196fc430752262605dc9449902fc9bd3d9b79976cb77"}, + {file = "librt-0.10.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7292edaaca294a61a978c53a3c7d6130d099b0dfbc8f0a65916cdc6b891b9852"}, + {file = "librt-0.10.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:89fe9d539f2c10a1666633eeeac507ce95dd06d9ecc58de3c6390dba156a3d3a"}, + {file = "librt-0.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4efa7b9587503fa5b67f40593302b9c8836d211d222ff9f7cafe67be5f8f0b10"}, + {file = "librt-0.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:22dc982ef59df0136df36092ccbdbb570ced8aafb33e49585739b2f1de1c13b6"}, + {file = "librt-0.10.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6f2e5f3606253a84cea719c94a3bb1c54487b5d617d0254d46e0920d8a06be3f"}, + {file = "librt-0.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:40884bfaa1e29f6b6a9be255007d8f359bfc9e61d68bdef8ed3158bfcbc95df9"}, + {file = "librt-0.10.0-cp313-cp313-win32.whl", hash = "sha256:3cd34cd8254eba756660bff6c2da91278248184301054fe3e4feb073bdd49b14"}, + {file = "librt-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:7baac5313e2d8dce1386f97777a8d03ab28f5fe1e780b3b9ac2ee7544551fedc"}, + {file = "librt-0.10.0-cp313-cp313-win_arm64.whl", hash = "sha256:afc5b4406c8e2515698d922a5c7823a009312835ea58196671fff40e35cb8166"}, + {file = "librt-0.10.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f09588a30e6a22ec624090d72a3ab1a6d4d5485c3ed739603e76aa3c16efa688"}, + {file = "librt-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:131ade118d12bd7a0adc4e655474a553f1b76cf78385868885944d21d51e45e0"}, + {file = "librt-0.10.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8b9ab28e40d011c373a189eae900c916e66d6fbecf7983e9e4883089ee085ef"}, + {file = "librt-0.10.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:67c39bb30da73bae1f293d1ed8bc2f8f6642649dd0928d3600aeff3041ac23d6"}, + {file = "librt-0.10.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8c3273c6b774614f093c8927c2bf1b077d0fefde988fe98f46a333734e5597ab"}, + {file = "librt-0.10.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9dd7c1b86a4baa583ab5db977484b93a2c474e69e96ef3e9538387ea54229cb9"}, + {file = "librt-0.10.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a77385c5a202e831149f7ad03be9e67cf80e957e52c614e83dcb822c95222eb8"}, + {file = "librt-0.10.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c6a5eafa74b5655bad59886138ed68426f098a6beb8cb95a71f2cc3cd8bb33fe"}, + {file = "librt-0.10.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:1fc93d0439204c50ab4d1512611ce2c206f1b369b419f69c7c27c761561e3291"}, + {file = "librt-0.10.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:79e713c178bc7a744adfbee6b4619a288eecc0c914da2a9313a20255abe2f0cf"}, + {file = "librt-0.10.0-cp314-cp314-win32.whl", hash = "sha256:2eba9d955a68c41d9f326be3da42f163ec3518b7ab20f1c826224e7bed71e0bf"}, + {file = "librt-0.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:cbfaf7f5145e9917f5d18bffa298eff6a19d74e7b8b11dabdca95785befe8dbf"}, + {file = "librt-0.10.0-cp314-cp314-win_arm64.whl", hash = "sha256:8d6d385d1969849a6b1397114df22714b6ded917bada98668e3e974dc663477e"}, + {file = "librt-0.10.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:6c3a82d3bd32631ef5c79922dfc028520c9ad840255979ab4d908271818039ee"}, + {file = "librt-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d64cc66005dc324c9bb1fa3fc2841f529002f6eb15966d55e46d430f56955a6a"}, + {file = "librt-0.10.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9bb562cd28c88cd2c6a9a6c78f99dc39348d6b16c94adc25de0e574acf1176e9"}, + {file = "librt-0.10.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:b809aa2854d019c28773b03605df22adc675ee4f3f4402d673581313e8906119"}, + {file = "librt-0.10.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc15acabdd519bd4176fdadc2119e5e3093485d86f89138daf47e5b4cedb983a"}, + {file = "librt-0.10.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b1b2d835307d08ddadd94568e2369648ec9173bd3eea6d7f52a1abe717c81f98"}, + {file = "librt-0.10.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d261c6a2f93335a5167887fb0223e8b98ffce20ee3fde242e8e58a37ece6d0e5"}, + {file = "librt-0.10.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e2ffd44963f8e7f68995504d90f9881d64e94dc1d8e310039b9526108fc0c0f7"}, + {file = "librt-0.10.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5f285f6455ed495791c4d8630e5af732960adea93cac4c893d15619f2eae53e8"}, + {file = "librt-0.10.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f6034ff52e663d34c7b82ef2aa2f94ad7c1d939e2368e63b06844bc4d127d2e1"}, + {file = "librt-0.10.0-cp314-cp314t-win32.whl", hash = "sha256:657860fd877fba6a241ea088ef99f63ca819945d3c715265da670bad56c37ebe"}, + {file = "librt-0.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:56ded2d66010203a0cb5af063b609e3f079531a0e5e576d618dece859fd2e1af"}, + {file = "librt-0.10.0-cp314-cp314t-win_arm64.whl", hash = "sha256:1ee63f30abf18ed4830fdbaf87b2b6f4bba1e198d46085c314edde4045e56715"}, + {file = "librt-0.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:83628c28545a5f4d860b48fae7f62367c006ab7405898573f34af8b7dcb178a2"}, + {file = "librt-0.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4bcf57b4de07e2d4bd093636ee59dc1b64298f304148dd9c4f001f7c7897650d"}, + {file = "librt-0.10.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2236c16bdb7c527eb671e4b599eec2c4229fddf80573de2bde529924f46db971"}, + {file = "librt-0.10.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:c1efa2f494811b245427095225a4d0251aee33ba4cf6ba2b7a6a9a619bc1a2ff"}, + {file = "librt-0.10.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d14626d350af79eed4b4f8886530052e3f78a62e9e53d2699f726f99c3d1d122"}, + {file = "librt-0.10.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b609f3461beae5608ca5219131ae5cdfea2e369818030abfc6ba7086830cde42"}, + {file = "librt-0.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0e2338b67c8e72755ccd1ab77b027e3701b375a1e12b4576fdefdf9c46448274"}, + {file = "librt-0.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:17cadff57139ff49beea0b17e50b28dfc3f9687126399696de4d2d8ae86ba7ff"}, + {file = "librt-0.10.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:5496102c8ed065c128d0f0fd10dcb3f9f3fd9b346954462d62af623f1b1ec7cd"}, + {file = "librt-0.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:537e1bfa459c1c92a263768a8a0c6fd0558049fa6c1b866d791eea711ae64114"}, + {file = "librt-0.10.0-cp39-cp39-win32.whl", hash = "sha256:85aca5a7ddc5f2d4cba24eba35667d83893ff2980dbd5884be16f538a24351e4"}, + {file = "librt-0.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:e45e46ff5fdfc690e77bb8557d5ba56974c4006b744ddbd70cce99fec6bfbeb8"}, + {file = "librt-0.10.0.tar.gz", hash = "sha256:1aba1e8aa4e3307a7be68a74149545fde7451964dc0235a8bec5704a17bdda42"}, +] + +[[package]] +name = "markdown" +version = "3.10.2" +description = "Python implementation of John Gruber's Markdown." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36"}, + {file = "markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950"}, +] + +[package.extras] +docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python] (>=0.28.3)"] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markupsafe" +version = "3.0.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, +] + +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +description = "Project documentation with Markdown." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, + {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} +ghp-import = ">=1.0" +jinja2 = ">=2.11.1" +markdown = ">=3.3.6" +markupsafe = ">=2.0.1" +mergedeep = ">=1.3.4" +mkdocs-get-deps = ">=0.2.0" +packaging = ">=20.5" +pathspec = ">=0.11.1" +pyyaml = ">=5.1" +pyyaml-env-tag = ">=0.1" +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.2" +description = "An extra command for MkDocs that infers required PyPI packages from `plugins` in mkdocs.yml" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "mkdocs_get_deps-0.2.2-py3-none-any.whl", hash = "sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650"}, + {file = "mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1"}, +] + +[package.dependencies] +mergedeep = ">=1.3.4" +platformdirs = ">=2.2.0" +pyyaml = ">=5.1" + +[[package]] +name = "mypy" +version = "1.20.2" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "mypy-1.20.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4"}, + {file = "mypy-1.20.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997"}, + {file = "mypy-1.20.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14"}, + {file = "mypy-1.20.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99"}, + {file = "mypy-1.20.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c"}, + {file = "mypy-1.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd"}, + {file = "mypy-1.20.2-cp310-cp310-win_arm64.whl", hash = "sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2"}, + {file = "mypy-1.20.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c"}, + {file = "mypy-1.20.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3"}, + {file = "mypy-1.20.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254"}, + {file = "mypy-1.20.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98"}, + {file = "mypy-1.20.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac"}, + {file = "mypy-1.20.2-cp311-cp311-win_amd64.whl", hash = "sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67"}, + {file = "mypy-1.20.2-cp311-cp311-win_arm64.whl", hash = "sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100"}, + {file = "mypy-1.20.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b"}, + {file = "mypy-1.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4"}, + {file = "mypy-1.20.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6"}, + {file = "mypy-1.20.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066"}, + {file = "mypy-1.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102"}, + {file = "mypy-1.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9"}, + {file = "mypy-1.20.2-cp312-cp312-win_arm64.whl", hash = "sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58"}, + {file = "mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026"}, + {file = "mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943"}, + {file = "mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517"}, + {file = "mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15"}, + {file = "mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee"}, + {file = "mypy-1.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f"}, + {file = "mypy-1.20.2-cp313-cp313-win_arm64.whl", hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330"}, + {file = "mypy-1.20.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30"}, + {file = "mypy-1.20.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924"}, + {file = "mypy-1.20.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb"}, + {file = "mypy-1.20.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc"}, + {file = "mypy-1.20.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558"}, + {file = "mypy-1.20.2-cp314-cp314-win_amd64.whl", hash = "sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8"}, + {file = "mypy-1.20.2-cp314-cp314-win_arm64.whl", hash = "sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3"}, + {file = "mypy-1.20.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609"}, + {file = "mypy-1.20.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2"}, + {file = "mypy-1.20.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c"}, + {file = "mypy-1.20.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744"}, + {file = "mypy-1.20.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6"}, + {file = "mypy-1.20.2-cp314-cp314t-win_amd64.whl", hash = "sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec"}, + {file = "mypy-1.20.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382"}, + {file = "mypy-1.20.2-py3-none-any.whl", hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563"}, + {file = "mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665"}, +] + +[package.dependencies] +librt = {version = ">=0.8.0", markers = "platform_python_implementation != \"PyPy\""} +mypy_extensions = ">=1.0.0" +pathspec = ">=1.0.0" +typing_extensions = [ + {version = ">=4.6.0", markers = "python_version < \"3.15\""}, + {version = ">=4.14.0", markers = "python_version >= \"3.15\""}, +] + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +native-parser = ["ast-serialize (>=0.1.1,<1.0.0)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + +[[package]] +name = "nodeenv" +version = "1.10.0" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +files = [ + {file = "nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827"}, + {file = "nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb"}, +] + +[[package]] +name = "packaging" +version = "26.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"}, + {file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"}, +] + +[[package]] +name = "pastel" +version = "0.2.1" +description = "Bring colors to your terminal." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] +files = [ + {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, + {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189"}, + {file = "pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a"}, +] + +[package.extras] +hyperscan = ["hyperscan (>=0.7)"] +optional = ["typing-extensions (>=4)"] +re2 = ["google-re2 (>=1.1)"] + +[[package]] +name = "platformdirs" +version = "4.9.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917"}, + {file = "platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a"}, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + +[[package]] +name = "poethepoet" +version = "0.45.0" +description = "A task runner that works well with poetry and uv." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "poethepoet-0.45.0-py3-none-any.whl", hash = "sha256:8e25f6e834ecf25fe2ddca676a4e0207eeb2e19def0a8709fc5c7f18e86cd68c"}, + {file = "poethepoet-0.45.0.tar.gz", hash = "sha256:cf2c2df4c185d33b619c2771de2b28c2b81c733f072226030c7fa4c273c040f8"}, +] + +[package.dependencies] +pastel = ">=0.2.1,<0.3.0" +pyyaml = ">=6.0.3,<7.0" + +[package.extras] +poetry-plugin = ["poetry (>=1.2.0,<3.0.0) ; python_version < \"4.0\""] + +[[package]] +name = "pre-commit" +version = "4.6.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b"}, + {file = "pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "pygments" +version = "2.20.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"}, + {file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pytest" +version = "9.0.3" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9"}, + {file = "pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1.0.1" +packaging = ">=22" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "7.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678"}, + {file = "pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2"}, +] + +[package.dependencies] +coverage = {version = ">=7.10.6", extras = ["toml"]} +pluggy = ">=1.2" +pytest = ">=7" + +[package.extras] +testing = ["process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "pytest-xdist" +version = "3.8.0" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88"}, + {file = "pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1"}, +] + +[package.dependencies] +execnet = ">=2.1" +pytest = ">=7.0.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["dev"] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-discovery" +version = "1.3.0" +description = "Python interpreter discovery" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "python_discovery-1.3.0-py3-none-any.whl", hash = "sha256:441d9ced3dfce36e113beb35ca302c71c7ef06f3c0f9c227a0b9bb3bd49b9e9f"}, + {file = "python_discovery-1.3.0.tar.gz", hash = "sha256:d098f1e86be5d45fe4d14bf1029294aabbd332f4321179dec85e76cddce834b0"}, +] + +[package.dependencies] +filelock = ">=3.15.4" +platformdirs = ">=4.3.6,<5" + +[package.extras] +docs = ["furo (>=2025.12.19)", "sphinx (>=9.1)", "sphinx-autodoc-typehints (>=3.6.3)", "sphinxcontrib-mermaid (>=2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.5.4)", "pytest (>=8.3.5)", "pytest-mock (>=3.14)", "setuptools (>=75.1)"] + +[[package]] +name = "pyyaml" +version = "6.0.3" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, +] + +[[package]] +name = "pyyaml-env-tag" +version = "1.1" +description = "A custom YAML tag for referencing environment variables in YAML files." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04"}, + {file = "pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff"}, +] + +[package.dependencies] +pyyaml = "*" + +[[package]] +name = "ruff" +version = "0.15.12" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ruff-0.15.12-py3-none-linux_armv6l.whl", hash = "sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c"}, + {file = "ruff-0.15.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c"}, + {file = "ruff-0.15.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5"}, + {file = "ruff-0.15.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002"}, + {file = "ruff-0.15.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5"}, + {file = "ruff-0.15.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6"}, + {file = "ruff-0.15.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33"}, + {file = "ruff-0.15.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847"}, + {file = "ruff-0.15.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0"}, + {file = "ruff-0.15.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339"}, + {file = "ruff-0.15.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5"}, + {file = "ruff-0.15.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd"}, + {file = "ruff-0.15.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b"}, + {file = "ruff-0.15.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e"}, + {file = "ruff-0.15.12-py3-none-win32.whl", hash = "sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20"}, + {file = "ruff-0.15.12-py3-none-win_amd64.whl", hash = "sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d"}, + {file = "ruff-0.15.12-py3-none-win_arm64.whl", hash = "sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f"}, + {file = "ruff-0.15.12.tar.gz", hash = "sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6"}, +] + +[[package]] +name = "setuptools" +version = "83.0.0" +description = "Most extensible Python build backend with support for C/C++ extension modules" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3"}, + {file = "setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.14)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.13.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +enabler = ["pytest-enabler (>=3.4)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.18.*)", "pytest-mypy (>=1.0.1) ; platform_python_implementation != \"PyPy\""] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["dev"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] + +[[package]] +name = "virtualenv" +version = "21.3.1" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "virtualenv-21.3.1-py3-none-any.whl", hash = "sha256:d1a71cf58f2f9228fff23a1f6ec15d39785c6b32e03658d104974247145edd35"}, + {file = "virtualenv-21.3.1.tar.gz", hash = "sha256:c2305bc1fddeec40699b8370d13f8d431b0701f00ce895061ce493aeded4426b"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = {version = ">=3.24.2,<4", markers = "python_version >= \"3.10\""} +platformdirs = ">=3.9.1,<5" +python-discovery = ">=1.2.2" + +[[package]] +name = "watchdog" +version = "6.0.0" +description = "Filesystem events monitoring" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c"}, + {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881"}, + {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11"}, + {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa"}, + {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2"}, + {file = "watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a"}, + {file = "watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680"}, + {file = "watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f"}, + {file = "watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282"}, +] + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + +[metadata] +lock-version = "2.1" +python-versions = ">=3.12" +content-hash = "0dadb3244ff0bd21e830b22407be7ee5a72b697363ed7ff102321e826971a64a" diff --git a/pyproject.toml b/pyproject.toml index 2a931a4e4..076e133f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,166 +1,190 @@ -[tool.poetry] -name = "zxbasic" -version = "1.18.2" -description = "Boriel's ZX BASIC Compiler" -authors = ["Jose Rodriguez "] -license = "AGPL-3.0-or-later" -documentation = "https://zxbasic.readthedocs.io" -repository = "https://github.com/boriel-basic/zxbasic" -homepage = "https://github.com/boriel-basic" -readme = "README.md" - -classifiers = [ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Topic :: Software Development :: Build Tools', - 'License :: OSI Approved :: GNU Affero General Public License v3', - 'Programming Language :: Python :: 3.11', - ] - -keywords = ['compiler', 'zxspectrum', 'BASIC', 'z80'] # arbitrary keywords - -packages = [ - { include = "src/**/*" } -] - -[tool.poetry.scripts] -zxbc = 'src.zxbc:main' -zxbasm = 'src.zxbasm:main' -zxbpp = 'src.zxbpp:entry_point' - -[tool.poetry.dependencies] -python = "^3.11" - -[tool.poetry.group.dev.dependencies] -pytest = "*" -bump2version = "^1.0.0" -pre-commit = "*" -mkdocs = "^1.2.2" -poethepoet = "*" -pytest-cov = "*" -ruff = "*" -mypy = "^1.8.0" -pytest-xdist = "*" -setuptools = ">=70.1.1,<79.0.0" - -[build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" - -[tool.poe.tasks] -[[tool.poe.tasks.lint]] -help = "Check code style and typing..." -shell = """ - ruff check . && - ruff format --check . && - mypy . - """ - -[[tool.poe.tasks.test]] -help = "Run tests" -shell = "pytest tests" - -[[tool.poe.tasks.test-no-cov]] -help = "Run tests faster (with no coverage)" -shell = "pytest tests --no-cov" - -[[tool.poe.tasks.format]] -help = "Formats code" -shell = """ - ruff check --select I --fix . - ruff format . - """ - -[tool.coverage.run] -branch = true -omit = [ - "tests/*", - "src/ply/*" -] - -[tool.pytest.ini_options] -minversion = "6.0" -norecursedirs = ["test_*tmp", "runtime"] -addopts = "--cov=src -n auto --dist=loadgroup --color=yes" - -[tool.mypy] -check_untyped_defs = true -disable_error_code = [ - "arg-type", - "assignment", - "attr-defined", - "call-overload", - "import-not-found", - "list-item", - "misc", - "name-defined", - "operator", - "union-attr", - "var-annotated", -] -exclude = [ - 'src/ply/.*\.py$' -] - -[[tool.mypy.overrides]] -module = [ - "src.ply.*", -] -follow_imports = "skip" - -[tool.ruff] -line-length = 120 -target-version = "py311" -exclude = [ - "src/ply" # PLY, external 3rd party tool -] - -[tool.ruff.lint] -select = [ - "C4", - "E", - "EXE", - "F", - "I", - "PLR", - "PLW", - "RET", - "RUF", - "UP", -] -ignore = [ - "E713", - "E722", - "E731", - "E741", - "EXE002", - "PLR0124", - "PLR0911", - "PLR0912", - "PLR0913", - "PLR0915", - "PLW1641", - "PLR1714", - "PLR1730", - "PLR2004", - "PLR5501", - "PLW0603", - "PLW2901", - "PLW0602", - "RET501", - "RET503", - "RET504", - "RET507", - "RUF005", - "RUF012", - "RUF013", - "RUF021", - "RUF022", # isort sorting style is not alphabetical and seems not natural either (despite the doc) - "UP008", - "UP015", # KEEP: redundant-open-modes - "UP024", - "UP028", - "UP030", - "UP031", - "UP038", -] +[project] +name = "zxbasic" +version = "1.18.7" +description = "Boriel's ZX BASIC Compiler" +authors = [ + { name = "Jose Rodriguez", email = "zxbasic@boriel.com" } +] +license = "AGPL-3.0-or-later" +license-files = [ + "LICENSE.txt" +] +readme = "README.md" +requires-python = ">=3.12" +keywords = ["compiler", "zxspectrum", "BASIC", "z80"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Topic :: Software Development :: Build Tools", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", +] + +[project.urls] +Homepage = "https://github.com/boriel-basic" +Repository = "https://github.com/boriel-basic/zxbasic" +Documentation = "https://zxbasic.readthedocs.io" + +[project.scripts] +zxbc = "src.zxbc:main" +zxbasm = "src.zxbasm:main" +zxbpp = "src.zxbpp:entry_point" + +# Poetry-specific packaging configuration that is not part of PEP 621. +# Keep this minimal section to preserve the current package discovery behavior. +[tool.poetry] +packages = [ + { include = "src/**/*" } +] + +[tool.poetry.group.dev.dependencies] +pytest = "*" +bump2version = "^1.0.0" +pre-commit = "*" +mkdocs = "^1.2.2" +poethepoet = "*" +pytest-cov = "*" +ruff = "*" +mypy = "^1.8.0" +pytest-xdist = "*" +setuptools = ">=83.0.0,<84.0.0" + +[build-system] +requires = ["poetry-core>=2.4.1"] +build-backend = "poetry.core.masonry.api" + +[tool.poe.tasks] +[[tool.poe.tasks.lint]] +help = "Check code style and typing..." +shell = """ + ruff check . && + ruff format --check . && + mypy . + """ + +[[tool.poe.tasks.test]] +help = "Run tests" +shell = "pytest tests" + +[[tool.poe.tasks.test-no-cov]] +help = "Run tests faster (with no coverage)" +shell = "pytest tests --no-cov" + +[[tool.poe.tasks.format]] +help = "Formats code" +shell = """ + ruff check --fix + ruff check --select I --fix . + ruff format . + """ + +[[tool.poe.tasks.clean]] +help = "Clean up the project working tree (i.e. remove __pycache__ folders)" +shell = """ + find . -name "__pycache__" | xargs rm -rf + find . -name "*.pyc" -delete + rm -rf .mypy_cache .pytest_cache .ruff_cache dist/ .coverage.* + """ + +[tool.coverage.run] +branch = true +omit = [ + "tests/*", + "src/**/*_standalone.py", +] + +[tool.pytest.ini_options] +minversion = "6.0" +norecursedirs = ["test_*tmp", "runtime"] +addopts = "--cov=src -n auto --dist=loadgroup --color=yes" + +[tool.mypy] +check_untyped_defs = true +disable_error_code = [ + "arg-type", + "assignment", + "attr-defined", + "call-overload", + "import-not-found", + "list-item", + "misc", + "name-defined", + "operator", + "union-attr", + "var-annotated", +] +exclude = [ + 'scratch/.*', + '.venv/.*', + 'venv/.*', + 'src/.*_standalone\.py$', +] + +[[tool.mypy.overrides]] +module = [ + "src.zxbasm.asmparse_standalone", + "src.zxbasm.asmparse_zxnext_standalone", + "src.zxbc.zxbparser_standalone", + "src.zxbpp.zxbpp_standalone", +] +follow_imports = "skip" + +[tool.ruff] +line-length = 120 +target-version = "py314" +exclude = [ + ".venv/", + "venv/", + "src/**/*standalone.py", +] + +[tool.ruff.lint] +select = [ + "C4", + "E", + "EXE", + "F", + "I", + "PLR", + "PLW", + "RET", + "RUF", + "UP", +] +ignore = [ + "E713", + "E722", + "E731", + "E741", + "EXE002", + "PLR0124", + "PLR0911", + "PLR0912", + "PLR0913", + "PLR0915", + "PLW1641", + "PLR1714", + "PLR1730", + "PLR2004", + "PLR5501", + "PLW0603", + "PLW2901", + "PLW0602", + "RET501", + "RET503", + "RET504", + "RET507", + "RUF005", + "RUF012", + "RUF013", + "RUF021", + "RUF022", # isort sorting style is not alphabetical and seems not natural either (despite the doc) + "UP008", + "UP015", # KEEP: redundant-open-modes + "UP024", + "UP028", + "UP030", + "UP031", +] diff --git a/src/__init__.py b/src/__init__.py index e69de29bb..906667a8d 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -0,0 +1,6 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- diff --git a/src/api/__init__.py b/src/api/__init__.py index e69de29bb..906667a8d 100644 --- a/src/api/__init__.py +++ b/src/api/__init__.py @@ -0,0 +1,6 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- diff --git a/src/api/check.py b/src/api/check.py index 228cddb04..b52092332 100644 --- a/src/api/check.py +++ b/src/api/check.py @@ -1,12 +1,10 @@ -# vim:ts=4:sw=4:et: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- - +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- +from typing import Any from src.api import config, errmsg, global_ from src.api.constants import CLASS, SCOPE @@ -85,13 +83,13 @@ def check_is_callable(lineno: int, id_: str) -> bool: def check_type_is_explicit(lineno: int, id_: str, type_): - assert isinstance(type_, symbols.TYPE) + assert isinstance(type_, symbols.TYPEREF) if type_.implicit: if config.OPTIONS.strict: errmsg.syntax_error_undeclared_type(lineno, id_) -def check_call_arguments(lineno: int, id_: str, args, filename: str): +def check_call_arguments(lineno: int, id_: str, args: symbols.ARGLIST, filename: str) -> bool: """Check arguments against function signature. Checks every argument in a function call against a function. @@ -131,8 +129,10 @@ def check_call_arguments(lineno: int, id_: str, args, filename: str): for param in entry.ref.params: if param.name in named_args: continue + if param.default_value is None: break + arg = symbols.ARGUMENT(param.default_value, lineno=lineno, byref=False, name=param.name) symbols.ARGLIST.make_node(args, arg) named_args[arg.name] = arg @@ -154,13 +154,13 @@ def check_call_arguments(lineno: int, id_: str, args, filename: str): if arg.class_ in (CLASS.var, CLASS.array) and param.class_ != arg.class_: errmsg.error(lineno, f"Invalid argument '{arg.value}'", fname=arg.filename) - return None + return False if not arg.typecast(param.type_): return False if param.byref: - if not isinstance(arg.value, (symbols.ID, symbols.ARRAYLOAD)): + if not isinstance(arg.value, symbols.ID | symbols.ARRAYLOAD): errmsg.error( lineno, "Expected a variable name, not an expression (parameter By Reference)", fname=arg.filename ) @@ -255,26 +255,31 @@ def check_and_make_label(lbl: str | int | float, lineno): # ---------------------------------------------------------------------- # Function for checking some arguments # ---------------------------------------------------------------------- -def is_null(*symbols_): +def is_null(*symbols_: Any) -> bool: """True if no nodes or all the given nodes are either None, NOP or empty blocks. For blocks this applies recursively """ for sym in symbols_: if sym is None: continue + if not isinstance(sym, symbols.SYMBOL): return False + if sym.token == "NOP": continue + if sym.token == "BLOCK": if not is_null(*sym.children): return False continue + return False + return True -def is_SYMBOL(token: str, *symbols_: symbols.SYMBOL): +def is_SYMBOL(token: str, *symbols_: symbols.SYMBOL) -> bool: """Returns True if ALL the given argument are AST nodes of the given token (e.g. 'BINARY') """ @@ -282,41 +287,66 @@ def is_SYMBOL(token: str, *symbols_: symbols.SYMBOL): return all(sym.token == token for sym in symbols_) -def is_LABEL(*p): +def is_LABEL(*p: symbols.SYMBOL) -> bool: return is_SYMBOL("LABEL", *p) -def is_string(*p): +def is_string(*p: symbols.SYMBOL) -> bool: """Returns True if ALL the arguments are AST nodes containing STRING or string CONSTANTS """ return all(is_SYMBOL("STRING", x) or is_const(x) and is_type(Type.string, x) for x in p) -def is_const(*p): +def is_const(*p: symbols.SYMBOL) -> bool: """A constant in the program, like CONST a = 5""" return is_SYMBOL("CONST", *p) -def is_CONST(*p): +def is_CONST(*p: symbols.SYMBOL) -> bool: """Not to be confused with the above. Check it's a CONSTant EXPRession """ return is_SYMBOL("CONSTEXPR", *p) -def is_static(*p): +def _is_static_unary(x: symbols.SYMBOL) -> bool: + if x.token != "UNARY": + return False + + if x.operator != "ADDRESS": + return False + + if x.operand.token == "LABEL": + return True + + if x.operand.token in ("ID", "VAR"): + return x.operand.scope == SCOPE.global_ + + if x.operand.token == "ARRAYACCESS": + return is_static(*(arg.value for arg in x.operand.args)) + + return False + + +def is_static(*p: symbols.SYMBOL) -> bool: """A static value (does not change at runtime) which is known at compile time """ - return all(is_CONST(x) or is_number(x) or is_const(x) for x in p) + assert all(isinstance(x, symbols.SYMBOL) for x in p) + return all(is_CONST(x) or is_number(x) or is_const(x) or _is_static_unary(x) for x in p) def is_number(*p): """Returns True if ALL the arguments are AST nodes containing NUMBER or numeric CONSTANTS """ - return all(isinstance(i, Symbol) and i.token in ("NUMBER", "CONST") and Type.is_numeric(i.type_) for i in p) + return all( + isinstance(i, Symbol) + and i.token in ("NUMBER", "CONST") + and Type.is_numeric(i.type_.type_ if isinstance(i.type_, symbols.TYPEREF) else i.type_) + for i in p + ) def is_static_str(*p): @@ -333,8 +363,8 @@ def is_var(*p): def is_unsigned(*p): """Returns false unless all types in p are unsigned""" try: - return all(i.type_.is_basic and Type.is_unsigned(i.type_) for i in p) - except Exception: + return all(i.type_.final.is_basic and Type.is_unsigned(i.type_.final) for i in p) + except (Exception,): pass return False @@ -344,7 +374,7 @@ def is_signed(*p): """Returns false unless all types in p are signed""" try: return all(i.type_.is_basic and Type.is_signed(i.type_) for i in p) - except Exception: + except (Exception,): pass return False @@ -353,8 +383,8 @@ def is_signed(*p): def is_numeric(*p): """Returns false unless all elements in p are of numerical type""" try: - return all(i.type_.is_basic and Type.is_numeric(i.type_) for i in p) - except Exception: + return all(i.type_.final.is_basic and Type.is_numeric(i.type_.final) for i in p) + except (Exception,): pass return False @@ -364,7 +394,7 @@ def is_type(type_, *p): """True if all args have the same type""" try: return all(i.type_ == type_ for i in p) - except Exception: + except (Exception,): pass return False @@ -376,7 +406,7 @@ def is_dynamic(*p): # TODO: Explain this better """ try: return not any(i.scope == SCOPE.global_ and i.is_basic and i.type_ != Type.string for i in p) - except Exception: + except (Exception,): pass return False @@ -387,11 +417,22 @@ def is_callable(*p): return all(x.token == "FUNCTION" for x in p) -def is_block_accessed(block): - """Returns True if a block is "accessed". A block of code is accessed if - it has a LABEL and it is used in a GOTO, GO SUB or @address access - :param block: A block of code (AST node) - :return: True / False depending if it has labels accessed or not +def is_block_accessed(block: Symbol): + """ + Checks if a code block or any of its nested children has been accessed. + + This function evaluates whether a specific block has been accessed. If the + block is not directly accessed, it recursively checks its child blocks. + + Args: + block: The code block to be evaluated. The block is assumed to have + attributes `accessed` (a boolean indicating if the block has + been accessed) and `children` (an iterable of nested child + blocks). + + Returns: + bool: True if the block or at least one of its nested child blocks has + been accessed; False otherwise. """ if is_LABEL(block) and block.accessed: return True @@ -404,17 +445,17 @@ def is_temporary_value(node) -> bool: return node.token not in ("STRING", "VAR") and node.t[0] not in ("_", "#") -def common_type(a: symbols.TYPE | Type | None, b: symbols.TYPE | Type | None) -> symbols.TYPE | Type | None: +def common_type(a: symbols.TYPING, b: symbols.TYPING) -> symbols.TYPE | None: """Returns a type which is common for both a and b types. Returns None if no common types allowed. """ - if a is None or b is None: - return None + assert isinstance(a, symbols.TYPING) + assert isinstance(b, symbols.TYPING) - if not isinstance(a, symbols.TYPE): + if isinstance(a, symbols.TYPEREF): a = a.type_ - if not isinstance(b, symbols.TYPE): + if isinstance(b, symbols.TYPEREF): b = b.type_ if a == b: # Both types are the same? @@ -452,7 +493,7 @@ def common_type(a: symbols.TYPE | Type | None, b: symbols.TYPE | Type | None) -> return result -def is_ender(node) -> bool: +def is_ender(node: symbols.SYMBOL) -> bool: """Returns whether this node ends a block, that is, the following instruction won't be executed after this one """ @@ -471,7 +512,7 @@ def is_ender(node) -> bool: } -def check_class(node, class_: CLASS, lineno: int) -> bool: +def check_class(node: symbols.ID, class_: CLASS, lineno: int) -> bool: """Returns whether the given node has CLASS.unknown or the given class_. It False, it will emit a syntax error """ diff --git a/src/api/config.py b/src/api/config.py index 66361b27d..7321fed46 100644 --- a/src/api/config.py +++ b/src/api/config.py @@ -1,11 +1,9 @@ -# vim:ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import configparser import enum @@ -117,7 +115,7 @@ def load_config_from_file( try: cfg = configparser.ConfigParser() cfg.read(filename, encoding="utf-8") - except (configparser.DuplicateSectionError, configparser.DuplicateOptionError): + except configparser.DuplicateSectionError, configparser.DuplicateOptionError: errmsg.msg_output(f"Invalid config file '{filename}': it has duplicated fields") if stop_on_error: sys.exit(1) @@ -159,7 +157,7 @@ def save_config_into_file( if os.path.exists(filename): try: cfg.read(filename, encoding="utf-8") - except (configparser.DuplicateSectionError, configparser.DuplicateOptionError): + except configparser.DuplicateSectionError, configparser.DuplicateOptionError: errmsg.msg_output(f"Invalid config file '{filename}': it has duplicated fields") if stop_on_error: sys.exit(1) diff --git a/src/api/constants.py b/src/api/constants.py index f5907072a..d02632c0b 100644 --- a/src/api/constants.py +++ b/src/api/constants.py @@ -1,27 +1,24 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- +from __future__ import annotations import enum import os from enum import StrEnum -from typing import Final, Optional, Union +from typing import Final -from .decorator import classproperty +from .decorator import cached_function, classproperty # ------------------------------------------------- # Global constants # ------------------------------------------------- -# Path to main ZX Basic compiler executable -ZXBASIC_ROOT = os.path.abspath( - os.path.join(os.path.abspath(os.path.dirname(os.path.abspath(__file__))), os.path.pardir) -) +# Path to main Boriel Basic compiler executable +ZXBASIC_ROOT = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.path.pardir)) # ---------------------------------------------------------------------- @@ -43,18 +40,18 @@ class CLASS(StrEnum): type = "type" # 7 # type @classproperty - def classes(cls): + def classes(cls) -> tuple[CLASS, ...]: return cls.unknown, cls.var, cls.array, cls.function, cls.sub, cls.const, cls.label @classmethod - def is_valid(cls, class_: Union[str, "CLASS"]): + def is_valid(cls, class_: str | CLASS) -> bool: """Whether the given class is valid or not. """ return class_ in set(CLASS) @classmethod - def to_string(cls, class_: "CLASS"): + def to_string(cls, class_: CLASS): assert cls.is_valid(class_) return class_.value @@ -71,7 +68,7 @@ class ARRAY: class TYPE(enum.IntEnum): """Enums primary type constants""" - unknown = 0 + unknown = 0 # Denotes a type that is not yet known byte = 1 ubyte = 2 integer = 3 @@ -82,9 +79,10 @@ class TYPE(enum.IntEnum): float = 8 string = 9 boolean = 10 + nil = 11 @classmethod - def type_size(cls, type_: "TYPE") -> int: + def type_size(cls, type_: TYPE) -> int: type_sizes = { cls.boolean: 1, cls.byte: 1, @@ -97,54 +95,55 @@ def type_size(cls, type_: "TYPE") -> int: cls.float: 5, cls.string: 2, cls.unknown: 0, + cls.nil: 0, } return type_sizes[type_] @classproperty - def types(cls) -> set["TYPE"]: + def types(cls) -> set[TYPE]: return set(TYPE) @classmethod - def size(cls, type_: "TYPE") -> int: + def size(cls, type_: TYPE) -> int: return cls.type_size(type_) @classproperty - def integral(cls) -> set["TYPE"]: + def integral(cls) -> set[TYPE]: return {cls.boolean, cls.byte, cls.ubyte, cls.integer, cls.uinteger, cls.long, cls.ulong} @classproperty - def signed(cls) -> set["TYPE"]: + def signed(cls) -> set[TYPE]: return {cls.byte, cls.integer, cls.long, cls.fixed, cls.float} @classproperty - def unsigned(cls) -> set["TYPE"]: + def unsigned(cls) -> set[TYPE]: return {cls.boolean, cls.ubyte, cls.uinteger, cls.ulong} @classproperty - def decimals(cls) -> set["TYPE"]: + def decimals(cls) -> set[TYPE]: return {cls.fixed, cls.float} @classproperty - def numbers(cls) -> set["TYPE"]: + def numbers(cls) -> set[TYPE]: return cls.integral | cls.decimals @classmethod - def is_valid(cls, type_: "TYPE") -> bool: + def is_valid(cls, type_: TYPE) -> bool: """Whether the given type is valid or not. """ return type_ in cls.types @classmethod - def is_signed(cls, type_: "TYPE") -> bool: + def is_signed(cls, type_: TYPE) -> bool: return type_ in cls.signed @classmethod - def is_unsigned(cls, type_: "TYPE") -> bool: + def is_unsigned(cls, type_: TYPE) -> bool: return type_ in cls.unsigned @classmethod - def to_signed(cls, type_: "TYPE") -> "TYPE": + def to_signed(cls, type_: TYPE) -> TYPE: """Return signed type or equivalent""" if type_ in cls.unsigned: return { @@ -160,12 +159,13 @@ def to_signed(cls, type_: "TYPE") -> "TYPE": return cls.unknown @staticmethod - def to_string(type_: "TYPE") -> str: + def to_string(type_: TYPE) -> str: """Return ID representation (string) of a type""" return type_.name + @cached_function @staticmethod - def to_type(typename: str) -> Optional["TYPE"]: + def to_type(typename: str) -> TYPE | None: """Converts a type ID to name. On error returns None""" for t in TYPE: if t.name == typename: @@ -175,7 +175,7 @@ def to_type(typename: str) -> Optional["TYPE"]: @enum.unique -class SCOPE(str, enum.Enum): +class SCOPE(StrEnum): """Enum scopes""" global_ = "global" @@ -183,33 +183,33 @@ class SCOPE(str, enum.Enum): parameter = "parameter" @staticmethod - def is_valid(scope: Union[str, "SCOPE"]) -> bool: + def is_valid(scope: str | SCOPE) -> bool: return scope in set(SCOPE) @staticmethod - def to_string(scope: "SCOPE") -> str: + def to_string(scope: SCOPE) -> str: assert SCOPE.is_valid(scope) return scope.value @enum.unique -class CONVENTION(str, enum.Enum): +class CONVENTION(StrEnum): unknown = "unknown" fastcall = "__fastcall__" stdcall = "__stdcall__" @staticmethod - def is_valid(convention: Union[str, "CONVENTION"]): + def is_valid(convention: str | CONVENTION): return convention in set(CONVENTION) @staticmethod - def to_string(convention: "CONVENTION"): + def to_string(convention: CONVENTION): assert CONVENTION.is_valid(convention) return convention.value @enum.unique -class LoopType(str, enum.Enum): +class LoopType(StrEnum): DO = "DO" FOR = "FOR" WHILE = "WHILE" diff --git a/src/api/dataref.py b/src/api/dataref.py index 9dbd79ce1..942b3e755 100644 --- a/src/api/dataref.py +++ b/src/api/dataref.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from dataclasses import dataclass from typing import Any diff --git a/src/api/debug.py b/src/api/debug.py index b305ab1fa..434158cc5 100644 --- a/src/api/debug.py +++ b/src/api/debug.py @@ -1,6 +1,9 @@ -# vim:ts=4:sw=4:et: - -# Simple debugging module +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import inspect import os diff --git a/src/api/decorator.py b/src/api/decorator.py index a9bc6bb83..e2573df65 100644 --- a/src/api/decorator.py +++ b/src/api/decorator.py @@ -1,4 +1,12 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from collections.abc import Callable +from functools import wraps class ClassProperty: @@ -17,3 +25,14 @@ def __get__(self, owner_self, owner_cls: type): def classproperty(fget: Callable[[type], Callable]) -> ClassProperty: """Use this function as the decorator in lowercase to follow Python conventions.""" return ClassProperty(fget) + + +def cached_function(f: Callable) -> Callable: + """Decorator to cache the return value of a function.""" + cache = {} + + @wraps(f) + def wrapper(*args, **kwargs): + return cache.setdefault(args, f(*args, **kwargs)) + + return wrapper diff --git a/src/api/errmsg.py b/src/api/errmsg.py index 13241294c..97ccbca98 100644 --- a/src/api/errmsg.py +++ b/src/api/errmsg.py @@ -1,11 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import sys from collections.abc import Callable @@ -21,6 +19,7 @@ "register_warning", "warning", "warning_not_used", + "warning_uninitalized_string_var", ) @@ -44,6 +43,9 @@ def info(msg: str) -> None: def error(lineno: int, msg: str, fname: str | None = None) -> None: """Generic syntax error routine""" + if getattr(global_, "syntax_error_occurred", False) and not getattr(global_, "reporting_syntax_error", False): + return + if fname is None: fname = global_.FILENAME @@ -61,6 +63,8 @@ def error(lineno: int, msg: str, fname: str | None = None) -> None: def warning(lineno: int, msg: str, fname: str | None = None) -> None: """Generic warning error routine""" + if getattr(global_, "syntax_error_occurred", False): + return global_.has_warnings += 1 if global_.has_warnings <= config.OPTIONS.expected_warnings: return @@ -129,6 +133,12 @@ def warning_implicit_type(lineno: int, id_: str, type_: str = None): warning(lineno, "Using default implicit type '%s' for '%s'" % (type_, id_)) +@register_warning("101") +def warning_uninitalized_string_var(lineno: int, id_: str): + """Warning: Accessing uninitialized string variable""" + warning(lineno, f"Accessing uninitialized string variable '{id_}'") + + @register_warning("110") def warning_condition_is_always(lineno: int, cond: bool = False): """Warning: Condition is always false/true""" diff --git a/src/api/exception.py b/src/api/exception.py index 97544bb40..ca353394a 100644 --- a/src/api/exception.py +++ b/src/api/exception.py @@ -1,14 +1,11 @@ -# vim:ts=4:et:sw=4: +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- - - -# ------------------------- ERROR exception classes --------------------------- +from __future__ import annotations __all__ = [ "Error", @@ -62,3 +59,10 @@ class TempAlreadyFreedError(InternalError): def __init__(self, label): super().__init__(f"Label '{label}' already freed") self.label = label + + +class InvalidTypeError(InternalError): + """Raised when a type is invalid.""" + + def __init__(self, msg: str): + super().__init__(msg) diff --git a/src/api/fp.py b/src/api/fp.py index 478271000..5e0475e71 100644 --- a/src/api/fp.py +++ b/src/api/fp.py @@ -1,4 +1,9 @@ -# Floating point converter +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- def fp(x: float) -> tuple[str, str]: diff --git a/src/api/global_.py b/src/api/global_.py index 01856fa5a..c432a4ed8 100644 --- a/src/api/global_.py +++ b/src/api/global_.py @@ -1,11 +1,10 @@ -# vim:ts=4:et:sw=4: +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- from __future__ import annotations from typing import TYPE_CHECKING, Final, NamedTuple @@ -60,6 +59,11 @@ class LoopInfo(NamedTuple): # ---------------------------------------------------------------------- has_errors = 0 # Number of errors has_warnings = 0 # Number of warnings +syntax_error_occurred = False # True if a syntax error occurred during parse +reporting_syntax_error = False # True if we are actively reporting a syntax error +tokens_yielded = 0 +tokens_rejected = 0 +shifted_at_last_error = 0 # ---------------------------------------------------------------------- # Default var type when not specified (implicit) an can't be guessed diff --git a/src/api/lex.py b/src/api/lex.py new file mode 100644 index 000000000..15b208e4f --- /dev/null +++ b/src/api/lex.py @@ -0,0 +1,245 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + +import re +import sys +from collections.abc import Callable +from typing import Any + + +class LexError(Exception): + pass + + +class LexToken: + def __init__(self) -> None: + self.type: str | None = None + self.value: Any = None + self.lineno: int = 1 + self.lexpos: int = 0 + self.lexer: Lexer | None = None + + def __repr__(self) -> str: + return f"LexToken({self.type},{self.value!r},{self.lineno},{self.lexpos})" + + +class Lexer: + def __init__(self, obj: Any) -> None: + self._object = obj + self.lexdata: str = "" + self.lexpos: int = 0 + self.lineno: int = 1 + self.statestack: list[str] = ["INITIAL"] + self.next_token: LexToken | None = None + + # Parse states + states = get_attr(obj, "states", ()) + state_names = {"INITIAL"} + for s in states: + state_names.add(s[0]) + + # Parse tokens + self.tokens = get_attr(obj, "tokens", ()) + + # Collect rules + functions: list[tuple[str, list[str], str, str, Callable[[LexToken], Any]]] = [] + strings: list[tuple[str, list[str], str, str]] = [] + self.error_handlers: dict[str, Callable[[LexToken], Any]] = {} + + # dir(obj) preserves definition order in modern Python, + # but we want to be safe and sort functions by co_firstlineno + for name in get_dir(obj): + if name.startswith("t_"): + val = get_attr(obj, name) + if name.endswith("_error"): + parts = name[2:-6].split("_") + target_states = [p for p in parts if p in state_names] + if not target_states: + target_states = ["INITIAL"] + for s in target_states: + self.error_handlers[s] = val + else: + parts = name[2:].split("_") + target_states = [] + i = 0 + # Consume state names from the left, leaving at least one part for the rule name + while i < len(parts) - 1 and parts[i] in state_names: + target_states.append(parts[i]) + i += 1 + + if not target_states: + target_states = ["INITIAL"] + rule_name = "_".join(parts) + else: + rule_name = "_".join(parts[i:]) + + if callable(val): + pattern = val.__doc__ + if pattern: + functions.append((name, target_states, rule_name, pattern, val)) + elif isinstance(val, str): + strings.append((name, target_states, rule_name, val)) + + # Sort functions by co_firstlineno to match definition order in file + def get_line_no(item: tuple[str, list[str], str, str, Callable[[LexToken], Any]]) -> int: + func = item[4] + try: + if hasattr(func, "__code__"): + return func.__code__.co_firstlineno + if hasattr(func, "__func__") and hasattr(func.__func__, "__code__"): + return func.__func__.__code__.co_firstlineno + except Exception: + pass + return 0 + + functions.sort(key=get_line_no) + + # Sort strings by pattern length descending + strings.sort(key=lambda x: len(x[3]), reverse=True) + + # Build rules per state + state_rules: dict[str, list[tuple[str, re.Pattern[str], Callable[[LexToken], Any] | None]]] = { + s: [] for s in state_names + } + + # Add functions first + for name, target_states, rule_name, pattern, func in functions: + try: + rx = re.compile(pattern) + for s in target_states: + state_rules[s].append((rule_name, rx, func)) + except Exception as e: + print(f"Error compiling pattern {pattern!r} for rule {name}: {e}", file=sys.stderr) + raise e + + # Add strings second + for name, target_states, rule_name, pattern in strings: + try: + rx = re.compile(pattern) + for s in target_states: + state_rules[s].append((rule_name, rx, None)) + except Exception as e: + print(f"Error compiling pattern {pattern!r} for rule {name}: {e}", file=sys.stderr) + raise e + + self.compiled_rules = state_rules + + def input(self, data: str) -> None: + self.lexdata = data + self.lexpos = 0 + self.lineno = 1 + self.statestack = ["INITIAL"] + self.next_token = None + + def begin(self, state: str) -> None: + if state not in self.compiled_rules: + raise LexError(f"Undefined state: {state}") + self.statestack[-1] = state + + def push_state(self, state: str) -> None: + if state not in self.compiled_rules: + raise LexError(f"Undefined state: {state}") + self.statestack.append(state) + + def pop_state(self) -> None: + if len(self.statestack) > 1: + self.statestack.pop() + else: + self.statestack = ["INITIAL"] + + def skip(self, n: int) -> None: + self.lexpos += n + + def clone(self) -> Lexer: + c = Lexer(self._object) + c.lexdata = self.lexdata + c.lexpos = self.lexpos + c.lineno = self.lineno + c.statestack = list(self.statestack) + c.next_token = self.next_token + return c + + def token(self) -> LexToken | None: + if self.next_token is not None: + t = self.next_token + self.next_token = None + return t + + while self.lexpos < len(self.lexdata): + state = self.statestack[-1] + rules = self.compiled_rules.get(state, []) + + matched = False + for rule_name, rx, val in rules: + m = rx.match(self.lexdata, self.lexpos) + if m: + matched = True + matched_text = m.group(0) + + t = LexToken() + t.type = rule_name + t.value = matched_text + t.lineno = self.lineno + t.lexpos = self.lexpos + t.lexer = self + + # Advance position BEFORE calling function + self.lexpos += len(matched_text) + + if val: + res = val(t) + if res is None: + # Ignored token + break + return res + # Convert to token type (which is rule_name) + return t + + if matched: + continue + + # No rule matched + err_handler = self.error_handlers.get(state) + if err_handler: + t = LexToken() + t.type = "error" + t.value = self.lexdata[self.lexpos :] + t.lineno = self.lineno + t.lexpos = self.lexpos + t.lexer = self + + old_pos = self.lexpos + res = err_handler(t) + if self.lexpos == old_pos: + self.lexpos += 1 + if res is not None: + return res + else: + raise LexError( + f"Lexical error: Illegal character {self.lexdata[self.lexpos]!r} at position {self.lexpos}" + ) + + return None + + +def get_attr(obj: Any, name: str, default: Any = None) -> Any: + if isinstance(obj, dict): + return obj.get(name, default) + return getattr(obj, name, default) + + +def get_dir(obj: Any) -> list[str]: + if isinstance(obj, dict): + return list(obj.keys()) + return dir(obj) + + +def lex(object: Any = None) -> Lexer: + if object is None: + frame = sys._getframe(1) + object = frame.f_globals + return Lexer(object) diff --git a/src/api/opcodestemps.py b/src/api/opcodestemps.py index a17aa27b3..89417c516 100644 --- a/src/api/opcodestemps.py +++ b/src/api/opcodestemps.py @@ -1,9 +1,9 @@ -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- __all__ = "OpcodesTemps", "init" diff --git a/src/api/optimize.py b/src/api/optimize.py index 1f7fdeca4..41284cb6c 100644 --- a/src/api/optimize.py +++ b/src/api/optimize.py @@ -1,5 +1,12 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import symtable -from collections.abc import Generator +from collections.abc import Callable, Generator from typing import Any, NamedTuple import src.api.check as chk @@ -10,28 +17,17 @@ from src.api import errmsg from src.api.config import OPTIONS from src.api.constants import CLASS, CONVENTION, SCOPE, TYPE -from src.api.debug import __DEBUG__ from src.api.errmsg import warning_not_used -from src.ast import Ast, NodeVisitor +from src.ast_ import Ast, NodeVisitor from src.symbols import sym as symbols from src.symbols.id_ import ref -class ToVisit(NamedTuple): - """Used just to signal an object to be - traversed. - """ - - obj: symbols.SYMBOL - - class GenericVisitor(NodeVisitor): """A slightly different visitor, that just traverses an AST, but does not return a translation of it. Used to examine the AST or do transformations """ - node_type = ToVisit - @property def O_LEVEL(self): return OPTIONS.optimization_level @@ -51,36 +47,42 @@ def TYPE(type_): assert TYPE.is_valid(type_) return gl.SYMBOL_TABLE.basic_types[type_] - def visit(self, node): - return super().visit(ToVisit(node)) - - def _visit(self, node: ToVisit): - if node.obj is None: - return None - - __DEBUG__(f"Optimizer: Visiting node {node.obj!s}[{node.obj.token}]", 1) - meth = getattr(self, f"visit_{node.obj.token}", self.generic_visit) - return meth(node.obj) - - def generic_visit(self, node: Ast) -> Generator[Ast | None, Any, None]: - for i, child in enumerate(node.children): - node.children[i] = yield self.visit(child) - - yield node - class UniqueVisitor(GenericVisitor): def __init__(self): super().__init__() self.visited = set() - def _visit(self, node: ToVisit): - if node.obj in self.visited: - return node.obj + def _visit(self, node: Ast): + if node in self.visited: + return node - self.visited.add(node.obj) + self.visited.add(node) return super()._visit(node) + def filter_inorder( + self, + node, + filter_func: Callable[[Any], bool], + child_selector: Callable[[Ast], bool] = lambda x: True, + ) -> Generator[Ast]: + """Visit the tree inorder, but only those that return true for filter_func and visiting children which + return true for child_selector. + """ + visited = set() + stack = [node] + while stack: + node = stack.pop() + if node in visited: + continue + + visited.add(node) + if filter_func(node): + yield self.visit(node) + + if isinstance(node, Ast) and child_selector(node): + stack.extend(node.children[::-1]) + class UnreachableCodeVisitor(UniqueVisitor): """Visitor to optimize unreachable code (and prune it).""" @@ -97,10 +99,10 @@ def visit_FUNCTION(self, node: symbols.ID): lineno = node.lineno if not node.body else node.body[-1].lineno errmsg.warning_function_should_return_a_value(lineno, node.name, node.filename) type_ = node.type_ - if type_ is not None and type_ == self.TYPE(TYPE.string): + if type_ is not None and type_ == self.TYPE(TYPE.string) and node.convention != CONVENTION.fastcall: node.body.append(symbols.ASM("\nld hl, 0\n", lineno, node.filename, is_sentinel=True)) - yield (yield self.generic_visit(node)) + yield self.generic_visit(node) def visit_BLOCK(self, node): # Remove CHKBREAK after labels @@ -148,7 +150,7 @@ def visit_BLOCK(self, node): yield self.NOP return - yield (yield self.generic_visit(node)) + yield self.generic_visit(node) class FunctionGraphVisitor(UniqueVisitor): @@ -158,6 +160,7 @@ def _get_calls_from_children(self, node: symtable.Symbol): return list(self.filter_inorder(node, lambda x: x.token in ("CALL", "FUNCCALL"))) def _set_children_as_accessed(self, node: symbols.SYMBOL): + """ "Traverse only those""" parent = node.get_parent(symbols.FUNCDECL) if parent is None: # Global scope? for symbol in self._get_calls_from_children(node): @@ -307,23 +310,27 @@ def visit_FUNCDECL(self, node): if self.O_LEVEL > 1 and node.params_size == node.locals_size == 0: node.entry.ref.convention = CONVENTION.fastcall - node.children[1] = yield ToVisit(node.entry) + node.children[1] = yield self.visit(node.entry) yield node def visit_LET(self, node): lvalue = node.children[0] if self.O_LEVEL > 1 and not lvalue.accessed: warning_not_used(lvalue.lineno, lvalue.name, fname=lvalue.filename) - block = symbols.BLOCK( - *[ - symbols.CALL(x.entry, x.args, x.lineno, lvalue.filename) - for x in self.filter_inorder( - node.children[1], - lambda x: x.token == "FUNCCALL", - lambda x: x.token != "FUNCTION", - ) - ] - ) + nodes = [ + symbols.CALL(x.entry, x.args, x.lineno, lvalue.filename) if x.token == "FUNCCALL" else x + for x in self.filter_inorder( + node.children[1], + lambda x: x.token in ("FUNCCALL", "BUILTIN"), + lambda x: x.token != "FUNCTION", + ) + if x.token == "FUNCCALL" or getattr(x, "fname") in {"IN", "RND", "USR"} + ] + for node_ in nodes: + if node_.token == "BUILTIN": + node_.discard_result = True + + block = symbols.BLOCK(*nodes) yield block else: yield (yield self.generic_visit(node)) @@ -359,19 +366,20 @@ def visit_RETURN(self, node): might cause infinite recursion. """ if len(node.children) == 2: - node.children[1] = yield ToVisit(node.children[1]) + node.children[1] = yield self.visit(node.children[1]) + yield node def visit_UNARY(self, node): if node.operator == "ADDRESS": - yield (yield self.visit_ADDRESS(node)) + yield self.visit_ADDRESS(node) else: - yield (yield self.generic_visit(node)) + yield self.generic_visit(node) def visit_IF(self, node): - expr_ = yield ToVisit(node.children[0]) - then_ = yield ToVisit(node.children[1]) - else_ = (yield ToVisit(node.children[2])) if len(node.children) == 3 else self.NOP + expr_ = yield self.visit(node.children[0]) + then_ = yield self.visit(node.children[1]) + else_ = (yield self.visit(node.children[2])) if len(node.children) == 3 else self.NOP if self.O_LEVEL >= 1: if chk.is_null(then_, else_): @@ -394,6 +402,7 @@ def visit_IF(self, node): for i in range(len(node.children)): node.children[i] = (expr_, then_, else_)[i] + yield node def visit_WHILE(self, node): @@ -408,6 +417,7 @@ def visit_WHILE(self, node): for i, child in enumerate((expr_, body_)): node.children[i] = child + yield node def visit_FOR(self, node): @@ -422,6 +432,7 @@ def visit_FOR(self, node): if from_.value > to_.value and step_.value > 0: yield self.NOP return + if from_.value < to_.value and step_.value < 0: yield self.NOP return @@ -435,12 +446,6 @@ def _visit_LABEL(self, node): else: yield node - def generic_visit(self, node: Ast): - for i, child in enumerate(node.children): - node.children[i] = yield ToVisit(child) - - yield node - def _check_if_any_arg_is_an_array_and_needs_lbound_or_ubound( self, params: symbols.PARAMLIST, args: symbols.ARGLIST ): @@ -491,10 +496,7 @@ class VariableVisitor(GenericVisitor): def generic_visit(self, node: Ast): if node not in VariableVisitor._visited: VariableVisitor._visited.add(node) - for i in range(len(node.children)): - node.children[i] = yield ToVisit(node.children[i]) - - yield node + yield super().generic_visit(node) def has_circular_dependency(self, var_dependency: VarDependency) -> bool: if var_dependency.dependency == VariableVisitor._original_variable: @@ -521,7 +523,7 @@ def visit_var(entry): if entry.token != "VAR": for child in entry.children: visit_var(child) - if child.token in ("FUNCTION", "LABEL", "VAR", "VARARRAY"): + if child.token in {"FUNCTION", "LABEL", "VAR", "VARARRAY"}: result.add(VarDependency(parent=VariableVisitor._parent_variable, dependency=child)) return diff --git a/src/api/options.py b/src/api/options.py index 4e0c7c71b..943be16d3 100644 --- a/src/api/options.py +++ b/src/api/options.py @@ -1,11 +1,9 @@ -# vim: ts=4:sw=4:et: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import enum import json diff --git a/src/api/python_version_check.py b/src/api/python_version_check.py index c54154767..78f31577f 100644 --- a/src/api/python_version_check.py +++ b/src/api/python_version_check.py @@ -1,7 +1,14 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import sys from typing import Final -MINIMUM_REQUIRED_PYTHON_VERSION: Final[tuple[int, int]] = (3, 11) +MINIMUM_REQUIRED_PYTHON_VERSION: Final[tuple[int, int]] = (3, 12) def init(): diff --git a/src/api/string_labels.py b/src/api/string_labels.py index 35a87841c..f86a92dbc 100644 --- a/src/api/string_labels.py +++ b/src/api/string_labels.py @@ -1,9 +1,9 @@ -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- __doc__ = """This module is a singleton instance that contains a mapping of constant Strings to Labels. diff --git a/src/api/symboltable/__init__.py b/src/api/symboltable/__init__.py index e69de29bb..906667a8d 100644 --- a/src/api/symboltable/__init__.py +++ b/src/api/symboltable/__init__.py @@ -0,0 +1,6 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- diff --git a/src/api/symboltable/scope.py b/src/api/symboltable/scope.py index 2ef7deefc..621d98903 100644 --- a/src/api/symboltable/scope.py +++ b/src/api/symboltable/scope.py @@ -1,14 +1,11 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from collections import OrderedDict -from typing import Optional from src.api.config import OPTIONS from src.symbols.id_ import SymbolID @@ -39,7 +36,7 @@ class Scope: myFunct will be output as _myFunct_a. """ - def __init__(self, namespace: str = "", parent_scope: Optional["Scope"] = None): + def __init__(self, namespace: str = "", parent_scope: Scope | None = None): from src.symbols.funcdecl import SymbolFUNCDECL self.symbols: dict[str, SymbolID] = OrderedDict() diff --git a/src/api/symboltable/symboltable.py b/src/api/symboltable/symboltable.py index 176a6a4eb..507b3731e 100644 --- a/src/api/symboltable/symboltable.py +++ b/src/api/symboltable/symboltable.py @@ -1,12 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- - +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.api import check as check from src.api import errmsg, global_ @@ -61,7 +58,7 @@ def __init__(self): # Initialize canonical types for type_ in TYPE.types: - self.basic_types[type_] = self.declare_type(symbols.BASICTYPE(type_)) + self.basic_types[type_] = self.declare_type(name=type_.name, lineno=0, type_=symbols.BASICTYPE(type_)) @property def current_scope(self) -> Scope: @@ -71,39 +68,54 @@ def current_scope(self) -> Scope: def global_scope(self) -> Scope: return self.table[0] - def get_entry(self, id_: str, scope: Scope | None = None) -> symbols.ID | None: + def get_entry(self, id_: str, scope: Scope | None = None, lineno: int | None = None) -> symbols.ID | None: """Returns the ID entry stored in self.table, starting by the first one. Returns None if not found. If scope is not None, only the given scope is searched. """ + suffix = None if id_[-1] in DEPRECATED_SUFFIXES: + suffix = id_[-1] id_ = id_[:-1] # Remove it + entry = None if scope is not None: - return scope[id_] - - for s in self: - if s[id_] is not None: - return s[id_] + entry = scope[id_] + else: + for s in self: + if s[id_] is not None: + entry = s[id_] + break + + if entry is not None and suffix is not None and lineno is not None: + if isinstance(entry, symbols.ID) and entry.type_ is not None: + expected_type = symbols.TYPEREF(self.basic_types[SUFFIX_TYPE[suffix]], lineno) + if entry.type_ != expected_type: + if suffix == "$": + syntax_error(lineno, "Invalid $ suffix (or sigil) for numeric variable") + else: + syntax_error(lineno, f"Invalid {suffix} suffix (or sigil) for {entry.type_.name} variable") - return None # Not found + return entry - def get_existing_entry(self, id_: str, scope: Scope | None = None) -> symbols.ID: - result = self.get_entry(id_, scope) + def get_existing_entry(self, id_: str, scope: Scope | None = None, lineno: int | None = None) -> symbols.ID: + result = self.get_entry(id_, scope, lineno=lineno) assert result is not None return result - def declare(self, id_: str, lineno: int, entry: symbols.ID) -> None | symbols.ID | symbols.TYPE: + def declare(self, id_: str, lineno: int, entry: symbols.ID | symbols.TYPE) -> None | symbols.ID | symbols.TYPE: """Check there is no 'id' already declared in the current scope, and creates and returns it. Otherwise, returns None, and the caller function raises the syntax/semantic error. Parameter entry is the SymbolVAR, SymbolVARARRAY, etc. instance - The entry 'declared' field is leave untouched. Setting it if on + The entry 'declared' field is left untouched. Setting it if on behalf of the caller. """ + assert isinstance(entry, symbols.ID | symbols.TYPE) + id2 = id_ - type_ = entry.type_ + type_ = entry.type_ if isinstance(entry, symbols.ID) else entry if id2[-1] in DEPRECATED_SUFFIXES: id2 = id2[:-1] # Remove it @@ -133,7 +145,7 @@ def declare(self, id_: str, lineno: int, entry: symbols.ID) -> None | symbols.ID return entry - def declare_safe(self, id_: str, lineno: int, entry: symbols.ID): + def declare_safe(self, id_: str, lineno: int, entry: symbols.ID) -> symbols.ID | symbols.TYPE: """Like declare, but never returns None""" result = self.declare(id_, lineno, entry) assert result is not None @@ -163,7 +175,7 @@ def check_is_declared( Note: classname is not the class attribute, but the name of the class as it would appear on compiler messages. """ - result = self.get_entry(id_, scope) + result = self.get_entry(id_, scope, lineno=lineno) if isinstance(result, symbols.TYPE): return True @@ -177,13 +189,21 @@ def check_is_declared( return False return True - def check_is_undeclared(self, id_: str, lineno: int, classname="identifier", scope=None, show_error=False) -> bool: + def check_is_undeclared( + self, + id_: str, + lineno: int, + classname: str = "identifier", + scope: Scope | None = None, + *, + show_error=False, + ) -> bool: """The reverse of the above. Check the given identifier is not already declared. Returns True if OK, False otherwise. """ - result = self.get_entry(id_, scope) + result = self.get_entry(id_, scope, lineno=lineno) if result is None or not result.declared: return True @@ -197,7 +217,7 @@ def check_is_undeclared(self, id_: str, lineno: int, classname="identifier", sco ) return False - def check_class(self, id_: str, class_: CLASS, lineno: int, scope: Scope = None, show_error=True) -> bool: + def check_class(self, id_: str, class_: CLASS, lineno: int, scope: Scope | None = None, show_error=True) -> bool: """Check the id is either undefined or defined with the given class. @@ -210,7 +230,7 @@ def check_class(self, id_: str, class_: CLASS, lineno: int, scope: Scope = None, Otherwise, fails returning False. """ assert CLASS.is_valid(class_) - entry = self.get_entry(id_, scope) + entry = self.get_entry(id_, scope, lineno=lineno) if entry is None or entry.class_ in (CLASS.unknown, class_): # Undeclared yet return True @@ -331,10 +351,11 @@ def access_id( id_: str, lineno: int, scope=None, - default_type=None, - default_class=CLASS.unknown, + default_type: symbols.TYPING | None = None, + default_class: CLASS = CLASS.unknown, + *, ignore_explicit_flag=False, - ): + ) -> symbols.ID | None: """Access a symbol by its identifier and checks if it exists. If not, it's supposed to be an implicitly declared variable. @@ -347,13 +368,13 @@ def access_id( if not ignore_explicit_flag: check.check_is_declared_explicit(lineno, id_) - result = self.get_entry(id_, scope) + result = self.get_entry(id_, scope, lineno=lineno) if result is None: if default_type is None: default_type = symbols.TYPEREF(self.basic_types[global_.DEFAULT_IMPLICIT_TYPE], lineno, implicit=True) result = self.declare_safe( - id_, lineno, entry=symbols.ID(id_, lineno, type_=default_type, class_=default_class) + id_, lineno, entry=symbols.ID(id_, lineno, type_ref=default_type, class_=default_class) ) return result @@ -361,7 +382,7 @@ def access_id( # update its type. if default_type is not None and result.type_ == self.basic_types[TYPE.unknown]: if default_type == self.basic_types[TYPE.boolean]: - default_type = self.basic_types[TYPE.ubyte] + default_type = symbols.TYPEREF(self.basic_types[TYPE.ubyte], 0) result.type_ = default_type warning_implicit_type(lineno, id_, default_type.name) @@ -418,7 +439,7 @@ def access_func(self, id_: str, lineno: int, scope=None, default_type=None): """ assert default_type is None or isinstance(default_type, symbols.TYPEREF) - result = self.get_entry(id_, scope) + result = self.get_entry(id_, scope, lineno=lineno) if result is None: if default_type is None: if global_.DEFAULT_IMPLICIT_TYPE == TYPE.unknown: @@ -434,10 +455,9 @@ def access_func(self, id_: str, lineno: int, scope=None, default_type=None): return result - def access_call(self, id_: str, lineno: int, scope=None, type_=None): + def access_call(self, id_: str, lineno: int, scope=None, type_=None) -> symbols.ID | None: """Creates a func/array/string call. Checks if id is callable or not. - An identifier is "callable" if it can be followed by a list of para- - meters. + An identifier is "callable" if it can be followed by a list of parameters. This does not mean the id_ is a function, but that it allows the same syntax a function does: @@ -465,7 +485,7 @@ def access_call(self, id_: str, lineno: int, scope=None, type_=None): return entry def access_label(self, id_: str, lineno: int, scope: Scope | None = None): - result = self.get_entry(id_, scope) + result = self.get_entry(id_, scope, lineno=lineno) if result is None: result = self.declare_label(id_, lineno) result.declared = False @@ -478,7 +498,9 @@ def access_label(self, id_: str, lineno: int, scope: Scope | None = None): return result - def declare_variable(self, id_: str, lineno: int, type_, default_value=None, class_: CLASS = CLASS.var): + def declare_variable( + self, id_: str, lineno: int, type_: symbols.TYPEREF, default_value=None, class_: CLASS = CLASS.var + ): """Like the above, but checks that entry.declared is False. Otherwise, raises an error. @@ -488,7 +510,7 @@ def declare_variable(self, id_: str, lineno: int, type_, default_value=None, cla assert class_ in (CLASS.const, CLASS.var, CLASS.unknown) if not self.check_is_undeclared(id_, lineno, scope=self.current_scope, show_error=False): - entry_ = self.get_existing_entry(id_) + entry_ = self.get_existing_entry(id_, lineno=lineno) if entry_.scope == SCOPE.parameter: syntax_error( lineno, @@ -501,9 +523,9 @@ def declare_variable(self, id_: str, lineno: int, type_, default_value=None, cla if not self.check_class(id_, class_, lineno, scope=self.current_scope): return None - entry = self.get_entry(id_, scope=self.current_scope) + entry = self.get_entry(id_, scope=self.current_scope, lineno=lineno) if entry is None: - entry = self.declare(id_, lineno, symbols.ID(name=id_, lineno=lineno, type_=type_)) + entry = self.declare(id_, lineno, symbols.ID(name=id_, lineno=lineno, type_ref=type_)) assert entry is not None if entry.class_ == CLASS.unknown: @@ -525,7 +547,8 @@ def declare_variable(self, id_: str, lineno: int, type_, default_value=None, cla if entry.type_ != type_: if not type_.implicit and entry.type_ is not None: syntax_error( - lineno, "'%s' suffix is for type '%s' but it was declared as '%s'" % (id_, entry.type_, type_) + lineno, + f"'{id_!s}' suffix is for type '{entry.type_.name}' but it was declared as '{type_.name}'", ) return None @@ -536,7 +559,7 @@ def declare_variable(self, id_: str, lineno: int, type_, default_value=None, cla if default_value is not None and entry.type_ != default_value.type_: if check.is_number(default_value): - default_value = symbols.TYPECAST.make_node(entry.type_, default_value, lineno) + default_value = symbols.TYPECAST.make_node(entry.type_.type_, default_value, lineno) if default_value is None: return None else: @@ -549,29 +572,33 @@ def declare_variable(self, id_: str, lineno: int, type_, default_value=None, cla return entry - def declare_type(self, type_): - """Declares a type. + def declare_type(self, name: str, lineno: int, type_: symbols.TYPE) -> symbols.TYPE | None: + """Declares a type. The Type object must be already instantiated, as it's not + a normal ID Symbol. + Checks its name is not already used in the current scope, and that it's not a basic type. Returns the given type_ Symbol, or None on error. """ assert isinstance(type_, symbols.TYPE) + # Checks it's not a basic type if not type_.is_basic and type_.name.lower() in TYPE.TYPE_NAMES.values(): syntax_error(type_.lineno, "'%s' is a basic type and cannot be redefined" % type_.name) return None - if not self.check_is_undeclared(type_.name, type_.lineno, scope=self.current_scope, show_error=True): + if not self.check_is_undeclared(name, lineno, scope=self.current_scope, show_error=True): return None entry = self.declare(type_.name, type_.lineno, type_) + assert isinstance(entry, symbols.TYPE) return entry def declare_const(self, id_: str, lineno: int, type_, default_value): """Similar to the above. But declares a Constant.""" if not self.check_is_undeclared(id_, lineno, scope=self.current_scope, show_error=False): - entry = self.get_entry(id_) + entry = self.get_entry(id_, lineno=lineno) if entry.scope == SCOPE.parameter: syntax_error( lineno, @@ -593,11 +620,11 @@ def declare_label(self, id_: str, lineno: int) -> symbols.ID | None: """ # TODO: consider to make labels private if not self.check_is_undeclared(id_, lineno, "label"): - e = self.get_existing_entry(id_) + e = self.get_existing_entry(id_, lineno=lineno) syntax_error(lineno, f"Label '{id_}' already used at {e.filename}:{e.lineno}") return e - entry = self.get_entry(id_) + entry = self.get_entry(id_, lineno=lineno) if entry is not None and entry.declared: if entry.is_line_number: syntax_error(lineno, "Duplicated line number '%s'. Previous was at %i" % (entry.name, entry.lineno)) @@ -606,8 +633,8 @@ def declare_label(self, id_: str, lineno: int) -> symbols.ID | None: return None entry = ( - self.get_entry(id_, scope=self.current_scope) - or self.get_entry(id_, scope=self.global_scope) + self.get_entry(id_, scope=self.current_scope, lineno=lineno) + or self.get_entry(id_, scope=self.global_scope, lineno=lineno) or self.declare(id_, lineno, symbols.ID(id_, lineno, class_=CLASS.label)) ) if entry is None: @@ -627,11 +654,17 @@ def declare_label(self, id_: str, lineno: int) -> symbols.ID | None: self.move_to_global_scope(id_) # Labels are always global # TODO: not in the future entry.declared = True - entry.type_ = self.basic_types[global_.PTR_TYPE] + entry.type_ = symbols.TYPEREF(self.basic_types[global_.PTR_TYPE], lineno, implicit=False) return entry def declare_param( - self, id_: str, lineno: int, type_=None, is_array=False, default_value: Symbol | None = None + self, + id_: str, + lineno: int, + type_: symbols.TYPEREF | None = None, + default_value: Symbol | None = None, + *, + is_array: bool = False, ) -> symbols.ID | None: """Declares a parameter Check if entry.declared is False. Otherwise, raises an error. @@ -644,23 +677,35 @@ def declare_param( syntax_error_cannot_define_default_array_argument(lineno) return None - entry = self.declare_safe(id_, lineno, symbols.ID(name=id_, lineno=lineno, type_=type_)).to_vararray( - bounds=symbols.BOUNDLIST() - ) + entry = self.declare_safe( + id_, + lineno, + symbols.ID(name=id_, lineno=lineno, type_ref=type_), + ).to_vararray(bounds=symbols.BOUNDLIST()) else: - entry = self.declare_safe(id_, lineno, symbols.ID(name=id_, lineno=lineno, type_=type_)).to_var( - default_value=default_value - ) + entry = self.declare_safe( + id_, + lineno, + symbols.ID(name=id_, lineno=lineno, type_ref=type_), + ).to_var(default_value=default_value) entry.scope = SCOPE.parameter entry.declared = True if entry.type_.implicit: - warning_implicit_type(lineno, id_, type_) + warning_implicit_type(lineno, id_, type_.name) return entry - def declare_array(self, id_: str, lineno: int, type_, bounds, default_value=None, addr=None): + def declare_array( + self, + id_: str, + lineno: int, + type_: symbols.TYPEREF, + bounds: symbols.BOUNDLIST, + default_value=None, + addr: str | None = None, + ): """Declares an array in the symbol table (VARARRAY). Error if already exists. The optional parameter addr specifies if the array elements must be placed at an specific @@ -672,9 +717,9 @@ def declare_array(self, id_: str, lineno: int, type_, bounds, default_value=None if not self.check_class(id_, CLASS.array, lineno, scope=self.current_scope): return None - entry = self.get_entry(id_, self.current_scope) + entry = self.get_entry(id_, self.current_scope, lineno=lineno) if entry is None: - entry = self.declare(id_, lineno, symbols.ID(name=id_, lineno=lineno, type_=type_)) + entry = self.declare(id_, lineno, symbols.ID(name=id_, lineno=lineno, type_ref=type_)) assert entry is not None if not entry.declared: @@ -702,7 +747,7 @@ def declare_array(self, id_: str, lineno: int, type_, bounds, default_value=None type_ = entry.type_ if type_.implicit: - warning_implicit_type(lineno, id_, type_) + warning_implicit_type(lineno, id_, type_.name) if entry.class_ != CLASS.array: entry = symbols.ID.to_vararray(entry, bounds) @@ -727,7 +772,7 @@ def declare_func(self, id_: str, lineno: int, type_=None, class_=CLASS.function) if not self.check_class(id_, class_, lineno): return None - entry = self.get_entry(id_) # Must not exist or have _class = None or Function and declared = False + entry = self.get_entry(id_, lineno=lineno) # Must not exist or have CLASS.unknown/function undeclared if entry is not None: if entry.declared: assert entry.token == "FUNCTION" @@ -743,7 +788,7 @@ def declare_func(self, id_: str, lineno: int, type_=None, class_=CLASS.function) entry.mangled = f"{self.current_namespace}_{entry.name}" # HINT: mangle for nexted scopes else: - entry = self.declare(id_, lineno, symbols.ID(id_, lineno, type_=type_).to_function(class_=class_)) + entry = self.declare(id_, lineno, symbols.ID(id_, lineno, type_ref=type_).to_function(class_=class_)) assert entry.token == "FUNCTION" if entry.forwarded: @@ -821,7 +866,7 @@ def __getitem__(self, level: int): return self.table[level] def __iter__(self): - """Iterates through scopes, from current one (innermost) to global + """Iterates through scopes, from the current one (innermost) to global (outermost) """ for scope in self.table[::-1]: diff --git a/src/api/tmp_labels.py b/src/api/tmp_labels.py index a6a608dd4..d3bcd029f 100644 --- a/src/api/tmp_labels.py +++ b/src/api/tmp_labels.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api import global_ as gl LABEL_COUNTER = 0 diff --git a/src/parsetab/__init__.py b/src/api/typing/__init__.py similarity index 100% rename from src/parsetab/__init__.py rename to src/api/typing/__init__.py diff --git a/src/api/typing/system.py b/src/api/typing/system.py new file mode 100644 index 000000000..5883c66a5 --- /dev/null +++ b/src/api/typing/system.py @@ -0,0 +1,63 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + +from src.api import global_ as gl +from src.api.constants import TYPE + + +class Type: + """Represents a type in the system.""" + + def __init__(self, name: str, size: int): + self._name = name + self._size = size + + @property + def name(self): + return self._name + + @property + def size(self) -> int: + return self._size + + @property + def is_primitive(self) -> bool: + return True + + +BASIC_TYPES: dict[str, Type] = {x.name: Type(x.name, TYPE.size(x)) for x in TYPE.types} + + +class CompositeType(Type): + def __init__(self, name: str, size: int): + super().__init__(name, size) + + @property + def is_primitive(self) -> bool: + return False + + +class AliasType(CompositeType): + def __init__(self, name: str, base: Type): + super().__init__(name, base.size) + self._base = base + + @property + def base(self) -> Type: + return self._base + + +class PointerType(CompositeType): + def __init__(self, name: str, base: Type): + super().__init__(name, TYPE.size(gl.PTR_TYPE)) + self._base = base + + +class ArrayType(CompositeType): + def __init__(self, name: str, base: Type): + super().__init__(name, base.size) + self._base = base diff --git a/src/api/utils.py b/src/api/utils.py index 0a9561cb8..2cdc07e5b 100644 --- a/src/api/utils.py +++ b/src/api/utils.py @@ -1,13 +1,19 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import errno import os -import shelve import signal from collections.abc import Callable, Iterable from contextlib import contextmanager from functools import wraps from typing import IO, Any, TypeVar -from src.api import constants, errmsg, global_ +from src.api import errmsg, global_ __all__ = ( "chdir", @@ -23,19 +29,16 @@ like reading files or path management""" -SHELVE_PATH = os.path.join(constants.ZXBASIC_ROOT, "parsetab", "tabs.dbm") -SHELVE = shelve.open(SHELVE_PATH) - T = TypeVar("T") -def first(iter_: Iterable[T], default: T | None = None) -> T | None: +def first[T](iter_: Iterable[T], default: T | None = None) -> T | None: """Return the first element of an Iterable, or None if it's empty or there are no more elements to return.""" return next(iter(iter_), default) -def sfirst(iter_: Iterable[T]) -> T: +def sfirst[T](iter_: Iterable[T]) -> T: """Return the first element of an Iterable, or fails if it's empty""" return next(iter(iter_)) @@ -168,29 +171,15 @@ def eval_to_num(expr: str) -> int | float | None: if it was non-numeric.""" try: result = eval(expr, {}, {}) - except (NameError, SyntaxError, ValueError): + except NameError, SyntaxError, ValueError: return None - if isinstance(result, (int, float)): + if isinstance(result, int | float): return result return None -def load_object(key: str) -> Any: - return SHELVE[key] if key in SHELVE else None - - -def save_object(key: str, obj: Any) -> Any: - SHELVE[key] = obj - SHELVE.sync() - return obj - - -def get_or_create(key: str, fn: Callable[[], Any]) -> Any: - return load_object(key) or save_object(key, fn()) - - def timeout(seconds: Callable[[], int] | int = 10, error_message=os.strerror(errno.ETIME)): def decorator(func): def _handle_timeout(signum, frame): diff --git a/src/arch/__init__.py b/src/arch/__init__.py index 2344039d2..241f40820 100755 --- a/src/arch/__init__.py +++ b/src/arch/__init__.py @@ -1,9 +1,9 @@ -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNUv3 General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import importlib from types import ModuleType @@ -13,6 +13,7 @@ __all__ = ( "zx48k", "zxnext", + "zx81sd", ) AVAILABLE_ARCHITECTURES = __all__ diff --git a/src/arch/interface/__init__.py b/src/arch/interface/__init__.py index e69de29bb..906667a8d 100644 --- a/src/arch/interface/__init__.py +++ b/src/arch/interface/__init__.py @@ -0,0 +1,6 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- diff --git a/src/arch/interface/backend.py b/src/arch/interface/backend.py index b93f444f5..b42d6e64f 100644 --- a/src/arch/interface/backend.py +++ b/src/arch/interface/backend.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from abc import ABC, abstractmethod __all__ = ("BackendInterface",) diff --git a/src/arch/interface/icinstruction.py b/src/arch/interface/icinstruction.py new file mode 100644 index 000000000..7a75eb375 --- /dev/null +++ b/src/arch/interface/icinstruction.py @@ -0,0 +1,5 @@ +from enum import StrEnum + + +class ICInstruction(StrEnum): + pass diff --git a/src/arch/interface/optimizer.py b/src/arch/interface/optimizer.py index bd1870e52..7d2d1383f 100644 --- a/src/arch/interface/optimizer.py +++ b/src/arch/interface/optimizer.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from abc import ABC, abstractmethod diff --git a/src/arch/interface/quad.py b/src/arch/interface/quad.py index a9e4140d4..d649edaf3 100644 --- a/src/arch/interface/quad.py +++ b/src/arch/interface/quad.py @@ -1,8 +1,16 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from dataclasses import dataclass -from src.arch.z80.backend.icinstruction import ICInstruction from src.symbols.symbol_ import Symbol +from .icinstruction import ICInstruction + __all__ = ("Quad",) diff --git a/src/arch/z80/__init__.py b/src/arch/z80/__init__.py index 8e3a66681..dcbfbadac 100644 --- a/src/arch/z80/__init__.py +++ b/src/arch/z80/__init__.py @@ -1,4 +1,9 @@ -# vim:ts=4:et:sw=4: +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import src.api.global_ from src.api.constants import TYPE diff --git a/src/arch/z80/backend/_16bit.py b/src/arch/z80/backend/_16bit.py index a9a5928c9..7df6dc1d9 100644 --- a/src/arch/z80/backend/_16bit.py +++ b/src/arch/z80/backend/_16bit.py @@ -1,11 +1,9 @@ -# -------------------------------------------------------------- -# Copyleft (k) 2008, by Jose M. Rodriguez-Rosa -# (a.k.a. Boriel, http://www.boriel.com) -# -# This module contains 16 bit boolean, arithmetic and -# comparison intermediate-code translations -# -------------------------------------------------------------- - +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.api.tmp_labels import tmp_label from src.arch.z80.backend._8bit import Bits8 @@ -754,11 +752,6 @@ def bxor16(cls, ins: Quad) -> list[str]: output.append("push hl") return output - if op2 == 0xFFFF: # X ^ 0xFFFF = bNOT X - output.append(runtime_call(RuntimeLabel.NEGHL)) - output.append("push hl") - return output - output = cls.get_oper(op1, op2) output.append(runtime_call(RuntimeLabel.BXOR16)) output.append("push hl") diff --git a/src/arch/z80/backend/_32bit.py b/src/arch/z80/backend/_32bit.py index 0ac274e2a..c436cbb69 100644 --- a/src/arch/z80/backend/_32bit.py +++ b/src/arch/z80/backend/_32bit.py @@ -1,10 +1,9 @@ -# -------------------------------------------------------------- -# Copyleft (k) 2008, by Jose M. Rodriguez-Rosa -# (a.k.a. Boriel, http://www.boriel.com) -# -# This module contains 32 bit boolean, arithmetic and -# comparison intermediate-code translation -# -------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.api.tmp_labels import tmp_label from src.arch.z80.backend._8bit import Bits8 diff --git a/src/arch/z80/backend/_8bit.py b/src/arch/z80/backend/_8bit.py index f0b1a7d41..0848a1ba9 100644 --- a/src/arch/z80/backend/_8bit.py +++ b/src/arch/z80/backend/_8bit.py @@ -1,12 +1,9 @@ -# vim: ts=4:sw=4:et - -# -------------------------------------------------------------- -# Copyleft (k) 2008, by Jose M. Rodriguez-Rosa -# (a.k.a. Boriel, http://www.boriel.com) -# -# This module contains 8 bit boolean, arithmetic and -# comparison intermediate-code translations -# -------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.api.tmp_labels import tmp_label from src.arch.interface.quad import Quad diff --git a/src/arch/z80/backend/__init__.py b/src/arch/z80/backend/__init__.py index 0de04d872..ddaefbe9f 100644 --- a/src/arch/z80/backend/__init__.py +++ b/src/arch/z80/backend/__init__.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.arch.z80.optimizer.helpers import HI16, LO16 from src.arch.z80.peephole import engine diff --git a/src/arch/z80/backend/_array.py b/src/arch/z80/backend/_array.py index c25c6429f..22f88d461 100644 --- a/src/arch/z80/backend/_array.py +++ b/src/arch/z80/backend/_array.py @@ -1,12 +1,9 @@ -# vim:ts=4:et:sw=4: - -# -------------------------------------------------------------- -# Copyleft (k) 2008, by Jose M. Rodriguez-Rosa -# (a.k.a. Boriel, http://www.boriel.com) -# -# This module contains array load/store -# intermediate-code translations -# -------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from ._32bit import Bits32 from ._f16 import Fixed16 @@ -316,8 +313,8 @@ def _astoref(ins: Quad) -> list[str]: def _astorestr(ins: Quad) -> list[str]: """Stores a string value into a memory address. - It copies content of 2nd operand (string), into 1st, reallocating - dynamic memory for the 1st str. These instruction DOES ALLOW + It copies the content of the 2nd operand (string), into 1st, reallocating + dynamic memory for the 1st str. These instructions DO ALLOW immediate strings for the 2nd parameter, starting with '#'. """ output = _addr(ins[1]) diff --git a/src/arch/z80/backend/_f16.py b/src/arch/z80/backend/_f16.py index 771214cb6..106655e93 100644 --- a/src/arch/z80/backend/_f16.py +++ b/src/arch/z80/backend/_f16.py @@ -1,10 +1,10 @@ -# -------------------------------------------------------------- -# Copyleft (k) 2008, by Jose M. Rodriguez-Rosa -# (a.k.a. Boriel, http://www.boriel.com) -# -# This module contains cls.f16 (fixed) bit boolean, arithmetic and -# comparison intermediate-code translations -# -------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.arch.interface.quad import Quad from ._32bit import Bits32 diff --git a/src/arch/z80/backend/_float.py b/src/arch/z80/backend/_float.py index f1fc0324d..3263d3266 100644 --- a/src/arch/z80/backend/_float.py +++ b/src/arch/z80/backend/_float.py @@ -1,14 +1,10 @@ -# -------------------------------------------------------------- -# Copyleft (k) 2008, by Jose M. Rodriguez-Rosa -# (a.k.a. Boriel, http://www.boriel.com) -# -# This module contains float (FP) boolean, arithmetic and -# comparison intermediate-code translations -# -------------------------------------------------------------- - -# ----------------------------------------------------- -# Floating Point operators -# ----------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api import fp from src.arch.interface.quad import Quad from src.arch.z80.backend.common import _f_ops, is_float, is_int, runtime_call diff --git a/src/arch/z80/backend/_parray.py b/src/arch/z80/backend/_parray.py index 4dd28b105..dd324a9e9 100644 --- a/src/arch/z80/backend/_parray.py +++ b/src/arch/z80/backend/_parray.py @@ -1,12 +1,9 @@ -# vim:ts=4:et:sw=4: - -# -------------------------------------------------------------- -# Copyleft (k) 2008, by Jose M. Rodriguez-Rosa -# (a.k.a. Boriel, http://www.boriel.com) -# -# This module contains local array (both parameters and -# comparison intermediate-code translations) -# -------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.api import fp @@ -287,22 +284,25 @@ def _pastoref(ins: Quad) -> list[str]: def _pastorestr(ins: Quad) -> list[str]: """Stores a string value into a memory address. - It copies content of 2nd operand (string), into 1st, reallocating - dynamic memory for the 1st str. These instruction DOES ALLOW + It copies the content of the 2nd operand (string), into 1st, reallocating + dynamic memory for the 1st str. These instructions DO ALLOW immediate strings for the 2nd parameter, starting with '#'. """ output = _paddr(ins[1]) - temporal = False value = ins[2] indirect = value[0] == "*" if indirect: value = value[1:] - immediate = value[0] + immediate = value[0] == "#" if immediate: value = value[1:] + temporal = not immediate and value[0] != "$" + if temporal: + value = value[1:] + if value[0] == "_": if indirect: if immediate: @@ -316,8 +316,10 @@ def _pastorestr(ins: Quad) -> list[str]: else: output.append("ld de, (%s)" % value) else: - output.append("pop de") - temporal = True + if immediate: + output.append("ld de, %s" % value) + else: + output.append("pop de") if indirect: output.append(runtime_call(RuntimeLabel.LOAD_DE_DE)) diff --git a/src/arch/z80/backend/_pload.py b/src/arch/z80/backend/_pload.py index 99cc9062b..8aa10624d 100644 --- a/src/arch/z80/backend/_pload.py +++ b/src/arch/z80/backend/_pload.py @@ -1,12 +1,10 @@ -# vim: et:ts=4:sw=4 - -# -------------------------------------------------------------- -# Copyleft (k) 2008, by Jose M. Rodriguez-Rosa -# (a.k.a. Boriel, http://www.boriel.com) -# -# This module contains parameter load -# intermediate-code translations -# -------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from . import common from ._8bit import Bits8 from ._16bit import Bits16 diff --git a/src/arch/z80/backend/_str.py b/src/arch/z80/backend/_str.py index 4492b636c..3f57ba47e 100644 --- a/src/arch/z80/backend/_str.py +++ b/src/arch/z80/backend/_str.py @@ -1,12 +1,9 @@ -# vim:ts=4:et:sw=4: - -# -------------------------------------------------------------- -# Copyleft (k) 2008, by Jose M. Rodriguez-Rosa -# (a.k.a. Boriel, http://www.boriel.com) -# -# This module contains string arithmetic and -# comparison intermediate-code translation -# -------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .common import runtime_call from .exception import InvalidICError as InvalidIC @@ -128,7 +125,7 @@ def addstr(cls, ins: Quad) -> list[str]: """Adds 2 string values. The result is pushed onto the stack. Note: This instruction does admit direct strings (as labels). """ - (tmp1, tmp2, output) = cls.get_oper(ins[2], ins[3], no_exaf=True) + tmp1, tmp2, output = cls.get_oper(ins[2], ins[3], no_exaf=True) if tmp1: output.append("push hl") @@ -146,7 +143,7 @@ def ltstr(cls, ins: Quad) -> list[str]: """Compares & pops top 2 strings out of the stack. Temporal values are freed from memory. (a$ < b$) """ - (tmp1, tmp2, output) = cls.get_oper(ins[2], ins[3]) + _, _, output = cls.get_oper(ins[2], ins[3]) output.append(runtime_call(RuntimeLabels.STRLT)) output.append("push af") return output @@ -156,7 +153,7 @@ def gtstr(cls, ins: Quad) -> list[str]: """Compares & pops top 2 strings out of the stack. Temporal values are freed from memory. (a$ > b$) """ - (tmp1, tmp2, output) = cls.get_oper(ins[2], ins[3]) + _, _, output = cls.get_oper(ins[2], ins[3]) output.append(runtime_call(RuntimeLabels.STRGT)) output.append("push af") return output @@ -166,7 +163,7 @@ def lestr(cls, ins: Quad) -> list[str]: """Compares & pops top 2 strings out of the stack. Temporal values are freed from memory. (a$ <= b$) """ - (tmp1, tmp2, output) = cls.get_oper(ins[2], ins[3]) + _, _, output = cls.get_oper(ins[2], ins[3]) output.append(runtime_call(RuntimeLabels.STRLE)) output.append("push af") return output @@ -176,7 +173,7 @@ def gestr(cls, ins: Quad) -> list[str]: """Compares & pops top 2 strings out of the stack. Temporal values are freed from memory. (a$ >= b$) """ - (tmp1, tmp2, output) = cls.get_oper(ins[2], ins[3]) + _, _, output = cls.get_oper(ins[2], ins[3]) output.append(runtime_call(RuntimeLabels.STRGE)) output.append("push af") return output @@ -186,7 +183,7 @@ def eqstr(cls, ins: Quad) -> list[str]: """Compares & pops top 2 strings out of the stack. Temporal values are freed from memory. (a$ == b$) """ - (tmp1, tmp2, output) = cls.get_oper(ins[2], ins[3]) + _, _, output = cls.get_oper(ins[2], ins[3]) output.append(runtime_call(RuntimeLabels.STREQ)) output.append("push af") return output @@ -196,7 +193,7 @@ def nestr(cls, ins: Quad) -> list[str]: """Compares & pops top 2 strings out of the stack. Temporal values are freed from memory. (a$ != b$) """ - (tmp1, tmp2, output) = cls.get_oper(ins[2], ins[3]) + _, _, output = cls.get_oper(ins[2], ins[3]) output.append(runtime_call(RuntimeLabels.STRNE)) output.append("push af") return output @@ -204,7 +201,7 @@ def nestr(cls, ins: Quad) -> list[str]: @classmethod def lenstr(cls, ins: Quad) -> list[str]: """Returns string length""" - (tmp1, output) = cls.get_oper(ins[2], no_exaf=True) + tmp1, output = cls.get_oper(ins[2], no_exaf=True) if tmp1: output.append("push hl") @@ -246,7 +243,7 @@ def storestr(cls, ins: Quad) -> list[str]: if not indirect: op1 = "#" + op1 - tmp1, tmp2, output = cls.get_oper(op1, ins[2], no_exaf=True) + _, tmp2, output = cls.get_oper(op1, ins[2], no_exaf=True) if not tmp2: output.append(runtime_call(RuntimeLabel.STORE_STR)) @@ -329,7 +326,7 @@ def paramstr(cls, ins: Quad) -> list[str]: to a string. For indirect values, it will push the pointer to the pointer :-) """ - (tmp, output) = cls.get_oper(ins[1]) + tmp, output = cls.get_oper(ins[1]) output.pop() # Remove a register flag (useless here) tmp = ins[1][0] in ("#", "_") # Determine if the string must be duplicated @@ -346,6 +343,6 @@ def fparamstr(cls, ins: Quad) -> list[str]: value, or by loading it from memory (indirect) or directly (immediate) --prefixed with '#'-- """ - (tmp1, output) = cls.get_oper(ins[1]) + _, output = cls.get_oper(ins[1]) return output diff --git a/src/arch/z80/backend/common.py b/src/arch/z80/backend/common.py index b49f0e3f3..c6eb7a969 100644 --- a/src/arch/z80/backend/common.py +++ b/src/arch/z80/backend/common.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import math import re from enum import StrEnum @@ -378,19 +385,35 @@ def to_long(stype: DataType) -> list[str]: ) return output - if stype in {I8_t, U8_t, F16_t}: # Byte to word + if stype in (I8_t, U8_t): # Byte to word output = to_word(stype) + output.append("ld e, h") + output.append("ld d, h") - if stype != F16_t: # If it's a byte, just copy H to D,E - output.append("ld e, h") - output.append("ld d, h") + elif stype == I16_t: # Signed byte or fixed to word + output.extend( + [ + "ld a, h", + "add a, a", + "sbc a, a", + "ld e, a", + "ld d, a", + ] + ) - elif stype in (I16_t, F16_t): # Signed byte or fixed to word - output.append("ld a, h") - output.append("add a, a") - output.append("sbc a, a") - output.append("ld e, a") - output.append("ld d, a") + elif stype == F16_t: + output.extend( + [ + "ex de, hl", + "ld de, 0", + # Copies the highest bit (sign) to DE + "ld a, h", + "add a, a", + "sbc a, a", + "ld e, a", + "ld d, a", + ] + ) elif stype in (U32_t, I32_t): return [] diff --git a/src/arch/z80/backend/exception.py b/src/arch/z80/backend/exception.py index 47845e5a9..2a4524411 100644 --- a/src/arch/z80/backend/exception.py +++ b/src/arch/z80/backend/exception.py @@ -1,4 +1,9 @@ -# vim:ts=4:et:sw=4: +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.api.exception import Error diff --git a/src/arch/z80/backend/generic.py b/src/arch/z80/backend/generic.py index 0f02a1efe..08bb9286e 100644 --- a/src/arch/z80/backend/generic.py +++ b/src/arch/z80/backend/generic.py @@ -1,6 +1,9 @@ -# ---------------------------------------------------------- -# Generic instructions -# ---------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import re @@ -342,7 +345,7 @@ def _cast(ins: Quad): xsB = sB = YY_TYPES[tB] # Type sizes if tA in ("u8", "i8") and tB == "bool": - return [] # bytes are booleans already (0 = False, not 0 = True) + return [*Bits8.get_oper(ins[4]), "push af"] # bytes are booleans already (0 = False, not 0 = True) output = [] if tA in ("u8", "i8", "bool"): diff --git a/src/arch/z80/backend/icinfo.py b/src/arch/z80/backend/icinfo.py index a903dd378..84b462e62 100644 --- a/src/arch/z80/backend/icinfo.py +++ b/src/arch/z80/backend/icinfo.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from collections.abc import Callable from dataclasses import dataclass diff --git a/src/arch/z80/backend/icinstruction.py b/src/arch/z80/backend/icinstruction.py index 2ae2c19be..0b9ed3d5b 100644 --- a/src/arch/z80/backend/icinstruction.py +++ b/src/arch/z80/backend/icinstruction.py @@ -1,14 +1,21 @@ -from enum import StrEnum +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from functools import lru_cache from typing import Any +from src.arch.interface.icinstruction import ICInstruction as ICInstructionInterface from src.arch.z80.backend.exception import InvalidICError __all__ = ("ICInstruction",) # HINT: Do not use Enums here. They cannot be subclassed -class ICInstruction(StrEnum): +class ICInstruction(ICInstructionInterface): ADDU8 = "addu8" ADDI8 = "addi8" ADDI16 = "addi16" diff --git a/src/arch/z80/backend/main.py b/src/arch/z80/backend/main.py index 2a484713d..f662186c9 100644 --- a/src/arch/z80/backend/main.py +++ b/src/arch/z80/backend/main.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from __future__ import annotations import re diff --git a/src/arch/z80/backend/quad.py b/src/arch/z80/backend/quad.py index 2c231fb7d..c774e309a 100644 --- a/src/arch/z80/backend/quad.py +++ b/src/arch/z80/backend/quad.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.arch.interface.quad import Quad as BaseQuad from .icinstruction import ICInstruction diff --git a/src/arch/z80/backend/runtime/__init__.py b/src/arch/z80/backend/runtime/__init__.py index 988edfd4a..6cef2a69d 100644 --- a/src/arch/z80/backend/runtime/__init__.py +++ b/src/arch/z80/backend/runtime/__init__.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from .labels import LABEL_REQUIRED_MODULES # noqa from .labels import RUNTIME_LABELS # noqa from .labels import Labels # noqa diff --git a/src/arch/z80/backend/runtime/core.py b/src/arch/z80/backend/runtime/core.py index 3dbd2780d..554e1ce93 100644 --- a/src/arch/z80/backend/runtime/core.py +++ b/src/arch/z80/backend/runtime/core.py @@ -1,4 +1,9 @@ -# Runtime labels +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .namespace import NAMESPACE diff --git a/src/arch/z80/backend/runtime/datarestore.py b/src/arch/z80/backend/runtime/datarestore.py index 7b7817bac..9e37bc0a4 100644 --- a/src/arch/z80/backend/runtime/datarestore.py +++ b/src/arch/z80/backend/runtime/datarestore.py @@ -1,4 +1,9 @@ -# Runtime labels +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .namespace import NAMESPACE diff --git a/src/arch/z80/backend/runtime/io.py b/src/arch/z80/backend/runtime/io.py index 1a8b36789..90caa1619 100644 --- a/src/arch/z80/backend/runtime/io.py +++ b/src/arch/z80/backend/runtime/io.py @@ -1,5 +1,9 @@ -# I/O labels - +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .namespace import NAMESPACE diff --git a/src/arch/z80/backend/runtime/labels.py b/src/arch/z80/backend/runtime/labels.py index b9359048d..5e7a4c5bb 100644 --- a/src/arch/z80/backend/runtime/labels.py +++ b/src/arch/z80/backend/runtime/labels.py @@ -1,4 +1,10 @@ -# Runtime Labels +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from typing import Final from . import core, datarestore, io, math, misc, random diff --git a/src/arch/z80/backend/runtime/math.py b/src/arch/z80/backend/runtime/math.py index e91bcb435..5bedf47e7 100644 --- a/src/arch/z80/backend/runtime/math.py +++ b/src/arch/z80/backend/runtime/math.py @@ -1,4 +1,9 @@ -# Runtime labels +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .namespace import NAMESPACE diff --git a/src/arch/z80/backend/runtime/misc.py b/src/arch/z80/backend/runtime/misc.py index ed46d9ade..f6b36d061 100644 --- a/src/arch/z80/backend/runtime/misc.py +++ b/src/arch/z80/backend/runtime/misc.py @@ -1,4 +1,9 @@ -# Miscelaneous functions +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .namespace import NAMESPACE diff --git a/src/arch/z80/backend/runtime/namespace.py b/src/arch/z80/backend/runtime/namespace.py index 142bf53b9..2c53420ec 100644 --- a/src/arch/z80/backend/runtime/namespace.py +++ b/src/arch/z80/backend/runtime/namespace.py @@ -1,4 +1,9 @@ -# Define just the main Private namespace +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.api import global_ diff --git a/src/arch/z80/backend/runtime/random.py b/src/arch/z80/backend/runtime/random.py index e83bb2f79..fbe496368 100644 --- a/src/arch/z80/backend/runtime/random.py +++ b/src/arch/z80/backend/runtime/random.py @@ -1,4 +1,9 @@ -# Runtime labels +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .namespace import NAMESPACE diff --git a/src/arch/z80/beep.py b/src/arch/z80/beep.py index cf4b646be..b2077d142 100755 --- a/src/arch/z80/beep.py +++ b/src/arch/z80/beep.py @@ -1,5 +1,11 @@ #!/usr/bin/env python3 -# vim:ts=4:et:sw=4: + +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- __doc___ = """This stdlib converts duration,pitch for a beep from floating point to HL,DE Integers. diff --git a/src/arch/z80/optimizer/__init__.py b/src/arch/z80/optimizer/__init__.py index 91259bc01..912fe0e82 100644 --- a/src/arch/z80/optimizer/__init__.py +++ b/src/arch/z80/optimizer/__init__.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from .main import Optimizer __all__ = ("Optimizer",) diff --git a/src/arch/z80/optimizer/asm.py b/src/arch/z80/optimizer/asm.py index 9743de045..640d6e882 100644 --- a/src/arch/z80/optimizer/asm.py +++ b/src/arch/z80/optimizer/asm.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import re from functools import lru_cache diff --git a/src/arch/z80/optimizer/basicblock.py b/src/arch/z80/optimizer/basicblock.py index 5c9320aa5..5c7480c14 100644 --- a/src/arch/z80/optimizer/basicblock.py +++ b/src/arch/z80/optimizer/basicblock.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from __future__ import annotations from collections.abc import Iterable, Iterator, Sequence @@ -439,7 +446,7 @@ def optimize(self, patterns_list): old_unary = dict(evaluator.UNARY) # monkey-patches some functions in this optimizer level (> 2) - evaluator.UNARY[FN.GVAL] = lambda x: self.cpu.get(x) + evaluator.UNARY[FN.GVAL] = self.cpu.get evaluator.UNARY[FN.FLAGVAL] = lambda x: { "c": str(self.cpu.C) if self.cpu.C is not None else new_tmp_val(), "z": str(self.cpu.Z) if self.cpu.Z is not None else new_tmp_val(), diff --git a/src/arch/z80/optimizer/cpustate.py b/src/arch/z80/optimizer/cpustate.py index 8e845a533..a96e46207 100644 --- a/src/arch/z80/optimizer/cpustate.py +++ b/src/arch/z80/optimizer/cpustate.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import re from collections import UserDict from collections.abc import Mapping diff --git a/src/arch/z80/optimizer/errors.py b/src/arch/z80/optimizer/errors.py index 8dc95e67d..a146f482f 100644 --- a/src/arch/z80/optimizer/errors.py +++ b/src/arch/z80/optimizer/errors.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api.exception import Error diff --git a/src/arch/z80/optimizer/flow_graph.py b/src/arch/z80/optimizer/flow_graph.py index f76d2451d..011e665c3 100644 --- a/src/arch/z80/optimizer/flow_graph.py +++ b/src/arch/z80/optimizer/flow_graph.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api.debug import __DEBUG__ from .basicblock import BasicBlock, DummyBasicBlock diff --git a/src/arch/z80/optimizer/helpers.py b/src/arch/z80/optimizer/helpers.py index b2a427bca..ececec619 100644 --- a/src/arch/z80/optimizer/helpers.py +++ b/src/arch/z80/optimizer/helpers.py @@ -1,5 +1,12 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from collections.abc import Iterable, Mapping -from typing import Any, Final, TypeVar, cast +from typing import Any, Final, cast from . import patterns @@ -41,10 +48,6 @@ ) -T = TypeVar("T") -K = TypeVar("K") - - # All 'single' registers (even f FLAG one). SP is not decomposable so it's 'single' already ALL_REGS: Final[frozenset[str]] = frozenset( [ @@ -486,7 +489,7 @@ def HI16_val(x: int | str | None) -> str: return f"0{HL_SEP}{x}".split(HL_SEP)[-2] -def dict_intersection(dict_a: Mapping[K, T], dict_b: Mapping[K, T]) -> dict[K, T]: +def dict_intersection[K, T](dict_a: Mapping[K, T], dict_b: Mapping[K, T]) -> dict[K, T]: """Given 2 dictionaries a, b, returns a new one which contains the common key/pair values. e.g. for {'a': 1, 'b': 'x'}, {'a': 'q', 'b': 'x', 'c': 2} returns {'b': 'x'} diff --git a/src/arch/z80/optimizer/labelinfo.py b/src/arch/z80/optimizer/labelinfo.py index 96b3e0dc1..85173d3e2 100644 --- a/src/arch/z80/optimizer/labelinfo.py +++ b/src/arch/z80/optimizer/labelinfo.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from __future__ import annotations from dataclasses import dataclass, field diff --git a/src/arch/z80/optimizer/labels_dict.py b/src/arch/z80/optimizer/labels_dict.py index 4849966c4..6d3ad7a26 100644 --- a/src/arch/z80/optimizer/labels_dict.py +++ b/src/arch/z80/optimizer/labels_dict.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from collections import UserDict from .errors import DuplicatedLabelError diff --git a/src/arch/z80/optimizer/main.py b/src/arch/z80/optimizer/main.py index 3283ac0f7..cb4d69d98 100644 --- a/src/arch/z80/optimizer/main.py +++ b/src/arch/z80/optimizer/main.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from collections import defaultdict from src.api.config import OPTIONS diff --git a/src/arch/z80/optimizer/memcell.py b/src/arch/z80/optimizer/memcell.py index 8ec29cf90..0a5611f60 100644 --- a/src/arch/z80/optimizer/memcell.py +++ b/src/arch/z80/optimizer/memcell.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import re from functools import cached_property diff --git a/src/arch/z80/optimizer/patterns.py b/src/arch/z80/optimizer/patterns.py index 0efb09544..a3d9f1259 100644 --- a/src/arch/z80/optimizer/patterns.py +++ b/src/arch/z80/optimizer/patterns.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import re __doc__ = """ Patterns to match against generated ASM code. diff --git a/src/arch/z80/peephole/__init__.py b/src/arch/z80/peephole/__init__.py index e69de29bb..906667a8d 100644 --- a/src/arch/z80/peephole/__init__.py +++ b/src/arch/z80/peephole/__init__.py @@ -0,0 +1,6 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- diff --git a/src/arch/z80/peephole/engine.py b/src/arch/z80/peephole/engine.py index abb56d743..484c6a2b5 100644 --- a/src/arch/z80/peephole/engine.py +++ b/src/arch/z80/peephole/engine.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import os from collections.abc import Iterable from typing import NamedTuple @@ -70,7 +77,7 @@ def read_opt(opt_path: str) -> OptPattern | None: errmsg.warning(define_.lineno, "this template will be ignored", fpath) return None - except (ValueError, KeyError, TypeError): + except ValueError, KeyError, TypeError: errmsg.warning(1, "There is an error in this template and it will be ignored", fpath) else: MAXLEN = max(len(pattern_.patt), MAXLEN or 0) @@ -88,7 +95,7 @@ def read_opts(folder_path: str, result: list[OptPattern] | None = None) -> list[ try: files_to_read = [f for f in os.listdir(folder_path) if f.endswith(".opt")] - except (FileNotFoundError, NotADirectoryError, PermissionError): + except FileNotFoundError, NotADirectoryError, PermissionError: return result for fname in files_to_read: diff --git a/src/arch/z80/peephole/evaluator.py b/src/arch/z80/peephole/evaluator.py index 7234841bb..c01ebf02f 100644 --- a/src/arch/z80/peephole/evaluator.py +++ b/src/arch/z80/peephole/evaluator.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from __future__ import annotations import re @@ -231,7 +238,7 @@ def eval(self, vars_: dict[str, Any] | None = None) -> str | Evaluator | list[An try: oper = FN(self.expression[0]) assert oper in UNARY - except (AssertionError, ValueError): + except AssertionError, ValueError: raise ValueError(f"Invalid unary operator '{self.expression[0]}'") operand = self.expression[1].eval(vars_) @@ -241,7 +248,7 @@ def eval(self, vars_: dict[str, Any] | None = None) -> str | Evaluator | list[An try: oper = FN(self.expression[1]) assert oper in BINARY - except (AssertionError, ValueError): + except AssertionError, ValueError: raise ValueError(f"Invalid binary operator '{self.expression[1]}'") # Do lazy evaluation diff --git a/src/arch/z80/peephole/opts/000_o1_push_pop.opt b/src/arch/z80/peephole/opts/000_o1_push_pop.opt index ea9da4ffe..c1d370da5 100644 --- a/src/arch/z80/peephole/opts/000_o1_push_pop.opt +++ b/src/arch/z80/peephole/opts/000_o1_push_pop.opt @@ -1,14 +1,14 @@ -;; Remove sequence: -;; push xx -;; pop xx - -OLEVEL: 1 -OFLAG: 1 - -REPLACE {{ - push $1 - pop $1 -}} - -WITH {{ -}} +;; Remove sequence: +;; push xx +;; pop xx + +OLEVEL: 1 +OFLAG: 1 + +REPLACE {{ + push $1 + pop $1 +}} + +WITH {{ +}} diff --git a/src/arch/z80/peephole/opts/000_o3_push_pop.opt b/src/arch/z80/peephole/opts/000_o3_push_pop.opt index 23435c3ad..ed00d9c15 100644 --- a/src/arch/z80/peephole/opts/000_o3_push_pop.opt +++ b/src/arch/z80/peephole/opts/000_o3_push_pop.opt @@ -1,14 +1,14 @@ -;; Remove sequence: -;; push xx -;; pop xx - -OLEVEL: 3 -OFLAG: 1 - -REPLACE {{ - push $1 - pop $1 -}} - -WITH {{ -}} +;; Remove sequence: +;; push xx +;; pop xx + +OLEVEL: 3 +OFLAG: 1 + +REPLACE {{ + push $1 + pop $1 +}} + +WITH {{ +}} diff --git a/src/arch/z80/peephole/opts/001_o1_ld_XXYY_ldYYXX.opt b/src/arch/z80/peephole/opts/001_o1_ld_XXYY_ldYYXX.opt index 52e8bd562..895da86c7 100644 --- a/src/arch/z80/peephole/opts/001_o1_ld_XXYY_ldYYXX.opt +++ b/src/arch/z80/peephole/opts/001_o1_ld_XXYY_ldYYXX.opt @@ -1,18 +1,18 @@ -;; Replaces sequence: -;; ld XX, YY -;; ld YY, XX ;; This is useless - -;; With: -;; ld XX, YY - -OLEVEL: 1 -OFLAG: 2 - -REPLACE {{ - ld $1, $2 - ld $2, $1 -}} - -WITH {{ - ld $1, $2 -}} +;; Replaces sequence: +;; ld XX, YY +;; ld YY, XX ;; This is useless + +;; With: +;; ld XX, YY + +OLEVEL: 1 +OFLAG: 2 + +REPLACE {{ + ld $1, $2 + ld $2, $1 +}} + +WITH {{ + ld $1, $2 +}} diff --git a/src/arch/z80/peephole/opts/002_o1_push_hlde_to_ex.opt b/src/arch/z80/peephole/opts/002_o1_push_hlde_to_ex.opt index 0c491ad92..3964d8415 100644 --- a/src/arch/z80/peephole/opts/002_o1_push_hlde_to_ex.opt +++ b/src/arch/z80/peephole/opts/002_o1_push_hlde_to_ex.opt @@ -1,26 +1,26 @@ -;; Replaces sequence: -;; push de -;; push hl -;; pop de -;; pop hl - -;; with -;; ex de, hl - -OLEVEL: 1 -OFLAG: 3 - -REPLACE {{ - push $1 - push $2 - pop $1 - pop $2 -}} - -IF {{ - (($1 == hl) && ($2 == de)) || (($1 == de) && ($2 == hl)) -}} - -WITH {{ - ex de, hl +;; Replaces sequence: +;; push de +;; push hl +;; pop de +;; pop hl + +;; with +;; ex de, hl + +OLEVEL: 1 +OFLAG: 3 + +REPLACE {{ + push $1 + push $2 + pop $1 + pop $2 +}} + +IF {{ + (($1 == hl) && ($2 == de)) || (($1 == de) && ($2 == hl)) +}} + +WITH {{ + ex de, hl }} \ No newline at end of file diff --git a/src/arch/z80/peephole/opts/003_o1_useless_jp.opt b/src/arch/z80/peephole/opts/003_o1_useless_jp.opt index ab9eadddb..2e3131fb7 100644 --- a/src/arch/z80/peephole/opts/003_o1_useless_jp.opt +++ b/src/arch/z80/peephole/opts/003_o1_useless_jp.opt @@ -1,18 +1,18 @@ -;; Replaces sequence: -;; jp XXX -;; XXX: -;; with -;; XXX: -;; (jp is useless here) - -OLEVEL: 1 -OFLAG: 4 - -REPLACE {{ - jp$1 $2 - $2: -}} - -WITH {{ - $2: +;; Replaces sequence: +;; jp XXX +;; XXX: +;; with +;; XXX: +;; (jp is useless here) + +OLEVEL: 1 +OFLAG: 4 + +REPLACE {{ + jp$1 $2 + $2: +}} + +WITH {{ + $2: }} \ No newline at end of file diff --git a/src/arch/z80/peephole/opts/004_o1_push_pop_ldld.opt b/src/arch/z80/peephole/opts/004_o1_push_pop_ldld.opt index a1f78caed..85b149097 100644 --- a/src/arch/z80/peephole/opts/004_o1_push_pop_ldld.opt +++ b/src/arch/z80/peephole/opts/004_o1_push_pop_ldld.opt @@ -1,27 +1,27 @@ -;; Replaces sequence: -;; push hl -;; pop de -;; pop hl | ld hl, XX -;; with -;; ex de, hl -;; pop hl | ld hl, XX -;; (also reverse order allowed) - -OLEVEL: 1 -OFLAG: 5 - -REPLACE {{ - ld d, h - ld e, l - $1 hl$2 -}} - -IF {{ - ($1 == pop) || ($1 == ld) -}} - -WITH {{ - ex de, hl - $1 hl$2 -}} - +;; Replaces sequence: +;; push hl +;; pop de +;; pop hl | ld hl, XX +;; with +;; ex de, hl +;; pop hl | ld hl, XX +;; (also reverse order allowed) + +OLEVEL: 1 +OFLAG: 5 + +REPLACE {{ + ld d, h + ld e, l + $1 hl$2 +}} + +IF {{ + ($1 == pop) || ($1 == ld) +}} + +WITH {{ + ex de, hl + $1 hl$2 +}} + diff --git a/src/arch/z80/peephole/opts/005_o1_push_af_ld_aX.opt b/src/arch/z80/peephole/opts/005_o1_push_af_ld_aX.opt index 484cf5945..cfc19bd71 100644 --- a/src/arch/z80/peephole/opts/005_o1_push_af_ld_aX.opt +++ b/src/arch/z80/peephole/opts/005_o1_push_af_ld_aX.opt @@ -1,23 +1,23 @@ -;; Replaces sequence: -;; ld a, (ix|iy +|- NNN) -;; inc|dec|cp a -;; ld (ix|iy +|- NNN), a -;; with -;; inc|dec|cp (ix|iy +|- NNN) - -OLEVEL: 1 -OFLAG: 6 - -REPLACE {{ - ld a, $1 - $2 a - ld $1, a -}} - -IF {{ - (IS_INDIR($1) || ($1 == "(hl)")) && (($2 == cp) || ($2 == inc) || ($2 == dec)) -}} - -WITH {{ - $2 $1 -}} +;; Replaces sequence: +;; ld a, (ix|iy +|- NNN) +;; inc|dec|cp a +;; ld (ix|iy +|- NNN), a +;; with +;; inc|dec|cp (ix|iy +|- NNN) + +OLEVEL: 1 +OFLAG: 6 + +REPLACE {{ + ld a, $1 + $2 a + ld $1, a +}} + +IF {{ + (IS_INDIR($1) || ($1 == "(hl)")) && (($2 == cp) || ($2 == inc) || ($2 == dec)) +}} + +WITH {{ + $2 $1 +}} diff --git a/src/arch/z80/peephole/opts/006_o1_push_af_pop_XX.opt b/src/arch/z80/peephole/opts/006_o1_push_af_pop_XX.opt index a28692209..f85f844fd 100644 --- a/src/arch/z80/peephole/opts/006_o1_push_af_pop_XX.opt +++ b/src/arch/z80/peephole/opts/006_o1_push_af_pop_XX.opt @@ -1,27 +1,27 @@ -;; Replaces sequence: -;; push af -;; pop XY -;; with -;; ld X, a -;; (also reverse order allowed) - -OLEVEL: 1 -OFLAG: 7 - -REPLACE {{ - push $1 - pop $2 -}} - -IF {{ - ($1 <> $2) && (($1 == af) || ($2 == af)) -}} - -DEFINE {{ - $3 = HIREG($1) - $4 = HIREG($2) -}} - -WITH {{ - ld $4, $3 -}} +;; Replaces sequence: +;; push af +;; pop XY +;; with +;; ld X, a +;; (also reverse order allowed) + +OLEVEL: 1 +OFLAG: 7 + +REPLACE {{ + push $1 + pop $2 +}} + +IF {{ + ($1 <> $2) && (($1 == af) || ($2 == af)) +}} + +DEFINE {{ + $3 = HIREG($1) + $4 = HIREG($2) +}} + +WITH {{ + ld $4, $3 +}} diff --git a/src/arch/z80/peephole/opts/006_o3_push_af_pop_XX.opt b/src/arch/z80/peephole/opts/006_o3_push_af_pop_XX.opt index 6b2cde034..7ead1848b 100644 --- a/src/arch/z80/peephole/opts/006_o3_push_af_pop_XX.opt +++ b/src/arch/z80/peephole/opts/006_o3_push_af_pop_XX.opt @@ -1,27 +1,27 @@ -;; Replaces sequence: -;; push af -;; pop XY -;; with -;; ld X, a -;; (also reverse order allowed) - -OLEVEL: 3 -OFLAG: 7 - -REPLACE {{ - push $1 - pop $2 -}} - -IF {{ - ($1 <> $2) && (($1 == af) || ($2 == af)) -}} - -DEFINE {{ - $3 = HIREG($1) - $4 = HIREG($2) -}} - -WITH {{ - ld $4, $3 -}} +;; Replaces sequence: +;; push af +;; pop XY +;; with +;; ld X, a +;; (also reverse order allowed) + +OLEVEL: 3 +OFLAG: 7 + +REPLACE {{ + push $1 + pop $2 +}} + +IF {{ + ($1 <> $2) && (($1 == af) || ($2 == af)) +}} + +DEFINE {{ + $3 = HIREG($1) + $4 = HIREG($2) +}} + +WITH {{ + ld $4, $3 +}} diff --git a/src/arch/z80/peephole/opts/007_o1_ex_de_hl.opt b/src/arch/z80/peephole/opts/007_o1_ex_de_hl.opt index 64bfd7aec..d0494bc02 100644 --- a/src/arch/z80/peephole/opts/007_o1_ex_de_hl.opt +++ b/src/arch/z80/peephole/opts/007_o1_ex_de_hl.opt @@ -1,16 +1,16 @@ -;; Remove sequence: -;; ex de, hl -;; ex de, hl -;; -;; Also works with ex af, af' and exx - -OLEVEL: 1 -OFLAG: 8 - -REPLACE {{ - ex$1 - ex$1 -}} - -WITH {{ -}} +;; Remove sequence: +;; ex de, hl +;; ex de, hl +;; +;; Also works with ex af, af' and exx + +OLEVEL: 1 +OFLAG: 8 + +REPLACE {{ + ex$1 + ex$1 +}} + +WITH {{ +}} diff --git a/src/arch/z80/peephole/opts/008_o1_sbc_jp.opt b/src/arch/z80/peephole/opts/008_o1_sbc_jp.opt index c3ed14eaf..2af5c2b95 100644 --- a/src/arch/z80/peephole/opts/008_o1_sbc_jp.opt +++ b/src/arch/z80/peephole/opts/008_o1_sbc_jp.opt @@ -1,28 +1,28 @@ -;; Replace sequence: -;; sbc a, a -;; or a -;; jp z, __X -;; -;; With: -;; jp nc, __X - -OLEVEL: 1 -OFLAG: 9 - -REPLACE {{ - sbc a, a - or a - $1 $2, $3 -}} - -DEFINE {{ - $4 = (($2 == "z") && "nc") || ("c") -}} - -IF {{ - (($1 == "jp") || ($1 == "jr")) -}} - -WITH {{ - $1 $4, $3 -}} +;; Replace sequence: +;; sbc a, a +;; or a +;; jp z, __X +;; +;; With: +;; jp nc, __X + +OLEVEL: 1 +OFLAG: 9 + +REPLACE {{ + sbc a, a + or a + $1 $2, $3 +}} + +DEFINE {{ + $4 = (($2 == "z") && "nc") || ("c") +}} + +IF {{ + (($1 == "jp") || ($1 == "jr")) +}} + +WITH {{ + $1 $4, $3 +}} diff --git a/src/arch/z80/peephole/opts/009_o1_inc_mem.opt b/src/arch/z80/peephole/opts/009_o1_inc_mem.opt index a49d185e6..fbeee997a 100644 --- a/src/arch/z80/peephole/opts/009_o1_inc_mem.opt +++ b/src/arch/z80/peephole/opts/009_o1_inc_mem.opt @@ -1,30 +1,30 @@ -;; Replace sequence: -;; ld a, (xxx) -;; inc a | dec a -;; ld (xxx), a -;; -;; With: -;; ld hl, xxx -;; inc (hl) | dec (hl) - -OLEVEL: 1 -OFLAG: 10 - -REPLACE {{ - ld a, ($1) - $2 a - ld ($1), a -}} - -DEFINE {{ - $3 = "(" + $1 + ")" -}} - -IF {{ - (($2 == "inc") || ($2 == "dec")) && (($1 <> "hl") && (!IS_INDIR($3))) -}} - -WITH {{ - ld hl, $1 - $2 (hl) -}} +;; Replace sequence: +;; ld a, (xxx) +;; inc a | dec a +;; ld (xxx), a +;; +;; With: +;; ld hl, xxx +;; inc (hl) | dec (hl) + +OLEVEL: 1 +OFLAG: 10 + +REPLACE {{ + ld a, ($1) + $2 a + ld ($1), a +}} + +DEFINE {{ + $3 = "(" + $1 + ")" +}} + +IF {{ + (($2 == "inc") || ($2 == "dec")) && (($1 <> "hl") && (!IS_INDIR($3))) +}} + +WITH {{ + ld hl, $1 + $2 (hl) +}} diff --git a/src/arch/z80/peephole/opts/010_o1_ld_de_hl_ex_de_hl.opt b/src/arch/z80/peephole/opts/010_o1_ld_de_hl_ex_de_hl.opt index 4f29f3eac..efc9def61 100644 --- a/src/arch/z80/peephole/opts/010_o1_ld_de_hl_ex_de_hl.opt +++ b/src/arch/z80/peephole/opts/010_o1_ld_de_hl_ex_de_hl.opt @@ -1,33 +1,33 @@ -;; Replaces sequence: -;; push AB -;; pop CD -;; With: -;; ld C, A -;; ld B, D - -OLEVEL: 1 -OFLAG: 11 - -REPLACE {{ - push $1 - pop $2 -}} - -DEFINE {{ - $3 = HIREG($1) - $4 = LOREG($1) - $5 = HIREG($2) - $6 = LOREG($2) -}} - -IF {{ - ($1 <> $2) && - (($1 == hl) || ($1 == de) || ($1 == bc)) && - (($2 == hl) || ($2 == de) || ($2 == bc)) -}} - -WITH {{ - ld $5, $3 - ld $6, $4 -}} - +;; Replaces sequence: +;; push AB +;; pop CD +;; With: +;; ld C, A +;; ld B, D + +OLEVEL: 1 +OFLAG: 11 + +REPLACE {{ + push $1 + pop $2 +}} + +DEFINE {{ + $3 = HIREG($1) + $4 = LOREG($1) + $5 = HIREG($2) + $6 = LOREG($2) +}} + +IF {{ + ($1 <> $2) && + (($1 == hl) || ($1 == de) || ($1 == bc)) && + (($2 == hl) || ($2 == de) || ($2 == bc)) +}} + +WITH {{ + ld $5, $3 + ld $6, $4 +}} + diff --git a/src/arch/z80/peephole/opts/011_o1_ld_h_a_pop_af_or_h.opt b/src/arch/z80/peephole/opts/011_o1_ld_h_a_pop_af_or_h.opt index 85c8e810d..ea8f660c6 100644 --- a/src/arch/z80/peephole/opts/011_o1_ld_h_a_pop_af_or_h.opt +++ b/src/arch/z80/peephole/opts/011_o1_ld_h_a_pop_af_or_h.opt @@ -1,27 +1,27 @@ -;; Replaces sequence: -;; ld h, a -;; pop af -;; or h | and h | xor h -;; With: -;; pop de -;; or d | and d | xor d -;; -;; At O1 Level, this happens only with 8 bit operations between h and a - -OLEVEL: 1 -OFLAG: 12 - -REPLACE {{ - ld h, a - pop af - $1 h -}} - -IF {{ - ($1 IN (or, and, xor)) -}} - -WITH {{ - pop de - $1 d -}} +;; Replaces sequence: +;; ld h, a +;; pop af +;; or h | and h | xor h +;; With: +;; pop de +;; or d | and d | xor d +;; +;; At O1 Level, this happens only with 8 bit operations between h and a + +OLEVEL: 1 +OFLAG: 12 + +REPLACE {{ + ld h, a + pop af + $1 h +}} + +IF {{ + ($1 IN (or, and, xor)) +}} + +WITH {{ + pop de + $1 d +}} diff --git a/src/arch/z80/peephole/opts/012_o1_ld_hl_push_pop_de.opt b/src/arch/z80/peephole/opts/012_o1_ld_hl_push_pop_de.opt index 5b5722ff0..5b438789a 100644 --- a/src/arch/z80/peephole/opts/012_o1_ld_hl_push_pop_de.opt +++ b/src/arch/z80/peephole/opts/012_o1_ld_hl_push_pop_de.opt @@ -1,27 +1,27 @@ -;; Replaces sequence: -;; ld hl, XX -;; push hl -;; ld hl, YY -;; pop de -;; with -;; ld de, XX -;; ld hl, YY - -OLEVEL: 1 -OFLAG: 13 - -REPLACE {{ - ld hl, $1 - push hl - ld hl, $2 - pop de -}} - -IF {{ -}} - -WITH {{ - ld de, $1 - ld hl, $2 -}} - +;; Replaces sequence: +;; ld hl, XX +;; push hl +;; ld hl, YY +;; pop de +;; with +;; ld de, XX +;; ld hl, YY + +OLEVEL: 1 +OFLAG: 13 + +REPLACE {{ + ld hl, $1 + push hl + ld hl, $2 + pop de +}} + +IF {{ +}} + +WITH {{ + ld de, $1 + ld hl, $2 +}} + diff --git a/src/arch/z80/peephole/opts/013_o1_neg_neg.opt b/src/arch/z80/peephole/opts/013_o1_neg_neg.opt index 6c625562a..058ed0cf0 100644 --- a/src/arch/z80/peephole/opts/013_o1_neg_neg.opt +++ b/src/arch/z80/peephole/opts/013_o1_neg_neg.opt @@ -1,16 +1,16 @@ -;; Replaces sequence: -;; neg -;; neg -;; with -;; ; nop ;; double neg can be removed - -OLEVEL: 1 -OFLAG: 13 - -REPLACE {{ - neg - neg -}} - -WITH {{ -}} +;; Replaces sequence: +;; neg +;; neg +;; with +;; ; nop ;; double neg can be removed + +OLEVEL: 1 +OFLAG: 13 + +REPLACE {{ + neg + neg +}} + +WITH {{ +}} diff --git a/src/arch/z80/peephole/opts/014_o1_or_sbc_a_a.opt b/src/arch/z80/peephole/opts/014_o1_or_sbc_a_a.opt index 26954a515..97ee62f05 100644 --- a/src/arch/z80/peephole/opts/014_o1_or_sbc_a_a.opt +++ b/src/arch/z80/peephole/opts/014_o1_or_sbc_a_a.opt @@ -1,22 +1,22 @@ -;; Replaces sequence: -;; or|xor|and a -;; sbc a, a -;; with -;; xor a - -OLEVEL: 1 -OFLAG: 14 - -REPLACE {{ - $1 a - sbc a, a -}} - -IF {{ - ($1 IN (or, and, xor)) -}} - -WITH {{ - xor a -}} - +;; Replaces sequence: +;; or|xor|and a +;; sbc a, a +;; with +;; xor a + +OLEVEL: 1 +OFLAG: 14 + +REPLACE {{ + $1 a + sbc a, a +}} + +IF {{ + ($1 IN (or, and, xor)) +}} + +WITH {{ + xor a +}} + diff --git a/src/arch/z80/peephole/opts/015_o3_ld_r_n_not_required.opt b/src/arch/z80/peephole/opts/015_o3_ld_r_n_not_required.opt index aa814c363..839a9aa5c 100644 --- a/src/arch/z80/peephole/opts/015_o3_ld_r_n_not_required.opt +++ b/src/arch/z80/peephole/opts/015_o3_ld_r_n_not_required.opt @@ -1,18 +1,18 @@ -;; Removes sequence -;; ld a, XXXX -;; if a is not used later - -OLEVEL: 3 -OFLAG: 15 - -REPLACE {{ - ld a, $1 -}} - -IF {{ - !IS_REQUIRED(a) -}} - -WITH {{ -}} - +;; Removes sequence +;; ld a, XXXX +;; if a is not used later + +OLEVEL: 3 +OFLAG: 15 + +REPLACE {{ + ld a, $1 +}} + +IF {{ + !IS_REQUIRED(a) +}} + +WITH {{ +}} + diff --git a/src/arch/z80/peephole/opts/016_o1_xor_a_jp_z.opt b/src/arch/z80/peephole/opts/016_o1_xor_a_jp_z.opt index 66315a3da..6b6dd4593 100644 --- a/src/arch/z80/peephole/opts/016_o1_xor_a_jp_z.opt +++ b/src/arch/z80/peephole/opts/016_o1_xor_a_jp_z.opt @@ -1,22 +1,22 @@ -;; Replaces sequence: -;; or|xor|and a -;; sbc a, a -;; with -;; xor a - -OLEVEL: 1 -OFLAG: 17 - -REPLACE {{ - xor a - $1 z, $2 -}} - -IF {{ - ($1 IN (jp, jr)) -}} - -WITH {{ - $1 $2 -}} - +;; Replaces sequence: +;; or|xor|and a +;; sbc a, a +;; with +;; xor a + +OLEVEL: 1 +OFLAG: 17 + +REPLACE {{ + xor a + $1 z, $2 +}} + +IF {{ + ($1 IN (jp, jr)) +}} + +WITH {{ + $1 $2 +}} + diff --git a/src/arch/z80/peephole/opts/017_jp_cond_jp_labell.opt b/src/arch/z80/peephole/opts/017_jp_cond_jp_labell.opt index ad275466b..9a2bbeb17 100644 --- a/src/arch/z80/peephole/opts/017_jp_cond_jp_labell.opt +++ b/src/arch/z80/peephole/opts/017_jp_cond_jp_labell.opt @@ -1,30 +1,30 @@ -;; Replace sequence: -;; jp , __LABEL -;; jp OTHER -;; __LABEL: -;; -;; With: -;; jp !, OTHER - -OLEVEL: 1 -OFLAG: 17 - -REPLACE {{ - $1 $2, $3 - $4 $5 -$3: -}} - -;; Defines $4 as the negated condition -DEFINE {{ - $6 = (($2 == nc) && c) || (($2 == c) && nc) || (($2 == nz) && z) || (($2 == z) && nz) -}} - -IF {{ - $6 && (LEN($5) == 1) && (($1 == jp) || ($1 == jr)) && (($4 == jp) || ($4 == jr)) -}} - -WITH {{ - $4 $6, $5 -$3: -}} +;; Replace sequence: +;; jp , __LABEL +;; jp OTHER +;; __LABEL: +;; +;; With: +;; jp !, OTHER + +OLEVEL: 1 +OFLAG: 17 + +REPLACE {{ + $1 $2, $3 + $4 $5 +$3: +}} + +;; Defines $4 as the negated condition +DEFINE {{ + $6 = (($2 == nc) && c) || (($2 == c) && nc) || (($2 == nz) && z) || (($2 == z) && nz) +}} + +IF {{ + $6 && (LEN($5) == 1) && (($1 == jp) || ($1 == jr)) && (($4 == jp) || ($4 == jr)) +}} + +WITH {{ + $4 $6, $5 +$3: +}} diff --git a/src/arch/z80/peephole/opts/018_EQ16_by_sbchl.opt b/src/arch/z80/peephole/opts/018_EQ16_by_sbchl.opt index 00afeaca2..e2008e5ff 100644 --- a/src/arch/z80/peephole/opts/018_EQ16_by_sbchl.opt +++ b/src/arch/z80/peephole/opts/018_EQ16_by_sbchl.opt @@ -1,33 +1,33 @@ -;; Tries to optimize a == b for U/Integers -;; Replace sequence: -;; call __EQ16 -;; or a | and a -;; jp nz, ... -;; With: -;; or a -;; sbc hl, de -;; jp z, ... - -OLEVEL: 1 -OFLAG: 18 - -REPLACE {{ - call __EQ16 - $1 a - jp $2, $3 -}} - -;; Defines $4 as the negated condition -DEFINE {{ - $4 = (($2 == nz) && z) || nz -}} - -IF {{ - ($1 == or) || ($1 == and) -}} - -WITH {{ - or a - sbc hl, de - jp $4, $3 -}} +;; Tries to optimize a == b for U/Integers +;; Replace sequence: +;; call __EQ16 +;; or a | and a +;; jp nz, ... +;; With: +;; or a +;; sbc hl, de +;; jp z, ... + +OLEVEL: 1 +OFLAG: 18 + +REPLACE {{ + call __EQ16 + $1 a + jp $2, $3 +}} + +;; Defines $4 as the negated condition +DEFINE {{ + $4 = (($2 == nz) && z) || nz +}} + +IF {{ + ($1 == or) || ($1 == and) +}} + +WITH {{ + or a + sbc hl, de + jp $4, $3 +}} diff --git a/src/arch/z80/peephole/opts/019_sub1_jpnc_ora_jpz.opt b/src/arch/z80/peephole/opts/019_sub1_jpnc_ora_jpz.opt index cae3a6e4a..b7348a713 100644 --- a/src/arch/z80/peephole/opts/019_sub1_jpnc_ora_jpz.opt +++ b/src/arch/z80/peephole/opts/019_sub1_jpnc_ora_jpz.opt @@ -1,28 +1,28 @@ -;; Tries to optimize a == b for U/Bytes -;; Replace sequence: -;; sub 1 -;; jp nc, __LABEL | jp c, __LABEL -;; With: -;; or a -;; jp nz, __LABEL | jp z, __LABEL - -OLEVEL: 1 -OFLAG: 19 - -REPLACE {{ - sub 1 - $1 $2, $3 -}} - -DEFINE {{ - $4 = (($2 == nc) && nz) || z -}} - -IF {{ - (($1 == jp) || ($1 == jr)) && (($2 == nc) || ($2 == c)) -}} - -WITH {{ - or a - $1 $4, $3 -}} +;; Tries to optimize a == b for U/Bytes +;; Replace sequence: +;; sub 1 +;; jp nc, __LABEL | jp c, __LABEL +;; With: +;; or a +;; jp nz, __LABEL | jp z, __LABEL + +OLEVEL: 1 +OFLAG: 19 + +REPLACE {{ + sub 1 + $1 $2, $3 +}} + +DEFINE {{ + $4 = (($2 == nc) && nz) || z +}} + +IF {{ + (($1 == jp) || ($1 == jr)) && (($2 == nc) || ($2 == c)) +}} + +WITH {{ + or a + $1 $4, $3 +}} diff --git a/src/arch/z80/peephole/opts/020_o1_bool_norm_empty.opt b/src/arch/z80/peephole/opts/020_o1_bool_norm_empty.opt index 07acaf1ae..e10815c07 100644 --- a/src/arch/z80/peephole/opts/020_o1_bool_norm_empty.opt +++ b/src/arch/z80/peephole/opts/020_o1_bool_norm_empty.opt @@ -1,32 +1,32 @@ -;; Remove the boolean normalization if it's done after calling -;; certain routines that return the bool result already normalized. - -;; The sequence -;; sub 1 -;; sbc a, a -;; inc a -;; can be removed - -OLEVEL: 1 -OFLAG: 20 - -REPLACE {{ - $1 - sub 1 - sbc a, a - inc a -}} - -WITH {{ - $1 -}} - -IF {{ - $1 IN ("xor a", "ld a, 0", - "call .core.__GEF", "call .core.__LEI16", "call .core.__LEI8", "call .core.__LTI8", - "call .core.__ANDF", "call .core.__EQF", "call .core.__GTF", "call .core.__LTI16", - "call .core.__LEF", "call .core.__LEI32", "call .core.__LTF", "call .core.__LTI32", - "call .core.__NEF", "call .core.__NOTF", "call .core.__ORF", "call .core.__XORF", - "call .core.__STREQ", "call .core.__STRNE", "call .core.__STRLT", "call .core.__STRLE", - "call .core.__STRGT", "call .core.__STRGE") -}} +;; Remove the boolean normalization if it's done after calling +;; certain routines that return the bool result already normalized. + +;; The sequence +;; sub 1 +;; sbc a, a +;; inc a +;; can be removed + +OLEVEL: 1 +OFLAG: 20 + +REPLACE {{ + $1 + sub 1 + sbc a, a + inc a +}} + +WITH {{ + $1 +}} + +IF {{ + $1 IN ("xor a", "ld a, 0", + "call .core.__GEF", "call .core.__LEI16", "call .core.__LEI8", "call .core.__LTI8", + "call .core.__ANDF", "call .core.__EQF", "call .core.__GTF", "call .core.__LTI16", + "call .core.__LEF", "call .core.__LEI32", "call .core.__LTF", "call .core.__LTI32", + "call .core.__NEF", "call .core.__NOTF", "call .core.__ORF", "call .core.__XORF", + "call .core.__STREQ", "call .core.__STRNE", "call .core.__STRLT", "call .core.__STRLE", + "call .core.__STRGT", "call .core.__STRGE") +}} diff --git a/src/arch/z80/peephole/opts/021_o1_bool_norm_neg.opt b/src/arch/z80/peephole/opts/021_o1_bool_norm_neg.opt index dc7f71d37..278bf3539 100644 --- a/src/arch/z80/peephole/opts/021_o1_bool_norm_neg.opt +++ b/src/arch/z80/peephole/opts/021_o1_bool_norm_neg.opt @@ -1,26 +1,26 @@ -;; The sequence: -;; sbc a, a ; A is either 0 or -1 -;; sub 1 -;; sbc a, a -;; inc a -;; can be replaced by -;; neg ; A is either 0 or 1 - -OLEVEL: 1 -OFLAG: 21 - -REPLACE {{ - $1 - sub 1 - sbc a, a - inc a -}} - -WITH {{ - $1 - neg -}} - -IF {{ - $1 IN ("sbc a, a", "call .core.__NOT32", "call .core.__XOR8", "call .core.__XOR16", "call .core.__XOR32") -}} +;; The sequence: +;; sbc a, a ; A is either 0 or -1 +;; sub 1 +;; sbc a, a +;; inc a +;; can be replaced by +;; neg ; A is either 0 or 1 + +OLEVEL: 1 +OFLAG: 21 + +REPLACE {{ + $1 + sub 1 + sbc a, a + inc a +}} + +WITH {{ + $1 + neg +}} + +IF {{ + $1 IN ("sbc a, a", "call .core.__NOT32", "call .core.__XOR8", "call .core.__XOR16", "call .core.__XOR32") +}} diff --git a/src/arch/z80/peephole/opts/022_insr_a_or_a.opt b/src/arch/z80/peephole/opts/022_insr_a_or_a.opt index 406296d01..fb3c21ac6 100644 --- a/src/arch/z80/peephole/opts/022_insr_a_or_a.opt +++ b/src/arch/z80/peephole/opts/022_insr_a_or_a.opt @@ -1,24 +1,24 @@ -;; Replace sequence: -;; and X|or X|xor X|sub X|cp X|dec a|inc a -;; or a -;; -;; With: -;; and X|or X|xor X|sub X|cp X|dec a|inc a - -OLEVEL: 1 -OFLAG: 22 - -REPLACE {{ - $1 $2 - $3 a -}} - -IF {{ - (($3 == or) || ($3 == and)) && - (($1 == or) || ($1 == sub) || ($1 == and) || ($1 == xor) || ($1 == cp) || - (($2 == a) && (($1 == dec) || ($1 == inc)))) -}} - -WITH {{ - $1 $2 -}} +;; Replace sequence: +;; and X|or X|xor X|sub X|cp X|dec a|inc a +;; or a +;; +;; With: +;; and X|or X|xor X|sub X|cp X|dec a|inc a + +OLEVEL: 1 +OFLAG: 22 + +REPLACE {{ + $1 $2 + $3 a +}} + +IF {{ + (($3 == or) || ($3 == and)) && + (($1 == or) || ($1 == sub) || ($1 == and) || ($1 == xor) || ($1 == cp) || + (($2 == a) && (($1 == dec) || ($1 == inc)))) +}} + +WITH {{ + $1 $2 +}} diff --git a/src/arch/z80/peephole/opts/023_ld_hl_bc_outl.opt b/src/arch/z80/peephole/opts/023_ld_hl_bc_outl.opt index 617a2ae4b..8a863b248 100644 --- a/src/arch/z80/peephole/opts/023_ld_hl_bc_outl.opt +++ b/src/arch/z80/peephole/opts/023_ld_hl_bc_outl.opt @@ -1,27 +1,27 @@ -;; Replace sequence: -;; ld hl, (NN) | ld hl, NN | pop hl -;; ld b, h -;; ld c, l -;; out (c), a | in a, (c) -;; With: -;; ld bc, (NN) | ld bc, NN | pop bc -;; out (c), a | in a, (c) - -OLEVEL: 1 -OFLAG: 23 - -REPLACE {{ - $1 hl$2 - ld b, h - ld c, l - $3 -}} - -IF {{ - (($1 == ld) || ($1 == pop)) && (($3 == "out (c), a") || ($3 == "in a, (c)")) -}} - -WITH {{ - $1 bc$2 - $3 -}} +;; Replace sequence: +;; ld hl, (NN) | ld hl, NN | pop hl +;; ld b, h +;; ld c, l +;; out (c), a | in a, (c) +;; With: +;; ld bc, (NN) | ld bc, NN | pop bc +;; out (c), a | in a, (c) + +OLEVEL: 1 +OFLAG: 23 + +REPLACE {{ + $1 hl$2 + ld b, h + ld c, l + $3 +}} + +IF {{ + (($1 == ld) || ($1 == pop)) && (($3 == "out (c), a") || ($3 == "in a, (c)")) +}} + +WITH {{ + $1 bc$2 + $3 +}} diff --git a/src/arch/z80/peephole/opts/024_o1_bool_norm_duble_neg.opt b/src/arch/z80/peephole/opts/024_o1_bool_norm_duble_neg.opt index 315ec4ea2..c3fb6a345 100644 --- a/src/arch/z80/peephole/opts/024_o1_bool_norm_duble_neg.opt +++ b/src/arch/z80/peephole/opts/024_o1_bool_norm_duble_neg.opt @@ -1,23 +1,23 @@ -;; The sequence: -;; sbc a, a ; A is either 0 or -1 -;; sub 1 -;; sbc a, a -;; neg -;; can be replaced by -;; sbc a, a ; A is either 0 or -1 -;; inc a - -OLEVEL: 1 -OFLAG: 24 - -REPLACE {{ - sbc a, a - sub 1 - sbc a, a - neg -}} - -WITH {{ - sbc a, a - inc a -}} +;; The sequence: +;; sbc a, a ; A is either 0 or -1 +;; sub 1 +;; sbc a, a +;; neg +;; can be replaced by +;; sbc a, a ; A is either 0 or -1 +;; inc a + +OLEVEL: 1 +OFLAG: 24 + +REPLACE {{ + sbc a, a + sub 1 + sbc a, a + neg +}} + +WITH {{ + sbc a, a + inc a +}} diff --git a/src/arch/z80/peephole/opts/025_ld_h_a_oper_A.opt b/src/arch/z80/peephole/opts/025_ld_h_a_oper_A.opt index 60c7aa662..c76aef181 100644 --- a/src/arch/z80/peephole/opts/025_ld_h_a_oper_A.opt +++ b/src/arch/z80/peephole/opts/025_ld_h_a_oper_A.opt @@ -1,27 +1,27 @@ -;; Replace sequence: -;; ld h, r (r != a) -;; ld a, X -;; or/and/cp/add a,/sbc a,/sub h -;; -;; With: -;; ld a, X -;; or|xor|and|cp|add|sub r - - -OLEVEL: 1 -OFLAG: 25 - -REPLACE {{ - ld h, $1 - ld a, $2 - $3 h -}} - -IF {{ - ($1 <> a) && (($3 == or) || ($3 == xor) || ($3 == and) || ($3 == cp) || ($3 == sub) || ($3 == "add a,") || ($3 == "sbc a,")) -}} - -WITH {{ - ld a, $2 - $3 $1 -}} +;; Replace sequence: +;; ld h, r (r != a) +;; ld a, X +;; or/and/cp/add a,/sbc a,/sub h +;; +;; With: +;; ld a, X +;; or|xor|and|cp|add|sub r + + +OLEVEL: 1 +OFLAG: 25 + +REPLACE {{ + ld h, $1 + ld a, $2 + $3 h +}} + +IF {{ + ($1 <> a) && (($3 == or) || ($3 == xor) || ($3 == and) || ($3 == cp) || ($3 == sub) || ($3 == "add a,") || ($3 == "sbc a,")) +}} + +WITH {{ + ld a, $2 + $3 $1 +}} diff --git a/src/arch/z80/peephole/opts/027_ld_h_a_oper_A.opt b/src/arch/z80/peephole/opts/027_ld_h_a_oper_A.opt index d3af28ac2..3afb79b9f 100644 --- a/src/arch/z80/peephole/opts/027_ld_h_a_oper_A.opt +++ b/src/arch/z80/peephole/opts/027_ld_h_a_oper_A.opt @@ -1,22 +1,22 @@ -;; Replace sequence: -;; ld h, X -;; or h | and h -;; -;; With: -;; or X | and X - -OLEVEL: 1 -OFLAG: 27 - -REPLACE {{ - ld h, $1 - $2 h -}} - -IF {{ - ($2 IN (or, xor, and, cp, sub, "add a,", "sbc a,")) -}} - -WITH {{ - $2 $1 -}} +;; Replace sequence: +;; ld h, X +;; or h | and h +;; +;; With: +;; or X | and X + +OLEVEL: 1 +OFLAG: 27 + +REPLACE {{ + ld h, $1 + $2 h +}} + +IF {{ + ($2 IN (or, xor, and, cp, sub, "add a,", "sbc a,")) +}} + +WITH {{ + $2 $1 +}} diff --git a/src/arch/z80/peephole/opts/028_o2_pop_up.opt b/src/arch/z80/peephole/opts/028_o2_pop_up.opt index c57a27a46..855643104 100644 --- a/src/arch/z80/peephole/opts/028_o2_pop_up.opt +++ b/src/arch/z80/peephole/opts/028_o2_pop_up.opt @@ -1,28 +1,28 @@ -;; Replace sequence: -;; -;; pop rr -;; -;; With: -;; pop rr -;; -;; -;; This frees the stack ASAP and hopefully clash against a PUSH - - -OLEVEL: 2 -OFLAG: 28 - -REPLACE {{ - $2 - pop $1 -}} - -IF {{ - !(INSTR($2) IN (jp, jr, ret, call, djnz, rst)) && !NEEDS($2, (sp, $1)) && !IS_LABEL($2) && - OP1($2) <> "sp" && OP2($2) <> "sp" -}} - -WITH {{ - pop $1 - $2 -}} +;; Replace sequence: +;; +;; pop rr +;; +;; With: +;; pop rr +;; +;; +;; This frees the stack ASAP and hopefully clash against a PUSH + + +OLEVEL: 2 +OFLAG: 28 + +REPLACE {{ + $2 + pop $1 +}} + +IF {{ + !(INSTR($2) IN (jp, jr, ret, call, djnz, rst)) && !NEEDS($2, (sp, $1)) && !IS_LABEL($2) && + OP1($2) <> "sp" && OP2($2) <> "sp" +}} + +WITH {{ + pop $1 + $2 +}} diff --git a/src/arch/z80/peephole/opts/028_o3_pop_up.opt b/src/arch/z80/peephole/opts/028_o3_pop_up.opt index f2998a4ac..00f9ff0e4 100644 --- a/src/arch/z80/peephole/opts/028_o3_pop_up.opt +++ b/src/arch/z80/peephole/opts/028_o3_pop_up.opt @@ -1,27 +1,27 @@ -;; Replace sequence: -;; -;; pop rr -;; -;; With: -;; pop rr -;; -;; -;; This frees the stack ASAP and hopefully clash against a PUSH - -OLEVEL: 3 -OFLAG: 28 - -REPLACE {{ - $2 - pop $1 -}} - -IF {{ - !(INSTR($2) IN (jp, jr, ret, call, djnz, rst)) && !NEEDS($2, (sp, $1)) && !IS_LABEL($2) - && OP1($2) <> "sp" -}} - -WITH {{ - pop $1 - $2 -}} +;; Replace sequence: +;; +;; pop rr +;; +;; With: +;; pop rr +;; +;; +;; This frees the stack ASAP and hopefully clash against a PUSH + +OLEVEL: 3 +OFLAG: 28 + +REPLACE {{ + $2 + pop $1 +}} + +IF {{ + !(INSTR($2) IN (jp, jr, ret, call, djnz, rst)) && !NEEDS($2, (sp, $1)) && !IS_LABEL($2) + && OP1($2) <> "sp" +}} + +WITH {{ + pop $1 + $2 +}} diff --git a/src/arch/z80/peephole/opts/029_cp_0_or_a.opt b/src/arch/z80/peephole/opts/029_cp_0_or_a.opt index be249f8db..2a0642d68 100644 --- a/src/arch/z80/peephole/opts/029_cp_0_or_a.opt +++ b/src/arch/z80/peephole/opts/029_cp_0_or_a.opt @@ -1,20 +1,20 @@ -;; Replace sequence: -;; sub 0|cp 0 -;; -;; With: -;; or a - -OLEVEL: 1 -OFLAG: 29 - -REPLACE {{ - $1 0 -}} - -IF {{ - ($1 == cp) || ($1 == sub) -}} - -WITH {{ - or a -}} +;; Replace sequence: +;; sub 0|cp 0 +;; +;; With: +;; or a + +OLEVEL: 1 +OFLAG: 29 + +REPLACE {{ + $1 0 +}} + +IF {{ + ($1 == cp) || ($1 == sub) +}} + +WITH {{ + or a +}} diff --git a/src/arch/z80/peephole/opts/030_ora_jp_nc_jp.opt b/src/arch/z80/peephole/opts/030_ora_jp_nc_jp.opt index 9d439e145..34674817a 100644 --- a/src/arch/z80/peephole/opts/030_ora_jp_nc_jp.opt +++ b/src/arch/z80/peephole/opts/030_ora_jp_nc_jp.opt @@ -1,22 +1,22 @@ -;; Replace sequence: -;; or a | and a -;; jp nc, X | jr nc, X -;; -;; With: -;; jp X | jr X - -OLEVEL: 1 -OFLAG: 30 - -REPLACE {{ - $1 a - $2 nc, $3 -}} - -IF {{ - ($1 IN (or, xor, and, cp, sub)) && (($2 == jp) || ($2 == jr)) -}} - -WITH {{ - $2 $3 -}} +;; Replace sequence: +;; or a | and a +;; jp nc, X | jr nc, X +;; +;; With: +;; jp X | jr X + +OLEVEL: 1 +OFLAG: 30 + +REPLACE {{ + $1 a + $2 nc, $3 +}} + +IF {{ + ($1 IN (or, xor, and, cp, sub)) && (($2 == jp) || ($2 == jr)) +}} + +WITH {{ + $2 $3 +}} diff --git a/src/arch/z80/peephole/opts/031_jpX_Y_jpX.opt b/src/arch/z80/peephole/opts/031_jpX_Y_jpX.opt index 3a404e2e1..e142b6d73 100644 --- a/src/arch/z80/peephole/opts/031_jpX_Y_jpX.opt +++ b/src/arch/z80/peephole/opts/031_jpX_Y_jpX.opt @@ -1,25 +1,25 @@ -;; Removes useless (jumped over) instructions -;; Replace sequence: -;; jp X | jr X -;; YYYY -;; -;; With: -;; jp X | jr X -;; -;; YYYY must not be a label - -OLEVEL: 1 -OFLAG: 31 - -REPLACE {{ - $1 $2 - $3 -}} - -IF {{ - (($1 == jp) || ($1 == jr)) && (!IS_LABEL($3)) && (LEN($2) == 1) && (!IS_ASM($3)) -}} - -WITH {{ - $1 $2 -}} +;; Removes useless (jumped over) instructions +;; Replace sequence: +;; jp X | jr X +;; YYYY +;; +;; With: +;; jp X | jr X +;; +;; YYYY must not be a label + +OLEVEL: 1 +OFLAG: 31 + +REPLACE {{ + $1 $2 + $3 +}} + +IF {{ + (($1 == jp) || ($1 == jr)) && (!IS_LABEL($3)) && (LEN($2) == 1) && (!IS_ASM($3)) +}} + +WITH {{ + $1 $2 +}} diff --git a/src/arch/z80/peephole/opts/032_call_LOADSTR_ld_a1.opt b/src/arch/z80/peephole/opts/032_call_LOADSTR_ld_a1.opt index 9b9902746..7156931c5 100644 --- a/src/arch/z80/peephole/opts/032_call_LOADSTR_ld_a1.opt +++ b/src/arch/z80/peephole/opts/032_call_LOADSTR_ld_a1.opt @@ -1,22 +1,22 @@ -;; Replace sequence: -;; call __LOADSTR -;; ld a, 1 -;; call __PRINTSTR -;; -;; With: -;; xor a -;; call __PRINTSTR - -OLEVEL: 1 -OFLAG: 32 - -REPLACE {{ - call __LOADSTR - ld a, 1 - call __PRINTSTR -}} - -WITH {{ - xor a - call __PRINTSTR -}} +;; Replace sequence: +;; call __LOADSTR +;; ld a, 1 +;; call __PRINTSTR +;; +;; With: +;; xor a +;; call __PRINTSTR + +OLEVEL: 1 +OFLAG: 32 + +REPLACE {{ + call __LOADSTR + ld a, 1 + call __PRINTSTR +}} + +WITH {{ + xor a + call __PRINTSTR +}} diff --git a/src/arch/z80/peephole/opts/050_o1_ld_a_ld_h_pop.opt b/src/arch/z80/peephole/opts/050_o1_ld_a_ld_h_pop.opt index 8d1fc4825..e8623acdd 100644 --- a/src/arch/z80/peephole/opts/050_o1_ld_a_ld_h_pop.opt +++ b/src/arch/z80/peephole/opts/050_o1_ld_a_ld_h_pop.opt @@ -1,29 +1,29 @@ -;; Replace sequence: -;; ld a, X -;; ld h, a -;; ld a, Y -;; -;; With: -;; ld h, X -;; ld a, Y -;; -;; Whenever X is (hl), (ix + n) or a register - - -OLEVEL: 1 -OFLAG: 50 - -REPLACE {{ - ld a, $4 - ld h, a - ld a, $3 -}} - -IF {{ - (($4 == "(hl)") || (IS_INDIR($4)) || (IS_REG8($4))) -}} - -WITH {{ - ld h, $4 - ld a, $3 -}} +;; Replace sequence: +;; ld a, X +;; ld h, a +;; ld a, Y +;; +;; With: +;; ld h, X +;; ld a, Y +;; +;; Whenever X is (hl), (ix + n) or a register + + +OLEVEL: 1 +OFLAG: 50 + +REPLACE {{ + ld a, $4 + ld h, a + ld a, $3 +}} + +IF {{ + (($4 == "(hl)") || (IS_INDIR($4)) || (IS_REG8($4))) +}} + +WITH {{ + ld h, $4 + ld a, $3 +}} diff --git a/src/arch/z80/peephole/opts/051_o1_ld_a_ld_h_a_pop.opt b/src/arch/z80/peephole/opts/051_o1_ld_a_ld_h_a_pop.opt index d6cb6a736..505bd6279 100644 --- a/src/arch/z80/peephole/opts/051_o1_ld_a_ld_h_a_pop.opt +++ b/src/arch/z80/peephole/opts/051_o1_ld_a_ld_h_a_pop.opt @@ -1,29 +1,29 @@ -;; Replace sequence: -;; ld a, X -;; ld h, a -;; pop af -;; -;; With: -;; ld h, X -;; pop af -;; -;; Whenever X is (hl), (ix + n) a number or a register - - -OLEVEL: 1 -OFLAG: 50 - -REPLACE {{ - ld a, $1 - ld h, a - pop af -}} - -IF {{ - (($1 == "(hl)") || (IS_INDIR($1)) || (IS_REG8($1))) -}} - -WITH {{ - pop af - ld h, $1 -}} +;; Replace sequence: +;; ld a, X +;; ld h, a +;; pop af +;; +;; With: +;; ld h, X +;; pop af +;; +;; Whenever X is (hl), (ix + n) a number or a register + + +OLEVEL: 1 +OFLAG: 50 + +REPLACE {{ + ld a, $1 + ld h, a + pop af +}} + +IF {{ + (($1 == "(hl)") || (IS_INDIR($1)) || (IS_REG8($1))) +}} + +WITH {{ + pop af + ld h, $1 +}} diff --git a/src/arch/z80/peephole/opts/052_o3_ldhl_ld_a_l.opt b/src/arch/z80/peephole/opts/052_o3_ldhl_ld_a_l.opt index 4606ba779..98029b41a 100644 --- a/src/arch/z80/peephole/opts/052_o3_ldhl_ld_a_l.opt +++ b/src/arch/z80/peephole/opts/052_o3_ldhl_ld_a_l.opt @@ -1,22 +1,22 @@ -;; Replaces sequence: -;; ld hl, (_XXXX) -;; ld a, l -;; -;; With: -;; ld a, (_XXXX) - -OLEVEL: 3 -OFLAG: 52 - -REPLACE {{ - ld hl, ($1) - ld a, l -}} - -IF {{ - !IS_REQUIRED(hl) -}} - -WITH {{ - ld a, ($1) -}} +;; Replaces sequence: +;; ld hl, (_XXXX) +;; ld a, l +;; +;; With: +;; ld a, (_XXXX) + +OLEVEL: 3 +OFLAG: 52 + +REPLACE {{ + ld hl, ($1) + ld a, l +}} + +IF {{ + !IS_REQUIRED(hl) +}} + +WITH {{ + ld a, ($1) +}} diff --git a/src/arch/z80/peephole/opts/053_o1_ldh_nnn.opt b/src/arch/z80/peephole/opts/053_o1_ldh_nnn.opt index 59b0d39f4..58d686a32 100644 --- a/src/arch/z80/peephole/opts/053_o1_ldh_nnn.opt +++ b/src/arch/z80/peephole/opts/053_o1_ldh_nnn.opt @@ -1,21 +1,21 @@ -;; Replaces sequence: -;; ld a, (_XXXX) -;; ld h, a -;; -;; With: -;; ld hl, (_XXXX - 1) - -OLEVEL: 1 -OFLAG: 53 - -REPLACE {{ - ld a, ($1) - ld h, a - pop af -}} - - -WITH {{ - pop af - ld hl, ($1 - 1) -}} +;; Replaces sequence: +;; ld a, (_XXXX) +;; ld h, a +;; +;; With: +;; ld hl, (_XXXX - 1) + +OLEVEL: 1 +OFLAG: 53 + +REPLACE {{ + ld a, ($1) + ld h, a + pop af +}} + + +WITH {{ + pop af + ld hl, ($1 - 1) +}} diff --git a/src/arch/z80/peephole/opts/054_o3_ld_a_N_ld_hl_a.opt b/src/arch/z80/peephole/opts/054_o3_ld_a_N_ld_hl_a.opt index dcff2a0d8..af02599cb 100644 --- a/src/arch/z80/peephole/opts/054_o3_ld_a_N_ld_hl_a.opt +++ b/src/arch/z80/peephole/opts/054_o3_ld_a_N_ld_hl_a.opt @@ -1,24 +1,24 @@ -;; Replaces sequence: -;; ld hl, (_XXXX) -;; ld a, l -;; -;; With: -;; ld a, (_XXXX) - -OLEVEL: 3 -OFLAG: 54 - -REPLACE {{ - ld a, $1 - ld hl, ($2) - ld (hl), a -}} - -IF {{ - !IS_REQUIRED(a) && IS_IMMED($1) -}} - -WITH {{ - ld hl, ($2) - ld (hl), $1 -}} +;; Replaces sequence: +;; ld hl, (_XXXX) +;; ld a, l +;; +;; With: +;; ld a, (_XXXX) + +OLEVEL: 3 +OFLAG: 54 + +REPLACE {{ + ld a, $1 + ld hl, ($2) + ld (hl), a +}} + +IF {{ + !IS_REQUIRED(a) && IS_IMMED($1) +}} + +WITH {{ + ld hl, ($2) + ld (hl), $1 +}} diff --git a/src/arch/z80/peephole/opts/100_o3_ld_rr_N_ld_ss_N.opt b/src/arch/z80/peephole/opts/100_o3_ld_rr_N_ld_ss_N.opt index 201dfb972..99cc5cdd2 100644 --- a/src/arch/z80/peephole/opts/100_o3_ld_rr_N_ld_ss_N.opt +++ b/src/arch/z80/peephole/opts/100_o3_ld_rr_N_ld_ss_N.opt @@ -1,20 +1,20 @@ -;; Removes useless LD's -;; Tries to guess the value of the 1st and 2nd operands. -;; If they're the same, this LD is useless - -OLEVEL: 3 -OFLAG: 100 - -REPLACE {{ - ld $1, $2 -}} - -IF {{ - (GVAL($1) == GVAL($2)) || - ((IS_REG8($1) || IS_REG8($2)) && - (LOVAL(GVAL($1)) == LOVAL(GVAL($2))) - ) -}} - -WITH {{ -}} +;; Removes useless LD's +;; Tries to guess the value of the 1st and 2nd operands. +;; If they're the same, this LD is useless + +OLEVEL: 3 +OFLAG: 100 + +REPLACE {{ + ld $1, $2 +}} + +IF {{ + (GVAL($1) == GVAL($2)) || + ((IS_REG8($1) || IS_REG8($2)) && + (LOVAL(GVAL($1)) == LOVAL(GVAL($2))) + ) +}} + +WITH {{ +}} diff --git a/src/arch/z80/peephole/opts/101_o3_ld_rr_N_ld_h_r_ld_l_r.opt b/src/arch/z80/peephole/opts/101_o3_ld_rr_N_ld_h_r_ld_l_r.opt index 30b229046..028800e8d 100644 --- a/src/arch/z80/peephole/opts/101_o3_ld_rr_N_ld_h_r_ld_l_r.opt +++ b/src/arch/z80/peephole/opts/101_o3_ld_rr_N_ld_h_r_ld_l_r.opt @@ -1,36 +1,36 @@ -;; Replace sequence: -;; ld hl, N -;; ld b, h -;; ld c, l -;; -;; With: -;; ld bc, N -;; -;; If hl is not required later -;; also works with any other 16bit register - -OLEVEL: 3 -OFLAG: 101 - -REPLACE {{ - ld $1, $2 - ld $3, $4 - ld $5, $6 -}} - -IF {{ - ( - IS_REG16($1) && IS_REG16($7) && !IS_REQUIRED($1) - && IS_REG8($5) && IS_REG8($3) && IS_REG8($4) && IS_REG8($6) - && ((HIREG($1) == $4 && HIREG($7) == $3 && LOREG($1) == $6 && LOREG($7) == $5) || - (HIREG($1) == $6 && HIREG($7) == $5 && LOREG($1) == $4 && LOREG($7) == $3)) - ) -}} - -DEFINE {{ - $7 = IS_REG16($3 + $5) && ($3 + $5) || ($5 + $3) -}} - -WITH {{ - ld $7, $2 -}} +;; Replace sequence: +;; ld hl, N +;; ld b, h +;; ld c, l +;; +;; With: +;; ld bc, N +;; +;; If hl is not required later +;; also works with any other 16bit register + +OLEVEL: 3 +OFLAG: 101 + +REPLACE {{ + ld $1, $2 + ld $3, $4 + ld $5, $6 +}} + +IF {{ + ( + IS_REG16($1) && IS_REG16($7) && !IS_REQUIRED($1) + && IS_REG8($5) && IS_REG8($3) && IS_REG8($4) && IS_REG8($6) + && ((HIREG($1) == $4 && HIREG($7) == $3 && LOREG($1) == $6 && LOREG($7) == $5) || + (HIREG($1) == $6 && HIREG($7) == $5 && LOREG($1) == $4 && LOREG($7) == $3)) + ) +}} + +DEFINE {{ + $7 = IS_REG16($3 + $5) && ($3 + $5) || ($5 + $3) +}} + +WITH {{ + ld $7, $2 +}} diff --git a/src/arch/z80/peephole/opts/102_o3_ld_r_n_ld_r_n2.opt b/src/arch/z80/peephole/opts/102_o3_ld_r_n_ld_r_n2.opt index b8a491e11..b9947b2fc 100644 --- a/src/arch/z80/peephole/opts/102_o3_ld_r_n_ld_r_n2.opt +++ b/src/arch/z80/peephole/opts/102_o3_ld_r_n_ld_r_n2.opt @@ -1,18 +1,18 @@ -;; Removes useless LD's -;; Replaces; -;; LD r, XXX -;; LD r, YYY -;; With: -;; LD r, YYY - -OLEVEL: 3 -OFLAG: 102 - -REPLACE {{ - ld $1, $2 - ld $1, $3 -}} - -WITH {{ - ld $1, $3 -}} +;; Removes useless LD's +;; Replaces; +;; LD r, XXX +;; LD r, YYY +;; With: +;; LD r, YYY + +OLEVEL: 3 +OFLAG: 102 + +REPLACE {{ + ld $1, $2 + ld $1, $3 +}} + +WITH {{ + ld $1, $3 +}} diff --git a/src/arch/z80/peephole/opts/103_o3_or_and_a.opt b/src/arch/z80/peephole/opts/103_o3_or_and_a.opt index fb00a768e..51590a888 100644 --- a/src/arch/z80/peephole/opts/103_o3_or_and_a.opt +++ b/src/arch/z80/peephole/opts/103_o3_or_and_a.opt @@ -1,17 +1,17 @@ -;; Removes useless AND, OR a - -OLEVEL: 3 -OFLAG: 103 - -REPLACE {{ - $1 $2 -}} - -IF {{ - ($1 IN (and, or)) && - (($2 == a) || (!IS_REQUIRED(a))) && - !IS_REQUIRED(f) -}} - -WITH {{ -}} +;; Removes useless AND, OR a + +OLEVEL: 3 +OFLAG: 103 + +REPLACE {{ + $1 $2 +}} + +IF {{ + ($1 IN (and, or)) && + (($2 == a) || (!IS_REQUIRED(a))) && + !IS_REQUIRED(f) +}} + +WITH {{ +}} diff --git a/src/arch/z80/peephole/opts/103_o3_xor_a.opt b/src/arch/z80/peephole/opts/103_o3_xor_a.opt index 4467f3323..8db94ac6b 100644 --- a/src/arch/z80/peephole/opts/103_o3_xor_a.opt +++ b/src/arch/z80/peephole/opts/103_o3_xor_a.opt @@ -1,15 +1,15 @@ -;; Removes useless XOR a - -OLEVEL: 3 -OFLAG: 103 - -REPLACE {{ - xor a -}} - -IF {{ - (GVAL(a) == 0) && ((FLAGVAL(c) == 0 && FLAGVAL(z) == 1) || !IS_REQUIRED(f)) -}} - -WITH {{ -}} +;; Removes useless XOR a + +OLEVEL: 3 +OFLAG: 103 + +REPLACE {{ + xor a +}} + +IF {{ + (GVAL(a) == 0) && ((FLAGVAL(c) == 0 && FLAGVAL(z) == 1) || !IS_REQUIRED(f)) +}} + +WITH {{ +}} diff --git a/src/arch/z80/peephole/opts/104_o3_opt27.opt b/src/arch/z80/peephole/opts/104_o3_opt27.opt index 09409c591..3b1ff6a51 100644 --- a/src/arch/z80/peephole/opts/104_o3_opt27.opt +++ b/src/arch/z80/peephole/opts/104_o3_opt27.opt @@ -1,29 +1,29 @@ -;; Replaces -;; -;; ld r, XX -;; cp r | sub r | and r | or r | xor r -;; -;; With -;; cp XX | sub XX | and XX | or XX | xor XX -;; -;; IF r != a, and r is not required later -;; -;; Formerly OPT27 - -OLEVEL: 3 -OFLAG: 104 - -REPLACE {{ - ld $1, $2 - $3 $1 -}} - -IF {{ - ($1 <> a) && - !IS_REQUIRED($1) && !IS_REQUIRED(f) && - ($3 IN (cp, or, and, xor, sub)) -}} - -WITH {{ - $3 $2 -}} +;; Replaces +;; +;; ld r, XX +;; cp r | sub r | and r | or r | xor r +;; +;; With +;; cp XX | sub XX | and XX | or XX | xor XX +;; +;; IF r != a, and r is not required later +;; +;; Formerly OPT27 + +OLEVEL: 3 +OFLAG: 104 + +REPLACE {{ + ld $1, $2 + $3 $1 +}} + +IF {{ + ($1 <> a) && + !IS_REQUIRED($1) && !IS_REQUIRED(f) && + ($3 IN (cp, or, and, xor, sub)) +}} + +WITH {{ + $3 $2 +}} diff --git a/src/arch/z80/peephole/opts/105_o3_opt27.opt b/src/arch/z80/peephole/opts/105_o3_opt27.opt index f43b81c87..67abb36c1 100644 --- a/src/arch/z80/peephole/opts/105_o3_opt27.opt +++ b/src/arch/z80/peephole/opts/105_o3_opt27.opt @@ -1,29 +1,29 @@ -;; Replaces -;; -;; ld r, XX -;; add a, r | adc a, r | sbc a, r -;; -;; With -;; add a, XX | adc a, XX | sbc a, XX -;; -;; IF r != a, and r is not required later -;; -;; Formerly OPT27 - -OLEVEL: 3 -OFLAG: 105 - -REPLACE {{ - ld $1, $2 - $3 a, $1 -}} - -IF {{ - ($1 <> a) && - !IS_REQUIRED($1) && !IS_REQUIRED(f) && - ($3 IN (add, adc, sbc)) -}} - -WITH {{ - $3 a, $2 -}} +;; Replaces +;; +;; ld r, XX +;; add a, r | adc a, r | sbc a, r +;; +;; With +;; add a, XX | adc a, XX | sbc a, XX +;; +;; IF r != a, and r is not required later +;; +;; Formerly OPT27 + +OLEVEL: 3 +OFLAG: 105 + +REPLACE {{ + ld $1, $2 + $3 a, $1 +}} + +IF {{ + ($1 <> a) && + !IS_REQUIRED($1) && !IS_REQUIRED(f) && + ($3 IN (add, adc, sbc)) +}} + +WITH {{ + $3 a, $2 +}} diff --git a/src/arch/z80/peephole/opts/106_o3_exdehl.opt b/src/arch/z80/peephole/opts/106_o3_exdehl.opt index c250b69aa..db95cfcd3 100644 --- a/src/arch/z80/peephole/opts/106_o3_exdehl.opt +++ b/src/arch/z80/peephole/opts/106_o3_exdehl.opt @@ -1,23 +1,23 @@ -;; Replaces -;; -;; ld de, nn -;; -;; With -;; ex de, hl -;; -;; if hl is no longer required, and guessed val of hl is nn - -OLEVEL: 3 -OFLAG: 106 - -REPLACE {{ - ld de, $1 -}} - -IF {{ - GVAL(hl) == GVAL($1) && !IS_REQUIRED(hl) -}} - -WITH {{ - ex de, hl -}} +;; Replaces +;; +;; ld de, nn +;; +;; With +;; ex de, hl +;; +;; if hl is no longer required, and guessed val of hl is nn + +OLEVEL: 3 +OFLAG: 106 + +REPLACE {{ + ld de, $1 +}} + +IF {{ + GVAL(hl) == GVAL($1) && !IS_REQUIRED(hl) +}} + +WITH {{ + ex de, hl +}} diff --git a/src/arch/z80/peephole/opts/107_o3_exdehl_ldde.opt b/src/arch/z80/peephole/opts/107_o3_exdehl_ldde.opt index 78386b331..78147aa54 100644 --- a/src/arch/z80/peephole/opts/107_o3_exdehl_ldde.opt +++ b/src/arch/z80/peephole/opts/107_o3_exdehl_ldde.opt @@ -1,25 +1,25 @@ -;; Replaces -;; -;; ld hl, nn -;; ex de, hl -;; -;; With -;; ld de, nn -;; -;; if hl is no longer required - -OLEVEL: 3 -OFLAG: 107 - -REPLACE {{ - ld hl, $1 - ex de, hl -}} - -IF {{ - !IS_REQUIRED(hl) -}} - -WITH {{ - ld de, $1 -}} +;; Replaces +;; +;; ld hl, nn +;; ex de, hl +;; +;; With +;; ld de, nn +;; +;; if hl is no longer required + +OLEVEL: 3 +OFLAG: 107 + +REPLACE {{ + ld hl, $1 + ex de, hl +}} + +IF {{ + !IS_REQUIRED(hl) +}} + +WITH {{ + ld de, $1 +}} diff --git a/src/arch/z80/peephole/opts/108_o3_inc_dec.opt b/src/arch/z80/peephole/opts/108_o3_inc_dec.opt index e7849f0e0..2d7f5a24b 100644 --- a/src/arch/z80/peephole/opts/108_o3_inc_dec.opt +++ b/src/arch/z80/peephole/opts/108_o3_inc_dec.opt @@ -1,28 +1,28 @@ -;; Replaces -;; -;; ld rr, nn -;; -;; With -;; inc / dec rr -;; -;; If the previous known value of rr is nn - / + 1 - - -OLEVEL: 3 -OFLAG: 108 - -REPLACE {{ - ld $1, $2 -}} - -DEFINE {{ - $3 = ($2 == GVAL($1) .+ 1) && "inc" || ($2 == GVAL($1) .- 1) && "dec" -}} - -IF {{ - IS_REG16($1) && $3 <> "" -}} - -WITH {{ - $3 $1 -}} +;; Replaces +;; +;; ld rr, nn +;; +;; With +;; inc / dec rr +;; +;; If the previous known value of rr is nn - / + 1 + + +OLEVEL: 3 +OFLAG: 108 + +REPLACE {{ + ld $1, $2 +}} + +DEFINE {{ + $3 = ($2 == GVAL($1) .+ 1) && "inc" || ($2 == GVAL($1) .- 1) && "dec" +}} + +IF {{ + IS_REG16($1) && $3 <> "" +}} + +WITH {{ + $3 $1 +}} diff --git a/src/arch/z80/peephole/opts/109_o4_ld_mem_op_ld_mem.opt b/src/arch/z80/peephole/opts/109_o4_ld_mem_op_ld_mem.opt index 713718dd2..95e730ed7 100644 --- a/src/arch/z80/peephole/opts/109_o4_ld_mem_op_ld_mem.opt +++ b/src/arch/z80/peephole/opts/109_o4_ld_mem_op_ld_mem.opt @@ -1,17 +1,17 @@ -;; Removes useless operation -;; Tries to guess if the result of the operation LD, OR, AND, XOR, ADC, ADD, SBC is later used - -OLEVEL: 4 -OFLAG: 109 - -REPLACE {{ - $1 $2, $3 -}} - -IF {{ -($2 <> sp) && ($2 <> ix) && !IS_REQUIRED($2) && - ($1 == ld || (!IS_REQUIRED(f) && ($1 == add || $1 == adc || $1 == sbc))) -}} - -WITH {{ -}} +;; Removes useless operation +;; Tries to guess if the result of the operation LD, OR, AND, XOR, ADC, ADD, SBC is later used + +OLEVEL: 4 +OFLAG: 109 + +REPLACE {{ + $1 $2, $3 +}} + +IF {{ +($2 <> sp) && ($2 <> ix) && !IS_REQUIRED($2) && + ($1 == ld || (!IS_REQUIRED(f) && ($1 == add || $1 == adc || $1 == sbc))) +}} + +WITH {{ +}} diff --git a/src/arch/z80/peephole/opts/110_o4_ld_mem_op_ld_mem.opt b/src/arch/z80/peephole/opts/110_o4_ld_mem_op_ld_mem.opt index aa76419ef..3780f39d5 100644 --- a/src/arch/z80/peephole/opts/110_o4_ld_mem_op_ld_mem.opt +++ b/src/arch/z80/peephole/opts/110_o4_ld_mem_op_ld_mem.opt @@ -1,19 +1,19 @@ -;; Removes useless LD's -;; Tries to guess the value of the 1st and 2nd operands. -;; If they're the same, this LD is useless - -OLEVEL: 4 -OFLAG: 110 - -REPLACE {{ - $1 $2 -}} - -IF {{ - ((($1 == inc || $1 == dec) && !IS_REQUIRED($2)) - || ($1 == sub && !IS_REQUIRED(a)) - ) && ($2 <> sp) && !IS_REQUIRED(f) -}} - -WITH {{ -}} +;; Removes useless LD's +;; Tries to guess the value of the 1st and 2nd operands. +;; If they're the same, this LD is useless + +OLEVEL: 4 +OFLAG: 110 + +REPLACE {{ + $1 $2 +}} + +IF {{ + ((($1 == inc || $1 == dec) && !IS_REQUIRED($2)) + || ($1 == sub && !IS_REQUIRED(a)) + ) && ($2 <> sp) && !IS_REQUIRED(f) +}} + +WITH {{ +}} diff --git a/src/arch/z80/peephole/parser.py b/src/arch/z80/peephole/parser.py index 99d2958e8..8e312773c 100644 --- a/src/arch/z80/peephole/parser.py +++ b/src/arch/z80/peephole/parser.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import re from collections import defaultdict from types import MappingProxyType diff --git a/src/arch/z80/peephole/pattern.py b/src/arch/z80/peephole/pattern.py index c6bf76a49..aee82d5cd 100644 --- a/src/arch/z80/peephole/pattern.py +++ b/src/arch/z80/peephole/pattern.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import itertools import re diff --git a/src/arch/z80/peephole/template.py b/src/arch/z80/peephole/template.py index 740548467..8b55cf284 100644 --- a/src/arch/z80/peephole/template.py +++ b/src/arch/z80/peephole/template.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from .pattern import BasicLinePattern diff --git a/src/arch/z80/visitor/__init__.py b/src/arch/z80/visitor/__init__.py index e69de29bb..906667a8d 100644 --- a/src/arch/z80/visitor/__init__.py +++ b/src/arch/z80/visitor/__init__.py @@ -0,0 +1,6 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- diff --git a/src/arch/z80/visitor/builtin_translator.py b/src/arch/z80/visitor/builtin_translator.py index 3a040e8e8..85e47f0d9 100644 --- a/src/arch/z80/visitor/builtin_translator.py +++ b/src/arch/z80/visitor/builtin_translator.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api import global_ as gl from src.api.constants import SCOPE, TYPE from src.api.global_ import optemps @@ -12,6 +19,13 @@ class BuiltinTranslator(TranslatorVisitor): REQUIRES = backend.REQUIRES + def __init__(self, backend: backend.Backend, parent_visitor: TranslatorVisitor): + super().__init__(backend) + self.parent_visitor = parent_visitor + + def visit(self, node): + return self.parent_visitor.visit(node) + # region STRING Functions def visit_INKEY(self, node): self.runtime_call(RuntimeLabel.INKEY, Type.string.size) @@ -118,7 +132,7 @@ def visit_SQR(self, node): # endregion def visit_LBOUND(self, node): - yield node.operands[1] + yield self.visit(node.operands[1]) self.ic_param(gl.BOUND_TYPE, node.operands[1].t) entry = node.operands[0] if entry.scope == SCOPE.global_: @@ -134,7 +148,7 @@ def visit_LBOUND(self, node): self.runtime_call(RuntimeLabel.LBOUND, self.TYPE(gl.BOUND_TYPE).size) def visit_UBOUND(self, node): - yield node.operands[1] + yield self.visit(node.operands[1]) self.ic_param(gl.BOUND_TYPE, node.operands[1].t) entry = node.operands[0] if entry.scope == SCOPE.global_: diff --git a/src/arch/z80/visitor/function_translator.py b/src/arch/z80/visitor/function_translator.py index b2b94a0b0..26257203e 100644 --- a/src/arch/z80/visitor/function_translator.py +++ b/src/arch/z80/visitor/function_translator.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import src.api from src.api import global_ as gl from src.api.config import OPTIONS @@ -17,9 +24,9 @@ class FunctionTranslator(Translator): REQUIRES = backend.REQUIRES def __init__(self, backend: Backend, function_list: list[symbols.ID]): + super().__init__(backend) if function_list is None: function_list = [] - super().__init__(backend) assert isinstance(function_list, list) assert all(x.token == "FUNCTION" for x in function_list) @@ -61,20 +68,20 @@ def visit_FUNCTION(self, node): if local_var.class_ == CLASS.array and local_var.scope == SCOPE.local: lbound_label = local_var.mangled + ".__LBOUND__" ubound_label = local_var.mangled + ".__UBOUND__" - lbound_needed = not local_var.is_zero_based and local_var.is_dynamically_accessed - - bound_ptrs = [lbound_label if lbound_needed else "0", "0"] - if local_var.ubound_used: - bound_ptrs[1] = ubound_label + lbound_needed = not local_var.is_zero_based and ( + local_var.is_dynamically_accessed or local_var.lbound_used or OPTIONS.array_check + ) + ubound_needed = local_var.ubound_used or OPTIONS.array_check + bound_ptrs = [lbound_label if lbound_needed else "0", ubound_label if ubound_needed else "0"] - if bound_ptrs != ["0", "0"]: + if lbound_needed or ubound_needed: OPTIONS["__DEFINES"].value["__ZXB_USE_LOCAL_ARRAY_WITH_BOUNDS__"] = "" if lbound_needed: l = ["%04X" % bound.lower for bound in local_var.bounds] bound_tables.append(LabelledData(lbound_label, l)) - if local_var.ubound_used: + if ubound_needed: l = ["%04X" % bound.upper for bound in local_var.bounds] bound_tables.append(LabelledData(ubound_label, l)) @@ -106,7 +113,7 @@ def visit_FUNCTION(self, node): self.ic_lvard(local_var.offset, q) for i in node.ref.body: - yield i + yield self.visit(i) self.ic_label("%s__leave" % node.mangled) diff --git a/src/arch/z80/visitor/translator.py b/src/arch/z80/visitor/translator.py index 8b47a634b..66725c0d0 100644 --- a/src/arch/z80/visitor/translator.py +++ b/src/arch/z80/visitor/translator.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from collections.abc import Callable from typing import Any, NamedTuple @@ -48,27 +55,27 @@ def visit_CLS(self, node): def visit_NUMBER(self, node): __DEBUG__("NUMBER " + str(node)) - yield node.value + yield node def visit_STRING(self, node): __DEBUG__("STRING " + str(node)) node.t = "#" + self.add_string_label(node.value) - yield node.t + yield node def visit_END(self, node): - yield node.children[0] + yield self.visit(node.children[0]) __DEBUG__("END") self.ic_end(node.children[0].t) def visit_ERROR(self, node): # Raises an error - yield node.children[0] + yield self.visit(node.children[0]) self.ic_fparam(TYPE.ubyte, node.children[0].t) self.runtime_call(RuntimeLabel.ERROR, 0) def visit_STOP(self, node): """Returns to BASIC with an error code""" - yield node.children[0] + yield self.visit(node.children[0]) self.ic_fparam(TYPE.ubyte, node.children[0].t) self.runtime_call(RuntimeLabel.STOP, 0) self.ic_end(0) @@ -76,15 +83,16 @@ def visit_STOP(self, node): def visit_LET(self, node): assert node.children[0].token == "VAR" if self.O_LEVEL < 2 or node.children[0].accessed or node.children[1].token == "CONSTEXPR": - yield node.children[1] + yield self.visit(node.children[1]) + __DEBUG__("LET") self.emit_let_left_part(node) def visit_POKE(self, node): ch0 = node.children[0] ch1 = node.children[1] - yield ch0 - yield ch1 + yield self.visit(ch0) + yield self.visit(ch1) if ch0.token == "VAR" and ch0.class_ != CLASS.const and ch0.scope == SCOPE.global_: self.ic_store(ch1.type_, "*" + str(ch0.t), ch1.t) @@ -92,7 +100,7 @@ def visit_POKE(self, node): self.ic_store(ch1.type_, str(ch0.t), ch1.t) def visit_RANDOMIZE(self, node): - yield node.children[0] + yield self.visit(node.children[0]) self.ic_fparam(node.children[0].type_, node.children[0].t) self.runtime_call(RuntimeLabel.RANDOMIZE, 0) @@ -100,7 +108,7 @@ def visit_LABEL(self, node): self.ic_label(node.mangled) def visit_CONST(self, node): - yield node.symbol + yield self.visit(node.symbol) def visit_VAR(self, node): __DEBUG__( @@ -110,7 +118,7 @@ def visit_VAR(self, node): scope = node.scope if node.t == node.mangled and scope == SCOPE.global_: - return + return node p = "*" if node.byref else "" # Indirection prefix @@ -120,6 +128,8 @@ def visit_VAR(self, node): offset = node.offset self.ic_pload(node.type_, node.t, p + str(-offset)) + return node + def visit_CONSTEXPR(self, node): yield node.t @@ -131,7 +141,7 @@ def visit_PARAMDECL(self, node): self.visit_VAR(node) def visit_UNARY(self, node): - uvisitor = UnaryOpTranslator(self.backend) + uvisitor = UnaryOpTranslator(self.backend, self) att = f"visit_{node.operator}" if hasattr(uvisitor, att): yield getattr(uvisitor, att)(node) @@ -140,18 +150,20 @@ def visit_UNARY(self, node): raise InvalidOperatorError(node.operator) def visit_BUILTIN(self, node): - yield node.operand - bvisitor = BuiltinTranslator(self.backend) + yield self.visit(node.operand) + bvisitor = BuiltinTranslator(self.backend, self) att = f"visit_{node.fname}" if hasattr(bvisitor, att): yield getattr(bvisitor, att)(node) + if node.discard_result: + self.ic_fparam(node.type_, optemps.new_t()) return raise InvalidBuiltinFunctionError(node.fname) def visit_BINARY(self, node): - yield node.left - yield node.right + yield self.visit(node.left) + yield self.visit(node.right) ins = {"PLUS": "add", "MINUS": "sub"}.get(node.operator, node.operator.lower()) ins_t: dict[str, Callable[[TYPE | symbols.BASICTYPE, Any, Any, Any], None]] = { @@ -178,18 +190,12 @@ def visit_BINARY(self, node): } ins_t[ins](node.left.type_, node.t, str(node.left.t), str(node.right.t)) - def visit_TYPECAST(self, node): - yield node.operand - assert node.operand.type_.is_basic - assert node.type_.is_basic - self.ic_cast(node.t, node.operand.type_, node.type_, node.operand.t) - def visit_FUNCDECL(self, node): # Delay emission of functions until the end of the main code gl.FUNCTIONS.append(node.entry) def visit_CALL(self, node: symbols.CALL): - yield node.args # arglist + yield self.visit(node.args) # arglist if node.entry.convention == CONVENTION.fastcall: if len(node.args) > 0: # At least 1 parameter self.ic_fparam(node.args[0].type_, optemps.new_t()) @@ -200,7 +206,7 @@ def visit_CALL(self, node: symbols.CALL): def visit_ARGLIST(self, node): for i in range(len(node) - 1, -1, -1): # visit in reverse order - yield node[i] + yield self.visit(node[i]) def visit_ARGUMENT(self, node): if not node.byref: @@ -212,7 +218,7 @@ def visit_ARGUMENT(self, node): else: # PARAMETER self.ic_pload(node.type_, node.t, str(node.value.offset)) else: - yield node.value + yield self.visit(node.value) self.ic_param(node.type_, node.t) return @@ -239,7 +245,9 @@ def visit_ARGUMENT(self, node): return # Must compute Address of @array(...) - if node.value.scope == SCOPE.global_ and self.O_LEVEL > 1: # Calculate offset if global variable + if ( + self.O_LEVEL > 1 and node.value.scope == SCOPE.global_ and node.value.offset is not None + ): # Calculate offset if global variable node.value = SymbolBINARY.make_node( "PLUS", symbols.UNARY("ADDRESS", node.value.entry, node.value.lineno, type_=self.TYPE(gl.PTR_TYPE)), @@ -251,13 +259,13 @@ def visit_ARGUMENT(self, node): node.value = SymbolARRAYACCESS.copy_from(node.value) node.value = symbols.UNARY("ADDRESS", node.value, node.lineno, type_=self.TYPE(gl.PTR_TYPE)) - yield node.value + yield self.visit(node.value) def visit_ARRAYLOAD(self, node): scope = node.entry.scope if node.offset is None: - yield node.args + yield self.visit(node.args) if scope == SCOPE.global_: self.ic_aload(node.type_, node.entry.t, node.entry.mangled) @@ -325,8 +333,8 @@ def visit_LETARRAY(self, node): scope = arr.scope if arr.offset is None: - yield node.children[1] # Right expression - yield arr + yield self.visit(node.children[1]) # Right expression + yield self.visit(arr) if scope == SCOPE.global_: self.ic_astore(arr.type_, arr.entry.mangled, node.children[1].t) @@ -338,14 +346,14 @@ def visit_LETARRAY(self, node): else: name = arr.entry.data_label if scope == SCOPE.global_: - yield node.children[1] # Right expression + yield self.visit(node.children[1]) # Right expression self.ic_store(arr.type_, "%s + %i" % (name, arr.offset), node.children[1].t) elif scope == SCOPE.local: t1 = optemps.new_t() t2 = optemps.new_t() self.ic_pload(gl.PTR_TYPE, t1, -(arr.entry.offset - self.TYPE(gl.PTR_TYPE).size)) self.ic_add(gl.PTR_TYPE, t2, t1, arr.offset) - yield node.children[1] # Right expression + yield self.visit(node.children[1]) # Right expression if arr.type_ == Type.string: self.ic_store(arr.type_, f"*{t2}", node.children[1].t) @@ -357,7 +365,7 @@ def visit_LETARRAY(self, node): def visit_LETSUBSTR(self, node): """LET X$(a TO b) = Y$""" # load Y$ - yield node.children[3] + self.visit(node.children[3]) if check.is_temporary_value(node.children[3]): self.ic_param(TYPE.string, node.children[3].t) @@ -367,10 +375,10 @@ def visit_LETSUBSTR(self, node): self.ic_param(TYPE.ubyte, 0) # Load a - yield node.children[1] + yield self.visit(node.children[1]) self.ic_param(gl.PTR_TYPE, node.children[1].t) # Load b - yield node.children[2] + yield self.visit(node.children[2]) self.ic_param(gl.PTR_TYPE, node.children[2].t) # Load x$ str_var = node.children[0] @@ -396,8 +404,8 @@ def visit_LETARRAYSUBSTR(self, node): if self.O_LEVEL > 1 and not node.children[0].entry.accessed: return - expr = node.children[3] # right expression - yield expr + expr = node.children[3] + yield self.visit(node.children[3]) # right expression if check.is_temporary_value(expr): self.ic_param(TYPE.string, expr.t) @@ -406,9 +414,9 @@ def visit_LETARRAYSUBSTR(self, node): self.ic_param(gl.PTR_TYPE, expr.t) self.ic_param(TYPE.ubyte, 0) - yield node.children[1] + yield self.visit(node.children[1]) self.ic_param(gl.PTR_TYPE, node.children[1].t) - yield node.children[2] + yield self.visit(node.children[2]) self.ic_param(gl.PTR_TYPE, node.children[2].t) node_ = node.children[0] @@ -417,7 +425,7 @@ def visit_LETARRAYSUBSTR(self, node): # Address of an array element. if node_.offset is None: - yield node_ + yield self.visit(node_) if scope == SCOPE.global_: self.ic_aload(gl.PTR_TYPE, node_.t, entry.mangled) elif scope == SCOPE.parameter: # TODO: These 2 are never used!?? @@ -437,18 +445,18 @@ def visit_LETARRAYSUBSTR(self, node): self.runtime_call(RuntimeLabel.LETSUBSTR, 0) def visit_ARRAYACCESS(self, node): - yield node.arglist + yield self.visit(node.args) def visit_STRSLICE(self, node): - yield node.string + yield self.visit(node.string) if node.string.token == "STRING" or node.string.token == "VAR" and node.string.scope == SCOPE.global_: self.ic_param(gl.PTR_TYPE, node.string.t) # Now emit the slicing indexes - yield node.lower + yield self.visit(node.lower) self.ic_param(node.lower.type_, node.lower.t) - yield node.upper + yield self.visit(node.upper) self.ic_param(node.upper.type_, node.upper.t) if node.string.token == "VAR" and node.string.mangled[0] == "_" or node.string.token == "STRING": @@ -459,7 +467,7 @@ def visit_STRSLICE(self, node): self.runtime_call(RuntimeLabel.STRSLICE, self.TYPE(gl.PTR_TYPE).size) def visit_FUNCCALL(self, node): - yield node.args + yield self.visit(node.args) if node.entry.convention == CONVENTION.fastcall: if len(node.args) > 0: # At least 1 @@ -500,7 +508,7 @@ def visit_READ(self, node): scope = arr.scope if arr.offset is None: - yield arr + yield self.visit(arr) if scope == SCOPE.global_: self.ic_astore(arr.type_, arr.entry.mangled, t) @@ -530,7 +538,7 @@ def visit_DO_LOOP(self, node): self.ic_label(loop_label) if node.children: - yield node.children[0] + yield self.visit(node.children[0]) self.ic_jump(loop_label) self.ic_label(end_loop) @@ -552,10 +560,10 @@ def visit_DO_WHILE(self, node): self.LOOPS.append(("DO", end_loop, continue_loop)) # Saves which labels to jump upon EXIT or CONTINUE if len(node.children) > 1: - yield node.children[1] + yield self.visit(node.children[1]) self.ic_label(continue_loop) - yield node.children[0] + yield self.visit(node.children[0]) self.ic_jnzero(node.children[0].type_, node.children[0].t, loop_label) self.ic_label(end_loop) self.LOOPS.pop() @@ -589,20 +597,20 @@ def visit_FOR(self, node): self.LOOPS.append(("FOR", end_loop, loop_continue)) # Saves which label to jump upon EXIT FOR and CONTINUE FOR - yield node.children[1] # Gets starting value (lower limit) + yield self.visit(node.children[1]) # Gets starting value (lower limit) self.emit_let_left_part(node) # Stores it in the iterator variable self.ic_jump(loop_label_start) # FOR body statements self.ic_label(loop_body) - yield node.children[4] + yield self.visit(node.children[4]) # Jump here to continue next iteration self.ic_label(loop_continue) # VAR = VAR + STEP - yield node.children[0] # Iterator Var - yield node.children[3] # Step + yield self.visit(node.children[0]) # Iterator Var + yield self.visit(node.children[3]) # Step t = optemps.new_t() self.ic_add(type_, t, node.children[0].t, node.children[3].t) self.emit_let_left_part(node, t) @@ -615,13 +623,13 @@ def visit_FOR(self, node): direct = True else: direct = False - yield node.children[3] # Step + yield self.visit(node.children[3]) # Step self.ic_jgezero(type_, node.children[3].t, loop_label_gt) if not direct or node.children[3].value < 0: # Here for negative steps # Compares if var < limit2 - yield node.children[0] # Value of var - yield node.children[2] # Value of limit2 + yield self.visit(node.children[0]) # Value of var + yield self.visit(node.children[2]) # Value of limit2 self.ic_lt(type_, node.t, node.children[0].t, node.children[2].t) self.ic_jzero(TYPE.ubyte, node.t, loop_body) @@ -631,8 +639,8 @@ def visit_FOR(self, node): if not direct or node.children[3].value >= 0: # Here for positive steps # Compares if var > limit2 - yield node.children[0] # Value of var - yield node.children[2] # Value of limit2 + yield self.visit(node.children[0]) # Value of var + yield self.visit(node.children[2]) # Value of limit2 self.ic_gt(type_, node.t, node.children[0].t, node.children[2].t) self.ic_jzero(TYPE.ubyte, node.t, loop_body) @@ -648,7 +656,7 @@ def visit_GOSUB(self, node): def visit_ON_GOTO(self, node): table_label = src.api.tmp_labels.tmp_label() self.ic_param(gl.PTR_TYPE, "#" + table_label) - yield node.children[0] + yield self.visit(node.children[0]) self.ic_fparam(node.children[0].type_, node.children[0].t) self.runtime_call(RuntimeLabel.ON_GOTO, 0) self.JUMP_TABLES.append(JumpTable(table_label, node.children[1:])) @@ -656,7 +664,7 @@ def visit_ON_GOTO(self, node): def visit_ON_GOSUB(self, node): table_label = src.api.tmp_labels.tmp_label() self.ic_param(gl.PTR_TYPE, "#" + table_label) - yield node.children[0] + yield self.visit(node.children[0]) self.ic_fparam(node.children[0].type_, node.children[0].t) self.runtime_call(RuntimeLabel.ON_GOSUB, 0) self.JUMP_TABLES.append(JumpTable(table_label, node.children[1:])) @@ -669,7 +677,7 @@ def visit_CHKBREAK(self, node): def visit_IF(self, node): assert 1 < len(node.children) < 4, "IF nodes: %i" % len(node.children) - yield node.children[0] + yield self.visit(node.children[0]) if_label_else = src.api.tmp_labels.tmp_label() if_label_endif = src.api.tmp_labels.tmp_label() @@ -678,18 +686,18 @@ def visit_IF(self, node): else: self.ic_jzero(node.children[0].type_, node.children[0].t, if_label_endif) - yield node.children[1] # THEN... + yield self.visit(node.children[1]) # THEN... if len(node.children) == 3: # Has else? self.ic_jump(if_label_endif) self.ic_label(if_label_else) - yield node.children[2] + yield self.visit(node.children[2]) self.ic_label(if_label_endif) def visit_RETURN(self, node): if len(node.children) == 2: # Something to return? - yield node.children[1] + yield self.visit(node.children[1]) self.ic_ret(node.children[1].type_, node.children[1].t, "%s__leave" % node.children[0].mangled) elif len(node.children) == 1: self.ic_return("%s__leave" % node.children[0].mangled) @@ -708,10 +716,10 @@ def visit_UNTIL_DO(self, node): self.LOOPS.append(("DO", end_loop, continue_loop)) # Saves which labels to jump upon EXIT or CONTINUE if len(node.children) > 1: - yield node.children[1] + yield self.visit(node.children[1]) self.ic_label(continue_loop) - yield node.children[0] # Condition + yield self.visit(node.children[0]) # Condition self.ic_jzero(node.children[0].type_, node.children[0].t, loop_label) self.ic_label(end_loop) self.LOOPS.pop() @@ -723,11 +731,11 @@ def visit_WHILE(self, node): self.LOOPS.append(("WHILE", end_loop, loop_label)) # Saves which labels to jump upon EXIT or CONTINUE self.ic_label(loop_label) - yield node.children[0] + yield self.visit(node.children[0]) self.ic_jzero(node.children[0].type_, node.children[0].t, end_loop) if len(node.children) > 1: - yield node.children[1] + yield self.visit(node.children[1]) self.ic_jump(loop_label) self.ic_label(end_loop) @@ -745,10 +753,10 @@ def visit_WHILE_DO(self, node): def visit_PLOT(self, node): self.norm_attr() TMP_HAS_ATTR = self.check_attr(node, 2) - yield TMP_HAS_ATTR - yield node.children[0] + yield self.visit(TMP_HAS_ATTR) + yield self.visit(node.children[0]) self.ic_param(node.children[0].type_, node.children[0].t) - yield node.children[1] + yield self.visit(node.children[1]) self.ic_fparam(node.children[1].type_, node.children[1].t) self.runtime_call(RuntimeLabel.PLOT, 0) self.HAS_ATTR = TMP_HAS_ATTR is not None @@ -756,10 +764,10 @@ def visit_PLOT(self, node): def visit_DRAW(self, node): self.norm_attr() TMP_HAS_ATTR = self.check_attr(node, 2) - yield TMP_HAS_ATTR - yield node.children[0] + yield self.visit(TMP_HAS_ATTR) + yield self.visit(node.children[0]) self.ic_param(node.children[0].type_, node.children[0].t) - yield node.children[1] + yield self.visit(node.children[1]) self.ic_fparam(node.children[1].type_, node.children[1].t) self.runtime_call(RuntimeLabel.DRAW, 0) self.HAS_ATTR = TMP_HAS_ATTR is not None @@ -767,12 +775,12 @@ def visit_DRAW(self, node): def visit_DRAW3(self, node): self.norm_attr() TMP_HAS_ATTR = self.check_attr(node, 3) - yield TMP_HAS_ATTR - yield node.children[0] + yield self.visit(TMP_HAS_ATTR) + yield self.visit(node.children[0]) self.ic_param(node.children[0].type_, node.children[0].t) - yield node.children[1] + yield self.visit(node.children[1]) self.ic_param(node.children[1].type_, node.children[1].t) - yield node.children[2] + yield self.visit(node.children[2]) self.ic_fparam(node.children[2].type_, node.children[2].t) self.runtime_call(RuntimeLabel.DRAW3, 0) self.HAS_ATTR = TMP_HAS_ATTR is not None @@ -780,12 +788,12 @@ def visit_DRAW3(self, node): def visit_CIRCLE(self, node): self.norm_attr() TMP_HAS_ATTR = self.check_attr(node, 3) - yield TMP_HAS_ATTR - yield node.children[0] + yield self.visit(TMP_HAS_ATTR) + yield self.visit(node.children[0]) self.ic_param(node.children[0].type_, node.children[0].t) - yield node.children[1] + yield self.visit(node.children[1]) self.ic_param(node.children[1].type_, node.children[1].t) - yield node.children[2] + yield self.visit(node.children[2]) self.ic_fparam(node.children[2].type_, node.children[2].t) self.runtime_call(RuntimeLabel.CIRCLE, 0) self.HAS_ATTR = TMP_HAS_ATTR is not None @@ -797,25 +805,18 @@ def visit_CIRCLE(self, node): # PRINT, LOAD, SAVE and I/O statements # ----------------------------------------------------------------------------------------------------- def visit_OUT(self, node): - yield node.children[0] - yield node.children[1] + yield self.visit(node.children[0]) + yield self.visit(node.children[1]) self.ic_out(node.children[0].t, node.children[1].t) def visit_PRINT(self, node): self.norm_attr() + skip_tokens = set(self.ATTR_TMP) | {"PRINT_TAB", "PRINT_AT", "PRINT_COMMA"} for i in node.children: - yield i + yield self.visit(i) # Print subcommands (AT, OVER, INK, etc... must be skipped here) - if ( - i.token - in ( - "PRINT_TAB", - "PRINT_AT", - "PRINT_COMMA", - ) - + self.ATTR_TMP - ): + if i.token in skip_tokens: continue self.ic_fparam(i.type_, i.t) @@ -837,14 +838,14 @@ def visit_PRINT(self, node): self.runtime_call(RuntimeLabel.PRINT_EOL, 0) def visit_PRINT_AT(self, node): - yield node.children[0] + yield self.visit(node.children[0]) self.ic_param(TYPE.ubyte, node.children[0].t) - yield node.children[1] + yield self.visit(node.children[1]) self.ic_fparam(TYPE.ubyte, node.children[1].t) self.runtime_call(RuntimeLabel.PRINT_AT, 0) # Procedure call. Discard return def visit_PRINT_TAB(self, node): - yield node.children[0] + yield self.visit(node.children[0]) self.ic_fparam(TYPE.ubyte, node.children[0].t) self.runtime_call(RuntimeLabel.PRINT_TAB, 0) @@ -852,22 +853,22 @@ def visit_PRINT_COMMA(self, node): self.runtime_call(RuntimeLabel.PRINT_COMMA, 0) def visit_LOAD(self, node): - yield node.children[0] + yield self.visit(node.children[0]) self.ic_param(TYPE.string, node.children[0].t) - yield node.children[1] + yield self.visit(node.children[1]) self.ic_param(gl.PTR_TYPE, node.children[1].t) - yield node.children[2] + yield self.visit(node.children[2]) self.ic_param(gl.PTR_TYPE, node.children[2].t) self.ic_param(TYPE.ubyte, int(node.token == "LOAD")) self.runtime_call(RuntimeLabel.LOAD_CODE, 0) def visit_SAVE(self, node): - yield (node.children[0]) + yield self.visit(node.children[0]) self.ic_param(TYPE.string, node.children[0].t) - yield (node.children[1]) + yield self.visit(node.children[1]) self.ic_param(gl.PTR_TYPE, node.children[1].t) - yield node.children[2] + yield self.visit(node.children[2]) self.ic_param(gl.PTR_TYPE, node.children[2].t) self.runtime_call(RuntimeLabel.SAVE_CODE, 0) @@ -875,7 +876,7 @@ def visit_VERIFY(self, node): return self.visit_LOAD(node) def visit_BORDER(self, node): - yield node.children[0] + yield self.visit(node.children[0]) self.ic_fparam(TYPE.ubyte, node.children[0].t) self.runtime_call(RuntimeLabel.BORDER, 0) @@ -886,14 +887,14 @@ def visit_BEEP(self, node): self.ic_fparam(TYPE.uinteger, DE) self.runtime_call(RuntimeLabel.BEEPER, 0) # Procedure call. Discard return else: - yield node.children[1] + yield self.visit(node.children[1]) self.ic_param(TYPE.float, node.children[1].t) - yield node.children[0] + yield self.visit(node.children[0]) self.ic_fparam(TYPE.float, node.children[0].t) self.runtime_call(RuntimeLabel.BEEP, 0) def visit_PAUSE(self, node): - yield node.children[0] + yield self.visit(node.children[0]) self.ic_fparam(node.children[0].type_, node.children[0].t) self.runtime_call(RuntimeLabel.PAUSE, 0) @@ -904,7 +905,7 @@ def visit_PAUSE(self, node): # ATTR sentences: INK, PAPER, BRIGHT, FLASH, INVERSE, OVER, ITALIC, BOLD # ----------------------------------------------------------------------- def visit_ATTR_sentence(self, node): - yield node.children[0] + yield self.visit(node.children[0]) self.ic_fparam(TYPE.ubyte, node.children[0].t) label = { @@ -1018,12 +1019,16 @@ def loop_cont_label(self, loop_type): raise InvalidLoopError(loop_type) @classmethod - def default_value(cls, type_: symbols.TYPE, expr) -> list[str]: # TODO: This function must be moved to api.xx + def default_value(cls, type_: symbols.TYPING, expr: symbols.EXPR) -> list[str]: + # TODO: This function must be moved to api.xx """Returns a list of bytes (as hexadecimal 2 char string)""" - assert isinstance(type_, symbols.TYPE) + assert isinstance(type_, symbols.TYPING) assert type_.is_basic assert check.is_static(expr) + if isinstance(type_, symbols.TYPEREF): + type_ = type_.final + if expr.token in ("CONSTEXPR", "CONST"): # a constant expression like @label + 1 if type_ in (cls.TYPE(TYPE.float), cls.TYPE(TYPE.string)): error(expr.lineno, f"Can't convert non-numeric value to {type_.name} at compile time") diff --git a/src/arch/z80/visitor/translator_inst_visitor.py b/src/arch/z80/visitor/translator_inst_visitor.py index b31d991f2..ea89f2541 100644 --- a/src/arch/z80/visitor/translator_inst_visitor.py +++ b/src/arch/z80/visitor/translator_inst_visitor.py @@ -1,14 +1,22 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api.constants import TYPE from src.api.debug import __DEBUG__ from src.arch.interface.quad import Quad from src.arch.z80.backend import Backend from src.arch.z80.backend.common import BOOL_t, F16_t, F_t, I8_t, I16_t, I32_t, STR_t, U8_t, U16_t, U32_t -from src.ast import NodeVisitor +from src.ast_ import NodeVisitor from src.symbols import sym class TranslatorInstVisitor(NodeVisitor): def __init__(self, backend: Backend): + super().__init__() self.backend = backend def emit(self, *args: str) -> None: @@ -18,8 +26,8 @@ def emit(self, *args: str) -> None: self.backend.MEMORY.append(quad) @staticmethod - def TSUFFIX(type_: TYPE | sym.TYPEREF | sym.BASICTYPE) -> str: - assert isinstance(type_, sym.TYPE) or TYPE.is_valid(type_) + def TSUFFIX(type_: TYPE | sym.TYPING) -> str: + assert isinstance(type_, sym.TYPING) or TYPE.is_valid(type_) _TSUFFIX = { TYPE.byte: I8_t, @@ -34,17 +42,16 @@ def TSUFFIX(type_: TYPE | sym.TYPEREF | sym.BASICTYPE) -> str: TYPE.boolean: BOOL_t, } - if isinstance(type_, sym.TYPEREF): + if isinstance(type_, sym.TYPEREF | sym.BASICTYPE): type_ = type_.final assert isinstance(type_, sym.BASICTYPE) + type_ = TYPE.to_type(type_.name) - if isinstance(type_, sym.BASICTYPE): - return _TSUFFIX[type_.type_] - + assert isinstance(type_, TYPE) return _TSUFFIX[type_] @classmethod - def _no_bool(cls, type_: TYPE | sym.TYPEREF | sym.BASICTYPE) -> str: + def _no_bool(cls, type_: TYPE | sym.TYPING | sym.BASICTYPE) -> str: """Returns the corresponding type suffix except for bool which maps to U8_t""" return cls.TSUFFIX(type_) if cls.TSUFFIX(type_) != BOOL_t else U8_t diff --git a/src/arch/z80/visitor/translator_visitor.py b/src/arch/z80/visitor/translator_visitor.py index b4d072ab8..e0c7cd5a2 100644 --- a/src/arch/z80/visitor/translator_visitor.py +++ b/src/arch/z80/visitor/translator_visitor.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from typing import NamedTuple import src.api.global_ as gl @@ -10,7 +17,7 @@ from src.arch.z80 import backend from src.arch.z80.backend.runtime import LABEL_REQUIRED_MODULES, RUNTIME_LABELS from src.arch.z80.backend.runtime import Labels as RuntimeLabel -from src.ast.tree import ChildrenList +from src.ast_.tree import ChildrenList from src.symbols import sym as symbols from src.symbols.symbol_ import Symbol from src.symbols.type_ import Type @@ -90,11 +97,17 @@ def dumpMemory(MEMORY): def visit_BLOCK(self, node): __DEBUG__("BLOCK", 2) for child in node.children: - yield child + yield self.visit(child) + + def visit_TYPECAST(self, node): + yield self.visit(node.operand) + assert node.operand.type_.is_basic + assert node.type_.is_basic + self.ic_cast(node.t, node.operand.type_, node.type_, node.operand.t) # Visits any temporal attribute def visit_ATTR_TMP(self, node): - yield node.children[0] + yield self.visit(node.children[0]) self.ic_fparam(node.children[0].type_, node.children[0].t) label = { @@ -115,8 +128,11 @@ def runtime_call(self, label: str, num: int): if label in LABEL_REQUIRED_MODULES: backend.REQUIRES.add(LABEL_REQUIRED_MODULES[label]) - # This function must be called before emit_strings def emit_data_blocks(self): + """Emits the DATA instruction blocks used by READ. + This function must be called before emit_strings() because it will emit access to string variables, + marking them as required. + """ if not gl.DATA_IS_USED or not gl.DATAS: return # nothing to do @@ -241,10 +257,12 @@ def traverse_const(node): raise InvalidCONSTexpr(node) @staticmethod - def check_attr(node, n): + def check_attr(node: Symbol, n: int) -> Symbol | None: """Check if ATTR has to be normalized after this instruction has been translated to intermediate code. """ if len(node.children) > n: return node.children[n] + + return None diff --git a/src/arch/z80/visitor/unary_op_translator.py b/src/arch/z80/visitor/unary_op_translator.py index 3984eb289..f81499f76 100644 --- a/src/arch/z80/visitor/unary_op_translator.py +++ b/src/arch/z80/visitor/unary_op_translator.py @@ -1,26 +1,41 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api.constants import SCOPE +from src.arch.z80.backend import Backend from src.arch.z80.visitor.translator_visitor import TranslatorVisitor class UnaryOpTranslator(TranslatorVisitor): """UNARY sub-visitor. E.g. -a or bNot pi""" + def __init__(self, backend: Backend, parent_visitor: TranslatorVisitor): + super().__init__(backend) + self.parent_visitor = parent_visitor # Will use this visitor visit() function + + def visit(self, node): + return self.parent_visitor.visit(node) + def visit_MINUS(self, node): - yield node.operand + yield self.visit(node.operand) self.ic_neg(node.type_, node.t, node.operand.t) def visit_NOT(self, node): - yield node.operand + yield self.visit(node.operand) self.ic_not(node.operand.type_, node.t, node.operand.t) def visit_BNOT(self, node): - yield node.operand + yield self.visit(node.operand) self.ic_bnot(node.operand.type_, node.t, node.operand.t) def visit_ADDRESS(self, node): scope = node.operand.scope if node.operand.token == "ARRAYACCESS": - yield node.operand + yield self.visit(node.operand) # Address of an array element. if scope == SCOPE.global_: self.ic_aaddr(node.t, node.operand.entry.mangled) diff --git a/src/arch/z80/visitor/var_translator.py b/src/arch/z80/visitor/var_translator.py index 276ecb190..b743819d5 100644 --- a/src/arch/z80/visitor/var_translator.py +++ b/src/arch/z80/visitor/var_translator.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import src.api from src.api import global_ as gl from src.api.config import OPTIONS @@ -48,7 +55,7 @@ def visit_ARRAYDECL(self, node): ubound_label = entry.mangled + ".__UBOUND__" bound_ptrs = ["0", "0"] # NULL by default - if not entry.is_zero_based and entry.is_dynamically_accessed: + if not entry.is_zero_based and (entry.is_dynamically_accessed or entry.lbound_used): bound_ptrs[0] = lbound_label if entry.ubound_used or OPTIONS.array_check: diff --git a/src/arch/zx48k/__init__.py b/src/arch/zx48k/__init__.py index 58de38638..facc1de1e 100644 --- a/src/arch/zx48k/__init__.py +++ b/src/arch/zx48k/__init__.py @@ -1,4 +1,9 @@ -# vim:ts=4:et:sw=4: +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import src.api.global_ from src.api.constants import TYPE diff --git a/src/arch/zx48k/backend/__init__.py b/src/arch/zx48k/backend/__init__.py index c631e4174..7c0a23e98 100644 --- a/src/arch/zx48k/backend/__init__.py +++ b/src/arch/zx48k/backend/__init__.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api.config import OPTIONS from src.arch.z80.backend import ( HI16, diff --git a/src/arch/zx48k/beep.py b/src/arch/zx48k/beep.py index 9bc5b557b..63ecd38c2 100755 --- a/src/arch/zx48k/beep.py +++ b/src/arch/zx48k/beep.py @@ -1,5 +1,11 @@ #!/usr/bin/env python3 -# vim:ts=4:et:sw=4: + +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- __doc___ = """This library converts duration,pitch for a beep from floating point to HL,DE Integers. diff --git a/src/arch/zx81sd/README.md b/src/arch/zx81sd/README.md new file mode 100644 index 000000000..1d2858b4c --- /dev/null +++ b/src/arch/zx81sd/README.md @@ -0,0 +1,114 @@ +# `zx81sd` architecture: ZX81 + SD81 Booster + +`zx81sd` is an architecture for this compiler targeting a **real ZX81 +with the [SD81 Booster](https://www.sd81.eu/) card** (an interface that +adds a Spectrum-like screen, AY/beeper sound, a paged memory mapper and +SD card access). + +All code specific to this architecture lives exclusively under: + +``` +src/arch/zx81sd/ (compiler backend, this documentation, + and the packaging tools under tools/) +src/lib/arch/zx81sd/ (ASM runtime + BASIC stdlib) +``` + +## The port's golden rule + +This compiler is shared by every architecture (zx48k, zx128k, +zxnext...). **The zx81sd port never modifies the shared frontend or +stdlib/runtime.** The `#include`/`#require` resolution mechanism looks +first in `src/lib/arch/zx81sd/`, and if it doesn't find the file it +automatically falls back to `src/lib/arch/zx48k/` (the shared one). +That's why many zx81sd overrides are full copies of the zx48k version +with just a few lines changed: the whole file has to be copied, not +patched — a partial override simply doesn't exist as a concept here. + +Before touching anything outside `zx81sd/`, stop: there's probably a +way to solve it with an override. + +## Port status + +Functionally complete since 2026-07-02: FP (our own RST $28), graphics +(PLOT/DRAW/arcs/CIRCLE), sound (BEEP and PLAY over the AY ZonX and the +beeper), keyboard (INKEY$/INPUT over the ZX81's physical keyboard), +joystick, the complete MCU library (files, RTC/BAT, voice, memory +mapper...) and LOAD/SAVE/VERIFY...CODE against SD. + +Pending / not audited, remaining screen utilities in the shared stdlib: + +- `winscroll.bas`: **confirmed working on real hardware** — no ROM + dependency, no override needed (like `scroll.bas` before its fix, or + `4inarow.bas`). No source changes needed either + (`examples/winscroll.bas` as-is). +- `putchars.bas`: **confirmed working on real hardware** — no ROM + dependency, no override needed + (`examples/sd81/putcharstile.bas`). +- `puttile.bas`: **confirmed working on real hardware** — needed a + zx81sd override for hardcoded screen/attribute base constants, same + bug class as `print42.bas`/`print64.bas` (fixed the same way, + self-modifying code patched at runtime). +- `screen.bas`: **does depend on the ROM** (`$2538`/`$5C65`/`$19E8`, + fixed Spectrum routines and sysvars to read back a screen character) + — will need a real override, not just an audit. +- `print42.bas`/`print64.bas`: **ported and confirmed working on real + hardware** — fixed sysvars → zx81sd equivalents, and the screen/ + attribute base constants are patched at runtime (self-modifying + code) instead of being fixed. + +See [doc/BASIC_CHANGES.md](doc/BASIC_CHANGES.md) for the general +pattern behind this kind of fix. The `maskedsprites.bas`/MSFS port +(masked sprites over the memory mapper) is also in progress, still not +fully working. + +## Documentation + +- **[doc/PORTING_GUIDE.md](doc/PORTING_GUIDE.md)** — step-by-step + checklist for porting a Spectrum program to zx81sd. Start here. +- **[doc/USAGE.md](doc/USAGE.md)** — how to compile a program, package + it for the ZX81 and load it from the SD card. +- **[doc/PRECAUTIONS.md](doc/PRECAUTIONS.md)** — what to keep in mind + when writing or porting software for this architecture (memory map, + sysvars, keyboard, things that do NOT exist here even though they do + on a Spectrum). +- **[doc/SYSVARS.md](doc/SYSVARS.md)** — full Spectrum ↔ zx81sd sysvar + and I/O port (ULA, screen address) equivalence table. +- **[doc/BASIC_CHANGES.md](doc/BASIC_CHANGES.md)** — what BASIC source + changes were needed to port each official example in `examples/` + (with the why for each one). Mandatory starting point before porting + a new example: it's almost always one of the patterns already + cataloged there. +- **[doc/MAP.md](doc/MAP.md)** — detailed technical log of every bug + found and fixed during the port (ASM runtime, not BASIC sources), + with the investigation trace for each one. The document to check + when something fails at runtime in a way that resembles an + already-solved bug. + +## Examples + +The example programs already adapted/tested for this architecture are +in [`examples/sd81/`](../../../examples/sd81/) (alongside the rest of +the compiler's `examples/`). The detail of what had to be changed in +each one, and why, is in +[doc/BASIC_CHANGES.md](doc/BASIC_CHANGES.md). + +## Packaging tools + +[`tools/split_sd81.py`](tools/split_sd81.py) splits the flat binary +produced by the compiler into 8KB pages and generates the `.p` loader +for the ZX81; [`tools/zx81_p_loader.py`](tools/zx81_p_loader.py) is the +BASIC tokenizer it uses to generate it; [`tools/boot1.asm`](tools/boot1.asm)/ +[`tools/boot1.bin`](tools/boot1.bin) are the second-stage loader (stage +1), fixed for every program. See [doc/USAGE.md](doc/USAGE.md) for the +full compile → package → load flow. The wider set of debugging/ +diagnostic sources used during the port's development (not tools, just +one-off tests) lives in a private companion repository, not this one. + +## Related repositories + +- **[SD81 Booster](https://codeberg.org/Retrostuff/SD81-Booster)** — + the hardware/firmware for the interface this port targets. +- **[EightyOne Cross-platform](https://codeberg.org/wilco2009/EightyOne-CrossPlatform)** — + the emulator used during development to test without real hardware. +- **[CPM_SD81](https://codeberg.org/wilco2009/CPM_SD81)** — CP/M on the + SD81 Booster. diff --git a/src/arch/zx81sd/__init__.py b/src/arch/zx81sd/__init__.py new file mode 100644 index 000000000..f4ec43c0c --- /dev/null +++ b/src/arch/zx81sd/__init__.py @@ -0,0 +1,34 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# ZX81 + SD81 Booster architecture for Boriel ZX BASIC compiler +# Inherits from zx48k, overriding the backend for SD81 hardware. +# -------------------------------------------------------------------- + +import src.api.global_ +from src.api.constants import TYPE +from src.arch.z80 import ( + FunctionTranslator, + Translator, + VarTranslator, + beep, + optimizer, # noqa +) +from src.arch.zx81sd import backend # noqa + +__all__ = ( + "FunctionTranslator", + "Translator", + "VarTranslator", + "beep", +) + +# ----------------------------------------- +# Arch initialization setup (same as zx48k) +# ----------------------------------------- +src.api.global_.PARAM_ALIGN = 2 +src.api.global_.BOUND_TYPE = TYPE.uinteger +src.api.global_.SIZE_TYPE = TYPE.ubyte +src.api.global_.PTR_TYPE = TYPE.uinteger +src.api.global_.STR_INDEX_TYPE = TYPE.uinteger +src.api.global_.MIN_STRSLICE_IDX = 0 +src.api.global_.MAX_STRSLICE_IDX = 65534 diff --git a/src/arch/zx81sd/backend/__init__.py b/src/arch/zx81sd/backend/__init__.py new file mode 100644 index 000000000..792f01277 --- /dev/null +++ b/src/arch/zx81sd/backend/__init__.py @@ -0,0 +1,29 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# ZX81 + SD81 Booster backend for Boriel ZX BASIC compiler +# -------------------------------------------------------------------- + +from src.arch.z80.backend import ( + HI16, + INITS, + LO16, + MEMINITS, + REQUIRES, + TMP_COUNTER, + TMP_STORAGES, + Float, +) + +from .main import Backend + +__all__ = [ + "HI16", + "INITS", + "LO16", + "MEMINITS", + "REQUIRES", + "TMP_COUNTER", + "TMP_STORAGES", + "Backend", + "Float", +] diff --git a/src/arch/zx81sd/backend/generic.py b/src/arch/zx81sd/backend/generic.py new file mode 100644 index 000000000..f9492c660 --- /dev/null +++ b/src/arch/zx81sd/backend/generic.py @@ -0,0 +1,29 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# ZX81 + SD81 Booster — END opcode handler +# -------------------------------------------------------------------- + +from src.arch.interface.quad import Quad +from src.arch.z80.backend import Bits16, common + + +def _end(ins: Quad): + """End-of-program sequence for ZX81 + SD81 Booster. + + There's no ROM or BASIC to return to: it stops the CPU safely. + If END appears more than once in the program (early exits), later + occurrences generate a JP to the first emitted END block. + """ + output = Bits16.get_oper(ins[1]) + output.append("ld b, h") + output.append("ld c, l") + + if common.FLAG_end_emitted: + return output + [f"jp {common.END_LABEL}"] + + common.FLAG_end_emitted = True + + output.append(f"{common.END_LABEL}:") + output.append("di") + output.append("halt") + return output diff --git a/src/arch/zx81sd/backend/main.py b/src/arch/zx81sd/backend/main.py new file mode 100644 index 000000000..70c32131c --- /dev/null +++ b/src/arch/zx81sd/backend/main.py @@ -0,0 +1,213 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# ZX81 + SD81 Booster backend — program prologue / epilogue +# -------------------------------------------------------------------- + +from src.api.config import OPTIONS +from src.api.options import Action +from src.arch.z80.backend import Backend as Z80Backend +from src.arch.z80.backend import ICInfo, common +from src.arch.z80.backend.icinstruction import ICInstruction +from src.arch.z80.backend.runtime import NAMESPACE +from src.arch.z80.peephole import engine + +from .generic import _end + +# --------------------------------------------------------------------------- +# ZX81 + SD81 Booster architecture constants +# --------------------------------------------------------------------------- + +# Memory map (flat, ORG $0000): +# $0000-$00FF RST vector table, emitted directly below (emit_prologue) +# + padding up to $0100 +# $0100-$0FFF stage 2 bootstrap (prologue) + system routines +# $1000-$7FFF ZX BASIC runtime + user code (28 KB) +# $8000-$80FF runtime sysvars +# $8100-$BFFF heap + user data (~15.75 KB) +# $C000-$D7FF Spectrum screen bitmap (block 6, dedicated page) +# $D800-$DAFF screen attributes +# $E000-$FFFF block 7 — data banking (maps, sprites...) + +_ORG = 0x0000 # the binary starts at $0000 (RST vectors pad up to $0100) +_STAGE2_ENTRY = 0x0100 # stage 2 bootstrap's entry point + +_HEAP_ADDR = 0x8100 # heap in the data area ($8100-$BFFF) +_HEAP_SIZE = 0x3EFF # ~15.75 KB + +# SD81 pages assigned to each block (loaded by BASIC before the jump) +# Page 8 -> block 0 ($0000-$1FFF) <- stage 1 only maps this one +# Page 9 -> block 1 ($2000-$3FFF) ┐ +# Page 10 -> block 2 ($4000-$5FFF) │ stage 2 (here) maps these +# Page 11 -> block 3 ($6000-$7FFF) │ +# Page 12 -> block 4 ($8000-$9FFF) │ (data, not executable without MC45) +# Page 13 -> block 5 ($A000-$BFFF) ┘ + +_PAGE_MAP = [ + (1, 9), # block 1 -> page 9 + (2, 10), # block 2 -> page 10 + (3, 11), # block 3 -> page 11 + (4, 12), # block 4 -> page 12 + (5, 13), # block 5 -> page 13 + (7, 15), # block 7 -> page 15 (data banking: large static tables via explicit ORG) +] + +_SD81_PAGE_PORT = 0xE7 # memory mapper port (full mode: OUT (C), A) +_STACK_TOP = 0x7FFF # stack at the top of the executable area + + +def _map_block(block: int, page: int) -> list[str]: + """Emits OUT (C), A to map a page to a block (full mode, 64 pages).""" + return [ + f"ld b, {page}", + f"ld a, {block}", + f"ld c, {_SD81_PAGE_PORT:#04x}", + "out (c), a", + ] + + +class Backend(Z80Backend): + def init(self): + super().init() + + OPTIONS(Action.ADD_IF_NOT_DEFINED, name="org", type=int, default=_ORG) + + # The generic Z80 backend (super().init(), right above) already + # registers "heap_size" (4768) and "heap_address" (None) with + # ADD_IF_NOT_DEFINED, so an ADD_IF_NOT_DEFINED here would be a + # no-op and the heap would end up reserved inline (DEFS) with + # 4768 bytes inside the executable area. Assigned directly so + # the heap lives in the data area $8100-$BFFF; the CLI's + # --heap-address/-H flags are applied afterward and can still + # override these values. + OPTIONS.heap_size = _HEAP_SIZE + OPTIONS.heap_address = _HEAP_ADDR + + OPTIONS(Action.ADD_IF_NOT_DEFINED, name="heap_start_label", type=str, default=f"{NAMESPACE}.ZXBASIC_MEM_HEAP") + OPTIONS(Action.ADD_IF_NOT_DEFINED, name="heap_size_label", type=str, default=f"{NAMESPACE}.ZXBASIC_HEAP_SIZE") + OPTIONS(Action.ADD_IF_NOT_DEFINED, name="headerless", type=bool, default=False, ignore_none=True) + + self._QUAD_TABLE.update( + { + ICInstruction.END: ICInfo(1, _end), + } + ) + + engine.main() + + @staticmethod + def emit_prologue() -> list[str]: + """ + Program prologue for ZX81 + SD81 Booster. + + Structure of the generated binary (ORG $0000): + $0000-$00FF RST vector table (emitted directly below) + $0100 START_LABEL (stage 2 bootstrap's entry point) + - mapping blocks 1-5 to their final pages + - SP = $7FFF + - CALL <#init routines> (SD81_INIT_SYSVARS, etc.) + - JP __MAIN_LABEL__ + + The (external) stage 1 bootstrap, in the BASIC loader at $6000, + has already: + - Set HFILE=$C000 and activated Spectrum mode (POKE 2045, 172) + - Disabled memory-mapped IO (POKE 2056) + - Disabled interrupts (DI) + - Mapped block 0 -> page 8 (JP $0100 now runs on clean RAM) + """ + # -- Heap definitions ------------------------------------------ + heap_init = [f"{common.DATA_LABEL}:"] + + if common.REQUIRES.intersection(common.MEMINITS) or f"{NAMESPACE}.__MEM_INIT" in common.INITS: + heap_init.append("; Defines HEAP SIZE\n" + OPTIONS.heap_size_label + " EQU " + str(OPTIONS.heap_size)) + if OPTIONS.heap_address is None: + heap_init.append(OPTIONS.heap_start_label + ":") + heap_init.append(f"DEFS {OPTIONS.heap_size}") + else: + heap_init.append("; Defines HEAP ADDRESS\n" + OPTIONS.heap_start_label + f" EQU {OPTIONS.heap_address}") + + heap_init.append( + "; Defines USER DATA Length in bytes\n" + + f"{NAMESPACE}.ZXBASIC_USER_DATA_LEN" + + f" EQU {common.DATA_END_LABEL} - {common.DATA_LABEL}" + ) + heap_init.append(f"{NAMESPACE}.__LABEL__.ZXBASIC_USER_DATA_LEN" + f" EQU {NAMESPACE}.ZXBASIC_USER_DATA_LEN") + heap_init.append(f"{NAMESPACE}.__LABEL__.ZXBASIC_USER_DATA EQU {common.DATA_LABEL}") + + # -- RST vector table ($0000-$00FF) --------------------------------- + # Must be the very first thing in the binary. Each RST takes 8 bytes. + # An absolute org is used for every entry: the assembler fills the + # gaps with zeros, which is correct for a non-executable area. + output = ["org $0000"] + output.append("jp $0100") # $0000: reset / RST 0 -> stage 2 + output.append("org $0008") + output.append("di") # $0008: RST $08 (Spectrum error handler) + output.append("halt") + output.append("org $0010") + output.append("di") # $0010-$0037: unused RSTs + output.append("halt") + output.append("org $0018") + output.append("di") + output.append("halt") + output.append("org $0020") + output.append("di") + output.append("halt") + output.append("org $0028") + output.append("jp .core.FP_CALC_ENTRY") # $0028: RST $28 -> our own FP calculator + output.append("org $0030") + output.append("di") + output.append("halt") + output.append("org $0038") + # $0038: RST $38 (IM1 interrupt). Interrupts stay disabled for the + # whole program, so this should never really be reached; if + # something did reactivate them, behave like a real Z80 HALT + # instead of hanging forever (di+halt): wait for the next VSYNC + # pulse (port $AF, bits 6-1 = pulse counter, reset on read) and + # return, resuming right after the RST $38 that got us here. + # AF must be preserved: ported code that executes EI (Spectrum + # habit) makes stray ZX81 INTs land here mid-computation, and + # clobbering A/F corrupted arithmetic at random (see MAP.md, + # oilpanic sprite(13,2) bug). + output.append("push af") + output.append(".core.__RST38_WAIT:") + output.append("in a, ($AF)") + output.append("and $7E") + output.append("jr z, .core.__RST38_WAIT") + output.append("pop af") + output.append("ret") + output.append("org $0066") + output.append("retn") # $0066: NMI disabled, but the vector must exist + + # -- Stage 2 bootstrap at $0100 ------------------------------------ + output.append(f"org {_STAGE2_ENTRY}") + output.append(f"{common.START_LABEL}:") + + if OPTIONS.headerless: + output.extend(heap_init) + return output + + # Mapping blocks 1-5 to their final pages. + # Block 0 was already mapped to page 8 by the external stage 1. + for block, page in _PAGE_MAP: + output.extend(_map_block(block, page)) + + # Stack at the top of the executable area + output.append(f"ld sp, {_STACK_TOP:#06x}") + + # Calls to initialization routines registered with #init + # (SD81_INIT_SYSVARS and any other one from the included runtime) + output.extend(f"call {label}" for label in sorted(common.INITS)) + + # Jump to the user's program + output.append(f"jp {common.MAIN_LABEL}") + + output.extend(heap_init) + return output + + @staticmethod + def emit_epilogue() -> list[str]: + output = list(common.AT_END) + if OPTIONS.autorun: + output.append(f"END {common.START_LABEL}") + else: + output.append("END") + return output diff --git a/src/arch/zx81sd/doc/BASIC_CHANGES.md b/src/arch/zx81sd/doc/BASIC_CHANGES.md new file mode 100644 index 000000000..255cb592c --- /dev/null +++ b/src/arch/zx81sd/doc/BASIC_CHANGES.md @@ -0,0 +1,481 @@ +# BASIC source changes needed to port official examples to zx81sd + +This file records, for every official `examples/` sample tried on +zx81sd, whether the BASIC source needed touching (never the original: +always an adapted copy in `examples/sd81/`) or just different compile +flags. + +Ground rule: the official zxbasic source **is never modified**. When a +program depends on a Spectrum-specific sysvar or absolute address, an +adapted copy is made next to the original. + +--- + +## 1. `examples/english/comecoquitos.bas` — NO source changes + +Compiles as-is. Only needs command-line flags: + +``` +python -m src.zxbc.zxbc comecoquitos.bas --arch zx81sd --string-base 1 --array-base 1 -o comecocos.bin +``` + +**Why**: the source uses 1-based string indexing (classic Sinclair +BASIC style, `l$(f)`, slicing). With zxbasic's default 0-based indexing, +the collision comparisons end up shifted by one position — not a bug in +the port, it happens exactly the same on zx48k without those flags. + +--- + +## 2. `examples/english/snake_en.bas` → `examples/sd81/snake_sd81.bas` + +One single-line change: + +```diff +-73 POKE UINTEGER 23675, @udg(0, 0): REM Sets UDG variable to first element ++73 POKE UINTEGER $8002, @udg(0, 0): REM zx81sd's UDG sysvar (was 23675 on Spectrum) +``` + +**Why**: `23675` ($5C7B) is the absolute address of the `UDG` sysvar in +the **Spectrum**'s memory map. On zx81sd the same sysvar lives at +`$8002` (see `SYSVAR_BASE+2` in `src/lib/arch/zx81sd/runtime/sysvars.asm`). +Without the change, the POKE lands in free RAM and the UDGs (the +snake's head and fruit) never activate — the game would run but draw +blank spaces instead of the graphics. + +**General pattern**: any example that does `POKE`/`PEEK` on a Spectrum +sysvar address instead of using the higher-level function (here it +would have been enough not to use raw UDG) needs this kind of address +translation. See also flights.bas below. + +--- + +## 3. `examples/flights.bas` → `examples/sd81/flights_sd81.bas` + +Three content changes (plus cosmetic trailing-whitespace cleanup with +no functional effect, not listed): + +### 3.1 Remove a Spectrum sysvar POKE with no equivalent + +```diff +-1 POKE 23658,8: BORDER 1: PAPER 1: INK 7: CLS ++1 BORDER 1: PAPER 1: INK 7: CLS +``` + +**Why**: `23658` ($5C6A) is `REPDEL` (key repeat delay) on the +Spectrum — that sysvar doesn't exist on zx81sd, and the POKE would +write to an arbitrary RAM address. Simply removed: the effect +(adjusting keyboard auto-repeat) has no equivalent and isn't needed by +the game. + +### 3.2 Translate the COORDS sysvar to its real zx81sd address + +```diff +-2295 IF gc<>0 THEN PLOT OVER 1;x1,168+16-y1: DRAW OVER 1;x2-PEEK 23677,168+16-y2-PEEK 23678: END IF ++2295 IF gc<>0 THEN PLOT OVER 1;x1,168+16-y1: DRAW OVER 1;x2-PEEK $8004,168+16-y2-PEEK $8005: END IF +``` +(and three more identical occurrences on lines 2370, 2378 and 2445) + +**Why**: `23677`/`23678` ($5C7D/$5C7E) are the Spectrum's `COORDS` +sysvar (last `PLOT` coordinate). On zx81sd it lives at `$8004`/`$8005` +(`COORDS EQU SYSVAR_BASE+$04` in `sysvars.asm`). Without the +translation, the `PEEK`s read the compiled program's own RAM instead +of the actual coordinate, and the horizon-line `DRAW` comes out with a +random offset depending on that RAM's contents — this was the first +symptom reported ("the horizon line draws wrong"). + +### 3.3 Key comparisons to lowercase + +```diff +-3010 IF k$="S" THEN LET pow=pow-1: END IF +-3020 IF k$="F" THEN LET pow=pow+1: END IF +-3030 IF k$="Q" THEN LET pt=pt+1: END IF +-3040 IF k$="A" THEN LET pt=pt-1: END IF +-3050 IF k$="O" AND rl>-30 THEN LET rl=rl-1: END IF +-3060 IF k$="P" AND rl<30 THEN LET rl=rl+1: END IF ++3010 IF k$="s" THEN LET pow=pow-1: END IF ++3020 IF k$="f" THEN LET pow=pow+1: END IF ++3030 IF k$="q" THEN LET pt=pt+1: END IF ++3040 IF k$="a" THEN LET pt=pt-1: END IF ++3050 IF k$="o" AND rl>-30 THEN LET rl=rl-1: END IF ++3060 IF k$="p" AND rl<30 THEN LET rl=rl+1: END IF +``` +(and the "Y"/"N" comparisons on lines 6150/6160) + +**Why (this change matches the old keyboard scheme — see the "no +longer needed" note below)**: at the time `flights.bas` was ported, +`zx81sd/runtime/io/keyboard/keyscan.asm` (a hand-written rescan of the +**ZX81's own physical keyboard**, since the SD81 Booster has no +Spectrum keyboard) only ever returned lowercase, regardless of +whether `SHIFT` was pressed — `SHIFT+letter` didn't produce the +uppercase of that letter, but one of the ZX81's classic symbols (colon, +quotes, `+`, `-`...). With that scheme there was no physical +combination that reproduced Spectrum-style "CAPS SHIFT+S", and the only +possible adaptation was comparing against the unshifted lowercase. +Same reason, already solved a different way in `comecoquitos.bas` +(there the source already compared in lowercase, "o","p","q","a","y","n" +— its author just happened to type it that way, nothing we had to +touch) and in `snake_sd81.bas` (compares "O"/"o" with `OR`, covering +both cases without needing any change). + +**No longer needed (2026-07-04)**: `keyscan.asm` was redesigned so a +direct key press gives lowercase and `SHIFT+letter` gives the +UPPERCASE of that letter (plus `SHIFT+"2"` as a persistent CAPS LOCK, +and the ZX81's classic symbols reachable with `"."+key` from +`INPUT()`). With the current scheme, `INKEY$="S"` would already work +naturally by pressing `SHIFT+S`, exactly like on a Spectrum — the +case change in `flights_sd81.bas` documented above was necessary at the +time but wouldn't be needed if ported today from scratch. Kept here as +a record instead of reverting the already-tested copy. Full detail of +the redesign in [MAP.md](MAP.md), section "New keyboard scheme". + +--- + +## 4. `examples/english/4inarow.bas` — NO source changes + +Compiles with default flags: + +``` +python -m src.zxbc.zxbc 4inarow.bas --arch zx81sd -o row4.bin +``` + +Its arrays use indices 1..8, which fit in the default 0-based indexing +without any slicing collision (it doesn't do string slicing), and its +key comparisons already use lowercase ("y"/"n" in the prompts). It +exercises the arc code (`DRAW 8,0,PI`) and `CIRCLE` without needing any +patch. + +--- + +## 5. `examples/scroll.bas` — NO source changes + +Compiles with default flags: + +``` +python -m src.zxbc.zxbc scroll.bas --arch zx81sd -o scroll.bin +``` + +The problem wasn't in the example but in the library: `scroll.bas` had +no zx81sd version — the zx48k one was used as-is, and its 8 subs +(`ScrollRight/Left/Up/Down` + their `*Aligned` variants) call `$22AC` +(the Spectrum ROM's *PIXEL-ADD* routine, which doesn't exist on +zx81sd). `src/lib/arch/zx81sd/stdlib/scroll.bas` was created, identical +except those 8 calls are replaced with `call PIXEL_ADDR` (our own +implementation, same register contract as the ROM — see +[MAP.md](MAP.md)). The example itself needed no change, same as +4inarow.bas. + +--- + +## 5b. `examples/winscroll.bas` — NO source changes, no library override needed either + +Compiles with default flags: + +``` +python -m src.zxbc.zxbc winscroll.bas --arch zx81sd -o winscroll.bin +``` + +Unlike `scroll.bas`, `winscroll.bas` (character-cell window scroll: +`WinScrollRight/Left/Up/Down(row, col, width, height)`) has **no ROM +dependency at all** — it computes everything from `SCREEN_ADDR`/ +`SCREEN_ATTR_ADDR` (read dynamically, not hardcoded) and only pulls in +`sysvars.asm` via `#require` (a link-time dependency tag, not a +textual `#include` — it doesn't risk the `INCBIN`-inline bug described +in [PRECAUTIONS.md](PRECAUTIONS.md) section 8). No zx81sd override was +needed; the shared zx48k library is used as-is via the normal fallback +mechanism. Verified by simulation (screen and attribute area fully +touched, clean `HALT` at `__END_PROGRAM`, no illegal writes). +**Confirmed working on real hardware.** + +--- + +## 5c. `putchars.bas`/`puttile.bas` — one clean, one needed a zx81sd override + +New example `examples/sd81/putcharstile.bas` (no official example to +adapt) exercises both libraries: + +- **`putchars.bas`** (`putChars`/`getChars`/`paint`/`paintData`/ + `getPaintData`/`putCharsOverMode`): audited, **no ROM dependency at + all** — every address is computed from `SCREEN_ADDR`/ + `SCREEN_ATTR_ADDR` read dynamically, and the file's own + `#require "sysvars.asm"` is the safe link-time tag (see section 5b + above), not a textual include. No zx81sd override needed; used as-is. +- **`puttile.bas`** (`putTile`, places a 16×16 px tile): **did need an + override**. Unlike `putchars.bas`, it hardcodes the screen/attribute + base as **immediate constants** — `add a,64` (screen, the Spectrum + ROM's `$4000` high byte) and `add a,88` (attributes, `$5800`'s high + byte) — instead of reading `SCREEN_ADDR`/`SCREEN_ATTR_ADDR`. Same bug + class as `print42.bas`/`print64.bas`. Fixed the same way: + `src/lib/arch/zx81sd/stdlib/puttile.bas` patches both constants once + on entering `putTile()` (self-modifying code on the `ADD A,n` + instructions themselves), reading the real high byte of + `SCREEN_ADDR`/`SCREEN_ATTR_ADDR` at that point. Everything else + (the interrupt save/restore dance, the stack-based pixel pushing) is + architecture-agnostic and copied unchanged — the `ld a,i` / `jp po` + idiom naturally never re-enables interrupts on zx81sd, since IFF2 is + always reset here (the runtime never does `EI`). + +**Debugging note**: the first simulation run of this example looked +broken (`paint()` seemingly painted only 1 of 3 rows, `putTile`'s area +looked empty) — turned out to be a false alarm caused by the example's +own final `PRINT AT` landing on the screen's last row (23), which +combined with `input()`'s cursor handling to trigger a normal scroll +that shifted the drawing up before the check ran. Moving the final +prompt to row 20 fixed it; `paint()`/`putTile()` were correct all +along. Verified with breakpoints right after each function returns +(not just at the end) to catch this class of false negative. +**Confirmed working on real hardware.** + +--- + +## 6. `examples/maskedsprites.bas` → `examples/sd81/maskedsprites_sd81.bas` + +**In progress — still not fully working.** + +One substantial change: `WaitForNewFrame` (defined in the example +itself, not the library) rewritten to not depend on interrupts: + +```diff +- ld de,23672 +- ld c,a ; A = C = minimumNumberofFramesToWaitSinceLastWait +- READ_IFF2 +- ex af,af' +- ei ; interrupts MUST be enabled before HALT +- halt ++ ld de, FRAMES ++ ld c,a ; C = minimumNumberofFramesToWaitSinceLastWait ++ call VSYNC_TICK ; guarantees at least one frame waited (replaces EI+HALT) + wait: + ld a,(de) + sub (hl) + cp c +- jr c,wait +- ld a,(de) +- ld (hl),a +- ex af,af' +- ret pe +- di +- RET ++ jr nc,enough ++ call VSYNC_TICK ++ jr wait ++enough: ++ ld a,(de) ++ ld (hl),a ++ ret +``` + +**Why**: `23672` is the Spectrum ROM's `FRAMES` counter, automatically +incremented by the 50Hz IM1 interrupt. The original does one initial +`HALT` (waits for the interrupt) and then a loop that trusts the +interrupt to keep incrementing it in the background while the loop just +*checks* without waiting again. On zx81sd there are no interrupts +(permanent DI; the `$0038` vector is a `DI;HALT` trap, not a real +handler) — that `HALT` would never wake up. It's replaced with +`VSYNC_TICK` (polling the SD81 Booster hardware's VSYNC pulse counter +through a port, the same routine `PAUSE` already uses), called +explicitly on every loop iteration that needs to wait. + +**Update — MSFS ported to the zx81sd mapper (block 7)**: the risk +above no longer applies. `src/lib/arch/zx81sd/stdlib/cb/maskedsprites.bas` +was created (a full override of the shared library, which stays +untouched): `SetBankPreservingINTs`/`GetBankPreservingRegs` rewritten +in hand-written ASM over port `$E7` (block 7, `$E000-$FFFF` — reserved +in our memory map for "data banking, maps, sprites"), and +`MaskedSpritesFileSystemStart` fixed at `$E000` instead of "whatever's +left up to `$FFFF`" (which assumed flat Spectrum RAM). The rest of MSFS +(hundreds of lines of block-bitmap arithmetic) is bank/address-agnostic +and was copied without touching a single line. Full detail, including a +real bug caught in the process (registers not preserved when written +in BASIC instead of ASM), in [MAP.md](MAP.md). + +Despite the fixes above (confirmed by simulation and, partially, on a +real emulator/hardware), the library in its current state **still +doesn't fully work** — work in progress, don't consider this example +closed yet. + +--- + +## 7. `examples/sd81/pong.bas` — classic transcription, one ASM change + +Not an official `examples/` sample but a classic Sinclair-BASIC-style +transcription of Pong (GOSUB/line numbers), added directly under +`examples/sd81/`. It already uses lowercase key comparisons +("4","q","3","a"), matching the current keyboard scheme with no change +needed — the only change was replacing the screen sync: + +```diff + ASM +- call VSYNC_TICK ; guarantees at least one frame waited (replaces EI+HALT) ++ call .core.VSYNC_TICK ; guarantees at least one frame waited (replaces EI+HALT) + ; halt ; Avoids screen flickering + END ASM +``` + +**Why**: `vsync.asm` wraps `VSYNC_TICK` in +`push namespace core ... pop namespace`; calling it from an +`ASM ... END ASM` block that isn't inside that namespace needs the +full `.core.VSYNC_TICK` prefix — the same error pattern as in +`maskedsprites_sd81.bas` (see [PRECAUTIONS.md](PRECAUTIONS.md), +section 5). Without the prefix, the compiler gives +`Undefined GLOBAL label '.VSYNC_TICK'`. + +--- + +## 8. `examples/sd81/block7test.bas` — no official equivalent, mapper test + +A minimal program (not a transcription of any example) written to +confirm, in isolation, that the SD81 Booster's memory mapper provides +independent storage per page: it writes different patterns to pages 20 +and 63 of block 7 (`$E000-$FFFF`) via `Map()` and checks they don't +overwrite each other. It was used to rule out the mapper as a cause +during the MSFS bug investigation (see [MAP.md](MAP.md)). Kept as a +reference example of direct `Map()`/`MapGet` usage over block 7. + +--- + +## 9. `print42.bas`/`print64.bas` — new libraries (no example changes) + +`stdlib/print42.bas` and `stdlib/print64.bas` (Britlion) implement a +42/64-column `PRINT` drawing pixel by pixel, instead of the normal +32-column `PRINT`. The zx48k version depends on the Spectrum ROM in +several places: + +- `print42.bas`: `ld hl,$5C7B` (`UDG` sysvar), `ld de,15360` + (fixed `CHARS-256`) and `ld a,(23693)` (`ATTR_P` sysvar). +- `print64.bas`: only `ld a,(23693)` (`ATTR_P`) — its 4px charset is + its own, it doesn't use `UDG`/`CHARS`. +- Both, additionally, place the screen and attributes using + **immediate** fixed-base constants (`add a,64` / `add a,88`, the + high bytes of `$4000`/`$5800`) instead of reading a pointer — this + works on the Spectrum because those addresses are ROM literals, but + on zx81sd `SCREEN_ADDR`/`SCREEN_ATTR_ADDR` are variables (block 6, + `$C000`/`$D800`). + +`src/lib/arch/zx81sd/stdlib/print42.bas` and `print64.bas` were +created (full overrides): fixed sysvars → their zx81sd equivalents +(`UDG`/`CHARS`/`ATTR_P`, see `sysvars.asm`), and the two screen/ +attribute base constants are **patched once on entering the function** +(self-modifying code on the `ADD A,n` instruction itself), reading the +real high byte of `SCREEN_ADDR`/`SCREEN_ATTR_ADDR` at that point — the +same thing the original ROM did with its own routines, only with a +variable value instead of a fixed one. + +**Real bug caught during the port** (not in the example, in the +library itself): a `#include once ` naively placed at the +top of the file (BASIC level, outside any `ASM` block) produced +`error: illegal preprocessor character`, and placed inside the +function's `ASM` block it compiled but hung the program at runtime +(`HALT` at a garbage code address). Cause: if this library happens to +be the **first** one to include `sysvars.asm` in the whole program, it +drags in `bootstrap.asm`/`charset.asm` behind it — and the latter does +`INCBIN "specfont.bin"` (the complete font, not assembly text) — right +at that point in the source file. At BASIC level, the BASIC lexer tries +to tokenize binary source bytes as if they were text (hence the +"illegal preprocessor character"). Inside the function, the font binary +gets emitted literally in the middle of the function's compiled body, +and the CPU "executes" it as if it were instructions the moment control +flow falls there. Fix: don't include `sysvars.asm` at all from these +files — they trust that it's already included by the rest of the +runtime (`CLS`/`PRINT`, which any program using `print42`/`print64` +will also use), and only reference the already-namespaced symbols +(`.core.CHARS`, etc.). Full detail in [PRECAUTIONS.md](PRECAUTIONS.md) +and [MAP.md](MAP.md). + +Verified by simulation (the usual Python harness): both test strings +(`tests_debug/print4264.bas`, companion repository) render legibly +pixel by pixel at the expected position, with no writes outside +screen/attributes/the file's own variables, and the program reaches its +normal `__END_PROGRAM`/`HALT`. **Confirmed working on real hardware.** + +--- + +## 10. MCU command examples (unofficial, no example to adapt) + +Six new example programs demonstrating the SD81 Booster MCU's command +surface (`stdlib/mcu.bas`/`joy.bas`), written directly for zx81sd — +none of them adapt an official `examples/` program. `examples/sd81/ +filesystem.bas` already existed from an earlier session (it's what the +companion repository's `files.bas`/`FILES*` binaries were compiled +from) and was expanded to also demo `McuMkdir`/`McuRmdir`/`McuCopy`/ +`McuMove`/`McuFree`, not just `McuCd`/`McuDirPrint`/`McuLoad`. + +| File | MCU commands exercised | Status | +|---|---|---| +| `examples/sd81/filesystem.bas` | `McuPwd`, `McuCd`, `McuDirPrint`, `McuMkdir`, `McuRmdir`, `McuSave`, `McuCopy`, `McuMove`, `McuDel`, `McuFree` | **Confirmed working on real hardware** | +| `examples/sd81/wavplayer.bas` | `McuLoad` with a `.WAV` file (played directly by the MCU, not loaded into memory) | **Confirmed working on real hardware** | +| `examples/sd81/vgmplayer.bas` | `McuPlayVgm`, `McuContVgm`, `McuPauseVgm`, `McuStopVgm` | **Confirmed working on real hardware** | +| `examples/sd81/pegdemo.bas` | `McuLoadPeg`, `McuPlayPeg`, `McuStopPeg` | **Confirmed working on real hardware** (byte order below is correct) | +| `examples/sd81/joystick.bas` | `Joy()` (joy.bas, MCU cmd 21) | **Confirmed working on real hardware** | +| `examples/sd81/randomaccess.bas` | `McuFOpenZx`, `McuFSeek`, `McuFRead`, `McuFWrite`, `McuFClose` | **Confirmed working on real hardware** | + +Memory-mapper access (`Map()`/`MapGet()`) already has a dedicated +example, `examples/sd81/block7test.bas` (see section 8 above), so no +new one was added for that. + +**Compile-time bug found while writing these**: `stdlib/input.bas` +defines `input()` as a **function** (`a$ = input(maxChars)`), not the +native BASIC `INPUT` statement — using `input a$` (statement syntax) +after `#include ` produces +`error: Cannot convert string to a value. Use VAL() function`, since +the identifier `input` is now bound to that function. All six examples +above end with `a$ = input(20)` to wait for a keypress, not +`input a$`. + +**PEG byte encoding, resolved**: the SD81 Booster manual's Appendix B +documents the PEG instruction set as 16-bit words in big-endian order +(e.g. `LD R,XX` = `0R XX`), while `mcu.bas`'s own comment on +`McuLoadPeg` says the raw bytes it expects are "2 bytes little-endian +per instruction" — `pegdemo.bas` followed the `mcu.bas` comment +literally (value byte before opcode byte), and this has now been +**confirmed correct on real hardware**: the manual's big-endian +notation describes the instruction word's numeric value, while +`McuLoadPeg`'s wire format for cmd 40 is genuinely little-endian, as +documented in the library. + +--- + +## Summary for the manual + +| Example | zx81sd copy | Source changes | Compile flags | +|---|---|---|---| +| comecoquitos.bas | no copy needed if the original isn't touched | none | `--string-base 1 --array-base 1` | +| snake_en.bas | `snake_sd81.bas` | 1 line (UDG address) | none special | +| flights.bas | `flights_sd81.bas` | 3 kinds of change (POKE removed, 4× PEEK COORDS, 8× key case) | none special | +| 4inarow.bas | no copy needed | none | none special | +| scroll.bas | no copy needed | none (the fix was in the `stdlib/scroll.bas` library) | none special | +| winscroll.bas | no copy needed | none (no library override needed either — no ROM dependency) | none special | +| putchars.bas/puttile.bas (unofficial, `putcharstile.bas`) | `putcharstile.bas` in `examples/sd81/` | none for putchars.bas; `puttile.bas` needed a zx81sd override (hardcoded screen/attr base constants, same fix as print42/64) | none special | +| maskedsprites.bas | `maskedsprites_sd81.bas` | `WaitForNewFrame` rewritten (EI+HALT → VSYNC_TICK); `cb/maskedsprites.bas` library ported to the mapper (block 7) — **in progress, still not fully working** | none special | +| pong.bas (unofficial) | `pong.bas` in `examples/sd81/` | 1 ASM line (VSYNC_TICK namespace) | none special | +| block7test.bas (unofficial) | `block7test.bas` in `examples/sd81/` | n/a (written directly for zx81sd) | none special | +| print42.bas/print64.bas (libraries) | not applicable (not examples) | none — the fix was in `stdlib/print42.bas`/`print64.bas` | none special | +| filesystem.bas, wavplayer.bas, vgmplayer.bas, pegdemo.bas, joystick.bas, randomaccess.bas (unofficial) | all in `examples/sd81/` | n/a (written directly for zx81sd) | none special | + +Pattern identified for future examples: + +1. Look for `POKE`/`PEEK` on numeric literals: it's almost always a + Spectrum sysvar that needs translating to its zx81sd equivalent + address (see `src/lib/arch/zx81sd/runtime/sysvars.asm`). +2. `INKEY$` comparisons against uppercase control keys (`"S"`, `"Q"`, + etc.): with the **current** keyboard scheme (direct press = + lowercase, `SHIFT+letter` = uppercase) this already works exactly + like on a Spectrum and **needs no change at all** — the case change + documented above for `flights.bas` corresponds to an earlier version + of `keyscan.asm` (see the "no longer needed" note in section 3.3) + and is kept here only as a historical record of that + already-generated copy. +3. Inline ASM code calling routines wrapped in `push namespace core` + (like `VSYNC_TICK`) needs the `.core.` prefix if the `ASM` block + calling it isn't already inside that namespace (see the `pong.bas` + case above). +4. If a new library needs its own sysvars (`CHARS`/`UDG`/`ATTR_P`/ + `SCREEN_ADDR`/...), **don't do `#include once ` inside + the file** — only reference the symbols with the `.core.` prefix, + trusting that the rest of the runtime (`CLS`/`PRINT`) already + included it. See the `print42.bas`/`print64.bas` case above and + [PRECAUTIONS.md](PRECAUTIONS.md). +5. `stdlib/input.bas` defines `input()` as a function + (`a$ = input(maxChars)`), not the native `INPUT a$` statement — once + `#include ` is used, `input a$` fails with + `error: Cannot convert string to a value`. See the MCU examples case + above. diff --git a/src/arch/zx81sd/doc/MAP.md b/src/arch/zx81sd/doc/MAP.md new file mode 100644 index 000000000..880005ead --- /dev/null +++ b/src/arch/zx81sd/doc/MAP.md @@ -0,0 +1,833 @@ +# Debug test log (DRAW3 / arc session) + +Note on paths: this log was originally written in the companion, +test-only repository for the port (where the `.bas` files mentioned +here all lived in a `tests_debug/` directory). The examples considered +mature enough to publish have already been copied into this +repository, under [`examples/sd81/`](../../../../examples/sd81/) +(`flights_sd81.bas`, `snake_sd81.bas`, `maskedsprites_sd81.bas`, +`pong.bas`, `block7test.bas` — see [BASIC_CHANGES.md](BASIC_CHANGES.md)). +The rest of the one-off debugging sources mentioned here (`diag1-6`, +`t_arc*`, `trig_test`, `heaptest`, `keytest`...) only remain in the +companion repository, not this one. + +Compiling each one (from the root of this repository): +``` +python -m src.zxbc.zxbc examples\sd81\.bas --arch zx81sd -o .bin +python src\arch\zx81sd\tools\split_sd81.py .bin +``` +(see [USAGE.md](USAGE.md) for the packager's detail.) + +| Source | SD81 prefix | What it tests | Result obtained | +|------------------------|--------------|---------------------------------------------------------------------|---------------------| +| `str_test.bas` | STRTEST | PRINT/STR$ of FLOAT (Phase 3) | OK: 7 / 7.5 / -3.25 / 0 / 123.5 / -0.5 | +| `trig_test.bas` | TRIGTST | SQR/SIN/COS/EXP/LN/ATN with trivial arguments (Phase 4) | OK: 3 / 1.41421 / 0 / 1 / 2.71828 / 1 / 3.14159 | +| `trig2_test.bas` | TRIG2 | SIN/COS with NON-trivial arguments (1, 1.5708, 3.14159) | OK: 0.8147(4?) / 0.5403 / 0.99999 / -0.99999 / 0 | +| `draw_arc_test.bas` | DRAWARC | DRAW+DRAW+CIRCLE combined (first arc attempt) | Only a vertical line appears, CIRCLE never shows | +| `t_line.bas` | TLINE | `DRAW 20,0` (2 args, no angle, no FP) | OK: horizontal line | +| `t_circle.bas` | TCIRC | `CIRCLE 60,30,20` alone | OK: correct circle | +| `t_arc.bas` | TARC | `DRAW 20,0,3.14159` (arc alone, horizontal offset, 180°) | BUG: vertical line (not horizontal, no arc) | +| `t_arc2.bas` | TARC2 | `DRAW 30,10,1.5708` (asymmetric offsets, 90°) | BUG: vertical line with a slight rightward tilt | +| `t_arc3.bas` | TARC3 | `PLOT 100,96` + `DRAW 20,0,3.14159` (centered, with margin) | BUG: chaotic star-like pattern of several lines from the center; one goes off-screen into the attribute area | +| `diag1.bas` | DIAG1 | Inline ASM: calls CD-PRMS1 (L247D) directly with z=40, A=pi/2, and prints mem-1/mem-3/mem-4/mem-0/line count | Blank screen — bug in the test itself (`#include` inside `ASM` puts executable code in the linear flow). Replaced by diag2 | +| `diag2.bas` | DIAG2 | Same as diag1 but without manual includes: BASIC variables + normal PRINT | OK: exact expected values → CD-PRMS1 numerically correct | +| `diag4.bas` | DIAG4 | With `-D DRAW3_DEBUG`: only prints N (number of segments captured by the draw3.asm hook) | OK: 16 → the capture hook works | +| `diag5.bas` | DIAG5 | diag4 + buffer traversal loop (2 SD81 pages) | Didn't print; dropped, replaced by diag6 (1 page) | +| `diag6.bas` | DIAG6/DIAG7 | diag4 + loop, 1 page. Its $152D dump + write breakpoints at $8004 located the bug | OK: 16/16. DIAG7 = identical, recompiled after the fix | +| `arcfix.bas` | ARCFIX | Reference test identical to a real Spectrum: `PLOT 100,100` + 2 arc DRAWs + CIRCLE | **OK after the fix**: hook + circle, matching the real Spectrum photo | + +## DRAW3 (arc) bug — RESOLVED + +**Root cause** (nothing to do with the FP calculator, whose math turned +out to be exact): `src/lib/arch/zx81sd/runtime/pixel_addr.asm` was +destroying register **D** (using it as scratch for V=191−Y). But +`draw.asm` (inherited from zx48k) saves the Bresenham Y coordinate in D +around the call (`ld d,b / call PIXEL_ADDR / ld b,d`), because the +Spectrum ROM's PIXEL-ADD ($22AC) preserves DE. As a result, every line +with a vertical component started internally with Y=191−y1, corrupting +COORDS and the drawing. Horizontal lines (t_line) and CIRCLE (which +doesn't go through that path) came out fine, which threw off the +initial investigation. + +**Fix**: PIXEL_ADDR rewritten to use B as scratch (it was already being +destroyed, same as the ROM) and preserve D and E. Verified on hardware +with ARCFIX (identical to the real Spectrum's result) and DIAG7. + +**Localization method**: a `#ifdef DRAW3_DEBUG` hook in draw3.asm +(enabled with `-D DRAW3_DEBUG`) that captures, for every segment: +|Dy|,|Dx|, signs and the previous COORDS in a buffer +(`DRAW3_DEBUG_BUF`); a memory dump of the buffer in EightyOne's +debugger + write breakpoints on COORDS ($8004/$8005). The dump showed +the accumulated FP position was perfect and that COORDS ended up wrong +after every line → the bug was in __DRAW/PIXEL_ADDR, not in +fp_calc.asm or draw3.asm. + +## Source files modified/created in this session (FP engine + arc) + +- `src/lib/arch/zx81sd/runtime/fp_calc.asm` — Phases 1-5 (the + CALCULATE engine, trig/log/exp/sqrt, and now STK-TO-A/STK-TO-BC/ + CD-PRMS1 for the arc). Also had `#include once ` added + (an unrelated bug: it's always included in every zx81sd binary, so it + had to be self-sufficient). +- `src/lib/arch/zx81sd/runtime/draw3.asm` — NEW. An override of + `zx48k/runtime/draw3.asm` that replaces calls to fixed ROM addresses + with the routines ported in `fp_calc.asm`. Includes trace + instrumentation behind `#ifdef DRAW3_DEBUG` (off by default; enabled + by compiling with `-D DRAW3_DEBUG`). +- `src/lib/arch/zx81sd/runtime/pixel_addr.asm` — FIX for the arc bug: + now preserves D and E (it used to destroy D, breaking draw.asm's + Bresenham on every non-horizontal line). +- `src/lib/arch/zx81sd/runtime/fp_tostr.asm`, `printf.asm`, `str.asm` — + from Phase 3 (PRINT/STR$ of FLOAT), unchanged in this session. + +## Sound: BEEP and PLAY (SD81's AY ZonX chips) + +| Source | SD81 prefix | What it tests | Result | +|-----------------|--------------|------------------------------------------------------------|-----------| +| `beeptest.bas` | BEEPTS2 | Variable BEEP (FP runtime) + constant BEEP (13/14 clock fix in __BEEPER) | OK: scale + DO/DO' + LA 440 | +| `playtest.bas` | PLAYTS2 | PLAY over 3 channels + AY/beeper comparison | OK (watch out: lowercase notes = one octave down!) | +| `aycal.bas` | AYCAL | AY vs beeper pairing by semitone | Used to detect the offset | +| `aycal2.bas` | AYCAL2 | Timeable durations + direct pairing | FFT: beeper 434.5 (correct, emulator pacing), AY 220 | +| `aycal3.bas` | AYCAL3 | Same with UPPERCASE notes | OK: unison 440/440 | + +Lesson from the "phantom octave" investigation: in PLAY's MML (128K +BASIC semantics), lowercase notes sound one octave below the current +octave. The initial tests used lowercase and the AY was sounding at +220 Hz *by design*. The emulator, the divider table (1.625 MHz) and the +beeper (3.25 MHz) were all correct. Verified with an FFT over recorded +audio output. As a bonus, a real latent EightyOne bug got fixed along +the way (the AY's clock kept the hardware-dialog card's setting instead +of the ZonX one forced by the SD81: it was missing a call to +Sound.InitDevices() after forcing machine.aytype). + +## MCU library (SD81 Booster) — `zx81sd/stdlib/mcu.bas` + `joy.bas` + +| Source | SD81 prefix | What it tests | Result | +|----------------|--------------|------------------------------------------------------------|-----------| +| `joytest.bas` | JOYTS2 | `Joy("QAOPM")` (cmd 21) + local validation + INKEY$ echo | OK | +| `mcutest.bas` | MCUTST | VERSION, GET/SETBYTE, PWD, SAVE+LOAD+verify, DEL, FREE, RTC, BAT, DIR, AY2 by registers, AyPlay | pending | +| `maptest.bas` | MAPTST | `Map(block,page)`/`MapGet` — the $E7 mapper: signatures in 2 pages switching block 5, and verification | OK | +| `exttest.bas` | EXTTST | Non-MCU extensions: `HexPoke` (*HEX), `MemMove` (*LDIR/*LDDR, stdlib), `StrInv`/`StrBold` (*INV/*BOLD) | OK | +| `ftest.bas` | FTEST | F_* handles: SAVE, F_OPEN_ZX81 (cmd 58), F_SEEK, F_READ with verification, F_WRITE+reread, F_CLOSE, DEL | OK | +| `lstest.bas` | LSTEST | Native LOAD/SAVE/VERIFY ... CODE statements → SD (runtime override load.asm/save.asm, cmd 9/10): SAVE+LOAD+verify, VERIFY ok/corrupt (ERR 26), missing file (ERR 26) | OK | + +Architecture: `mcu.bas` contains the protocol primitives in ASM +(McuSend/McuRecv/McuSendBlock/McuRecvBlock — the *Block ones are the +critical path for LOAD/SAVE/F_READ/F_WRITE) and wrappers for every +command in the manual (system, files, F_* handles, hardware, voice, +AY2/VGM/PEG, RTC/BAT). Automatic ASCII↔ZX81 conversion in the text +commands. `joy.bas` is a thin layer over `mcu.bas`. + +Protocol notes (extracted from the emulator's SD81Booster.cpp): +- After EVERY operation on $A7, the change in bit 7 of $AF is expected. + NEVER write to $AF (MCU reset). +- Z80→MCU strings: length byte + data (the MCU converts ZX81→ASCII + except for "raw" commands: JOY, BINARY_SAY, F_OPEN). +- MCU→Z80 streams (PWD/DIR/TYPE/FREE_TXT): request each character by + writing CMD_NEXTCH ($0D); end = EOT ($6F); the status arrives after. +- Fixed-length responses (LOAD, FREE, RTC, BAT, F_READ): a burst of + bytes reading $A7 with a clock wait between each one. +- F_OPEN: confirmed in the firmware (COMMANDS.cpp) that the MCU + assigns the handle and returns it (the manual was wrong and has been + corrected). Added F_OPEN_ZX81 (58) to the library and the emulator. +- OPENDIR/GETROWLEN/GETROW (16-18) aren't emulated in EightyOne: only + test them on real hardware. + +## Integration test: comecoquitos.bas (official zxbasic example) + +| Source | SD81 prefix | What it tests | Result | +|---|---|---|---| +| `examples/english/comecoquitos.bas` | COMECO | Complete 1985 game: FP, strings/slices, arrays, UDGs, block graphics, color/FLASH/BRIGHT, INKEY$, BEEP, RND | OK — identical to the Spectrum .tap | +| `fpleak.bas` | FPLEAK | Detector for FP stack leaks by block (reads $8024 after each idiom) | Helped narrow it down; the "leak" was UDG corruption | +| `blocktest.bas` | BLKTST | The 16 block graphics CHR$(128)-143 | OK after the PO_GR_1 fix | + +Three runtime bugs caught with this game (commits 9903c866 and +9a24059e): +1. UDG pointed 128 bytes past the end of the font (96 chars, not 256): + POKE USR CHR$ was clobbering runtime code → hang on the calculator's + first use. Fix: a dedicated 21-UDG area. +2. INKEY$ returned uppercase; the Spectrum's L mode (and the era's + programs) use lowercase. Fix: lowercase keyscan table. +3. PO_GR_1 (blocks CHR$(128)-143) generated corrupt patterns (OR into + the wrong register + swapped left/right quadrants). Fix: the ROM's + literal algorithm. + +RECIPE for classic examples transcribed from Sinclair BASIC: compile +with `--string-base 1 --array-base 1` (1-based indexing). Without it, +string-slicing collisions come out shifted by one position (not a bug +in the port: it happens the same way on zx48k). + +## Heap at $8100 + EightyOne tape traps — RESOLVED (2026-07-04) + +| Source | SD81 prefix | What it tests | Result | +|---|---|---|---| +| `tests_debug/heaptest.bas` | HEAPA..HEAPE | Dynamic memory manager (3 phases: char-by-char growth, large REALLOCs, STR$ in a loop) with the heap at different addresses | Bisection that isolated the bug | +| `tests_debug/memtest.bas` | MEMTST | R/W pattern dependent on address in $8100-$BFFF (2 passes) | OK — ruled out hardware/paging | +| `tests_debug/inputtest.bas` | INTEST | Minimal, isolated INPUT() | Reproduced the hang | +| `examples/sd81/flights_sd81.bas` | FLIGHT | Flight simulator: PEEK COORDS ($8004/5), FP-intensive, INPUT | Adaptation of examples/flights.bas | + +Two chained bugs, caught on 2026-07-04: + +1. **Compiler (`src/arch/zx81sd/backend/main.py`)**: `heap_size`/ + `heap_address` were registered with `ADD_IF_NOT_DEFINED`, but the + generic Z80 backend already defines them earlier (4768 / None) → + zx81sd's values ($8100 / 16127) were never applied and the heap + ended up inline (DEFS) inside the executable area, wasting 4768 + bytes and limiting the heap. Fix: direct assignment of + `OPTIONS.heap_size/heap_address` (the CLI can still override it). + +2. **Emulator (`Eightyone2/src/ZX81/rompatch.cpp`, `PatchTest`)**: the + ZX81 ROM's tape traps ($0207/$02FF/$031E/$0356) were triggered by + comparing PC + a **flat** `memory[pc]` read, which keeps reading the + ROM even though the SD81 has RAM mapped. With the heap in EQU, the + runtime moves $12A0 bytes down and the __DIVU16_FAST division lands + on $02FF → the SAVE trap sets `DE=1` mid-division → stable garbage + quotient → an infinite digit loop in __PRINTU_LOOP (PUSH AF with no + matching pop) → the stack descends, wiping out the runtime. Builds + with an inline heap were immune by coincidence: the 4 trap addresses + fell inside the DEFS block (data, the PC never passes through + there). Fix: `PatchTest` reads the byte with `zx81_PatchPeek()` + (mapper-aware). This bug does NOT exist on real hardware (there are + no traps). + +Methodology that solved it: deterministic simulation of the binary +with Python's `z80` library (pip install z80) — code-area integrity +diff after each stretch + breakpoints comparing registers against +EightyOne. The binary was correct in pure Z80 → the divergence was in +the emulator. Harness in the session's scratchpad (runsim*.py, +reproducible). + +## PRINT scroll jumping into the Spectrum ROM — RESOLVED (2026-07-04) + +Third bug in the flights.bas chain (wind=10, dir=100 → HALT): zx81sd's +`print.asm` had kept, from zx48k, the fallback +`__SCROLL_SCR EQU 0DFEh` (the Spectrum ROM's CL-SC-ALL routine). On +zx81sd there's no ROM: the first PRINT that overflowed the screen did a +CALL to whatever compiled BASIC line happened to occupy $0DFE → wild +execution → a gosub RETURN popping garbage → HALT. It depended on the +input because the number of digits typed moved the cursor: with wind +"1"/"0" the text never overflowed; with "10"/"100" it did. No previous +test caught this because they all use PRINT AT (never scroll). + +Fix: the buffer-based implementation (`__ZXB_ENABLE_BUFFER_SCROLL`, +scrolls via SCREEN_ADDR/SCREEN_ATTR_ADDR) is now the only branch of +__SCROLL_SCR in zx81sd/runtime/print.asm. Verified with the Python +simulator + scripted keyboard: the game's main loop keeps SP stable +over thousands of steps. Future note: check for any other EQU/CALL to +absolute Spectrum ROM addresses when porting files from zx48k (grep +already done: none remain in the zx81sd runtime). + +## New keyboard scheme: uppercase, CAPS LOCK and symbols — 2026-07-04 + +| Source | SD81 prefix | What it tests | Result | +|---|---|---|---| +| `tests_debug/keytest.bas` | KEYTST | Interactive INKEY$: prints the ASCII code + character of every key | Verified by exhaustive simulation (see below); pending manual testing on the emulator/hardware | + +The ZX81's physical keyboard doesn't distinguish upper/lowercase per +key: SHIFT+letter gives a symbol, not the uppercase of that letter. +Until now `keyscan.asm` only ever returned lowercase. New scheme +(`src/lib/arch/zx81sd/runtime/io/keyboard/keyscan.asm`, rewritten from +scratch): + +- No modifier: lowercase (same as before — comecoquitos, snake, + flights, row4 keep working without touching a line, because none of + them use SHIFT). +- `SHIFT + letter`: UPPERCASE of that letter. +- `SHIFT + "2"`: toggles a persistent CAPS LOCK (silent, prints + nothing). +- CAPS LOCK active: lowercase becomes uppercase; SHIFT still gives + uppercase the same way (it's an OR, no interaction — decision made + with the user). +- `"."` alone: `.` +- `SHIFT + "."`: `,` (same as a real ZX81). +- `"." + another key`: the symbol printed on the ZX81 keyboard for that + key (`:` with Z, `)` with O, RUBOUT with 0, etc. — the ZX81's old + SHIFT table, now reachable with "." instead of SHIFT, since SHIFT has + been redefined to give uppercase). + +Required rewriting the scan: it used to stop at the first row with +anything pressed (one key at a time was enough). Now two dedicated port +reads are needed for SHIFT (row 0) and "." (row 7, column 1), plus a +scan of the other rows looking for a third key, excluding those two +positions (`FIND_OTHER` routine). The `SHIFT+"2"` combo uses a +persistent state byte with edge detection so it doesn't toggle +repeatedly while held down. + +Verified with a Python simulation harness (`z80`, same methodology +already used for the heap bugs): `__ZX81SD_KEYSCAN` is called directly, +injecting the exact row bits of each combination through the I/O +callback (Z alone, SHIFT+Z, "."+Z, SHIFT+2 held for 4 consecutive +polls, etc.), without dealing with the complexity of a real keyboard. +The table and state offsets (`_KBD_*`, all LOCAL to the PROC, don't +show up in the `.map`) were located by searching for +`UNSHIFT_TABLE`'s byte pattern ("zxcvasdfg") in the binary and +computing the rest by fixed offset (each table takes 39 bytes). All 12 +cases in the design table matched exactly, including the CAPS LOCK +debounce. + +### 2026-07-04 fix: the "." modifier moved from keyscan to input.bas + +When testing it on the emulator, the user found that `"."+key` +(intended to give the ZX81 symbol by pressing both at once, like +SHIFT+letter) was impractical from `INPUT`: `PRIVATEInputWaitKey` +commits to the `"."` key as soon as it detects it alone, without giving +the second key time to actually arrive at the same time (unlike +SHIFT+letter, which can comfortably be held with the other hand). The +user's correct diagnosis: *"symbol handling shouldn't live in keyscan +but in INPUT.bas"*. + +Fix: `keyscan.asm` now treats `"."` like any other key — unshifted +gives `.`, with SHIFT gives `,` (same as a real ZX81), with none of the +"third key" logic for the dot (the row-7 exclusion in `FIND_OTHER` was +removed entirely, no longer needed). The `UNSHIFT_TABLE`/ +`SYMBOL_TABLE`/`CAPS_TABLE` tables were promoted from `LOCAL` to file +scope (`__ZX81SD_` prefix) and a new routine was added, +`__ZX81SD_SYMBOL_FOR(char)`, which does the reverse +UNSHIFT_TABLE→SYMBOL_TABLE lookup given an already-decoded character. + +Symbol composition now lives in `stdlib/input.bas` as a sequential +"dead key": on reading `"."`, the `input()` function reads the NEXT key +separately (with no simultaneity requirement) and calls +`PRIVATEInputSymbolFor()`; if there's a symbol, it's appended; if not, +the literal dot is appended and the second key is processed normally +(DEL, ENTER, or one more character). + +A bug of my own caught during the implementation (before the user ever +saw it): `"."` + `"0"` resolves to RUBOUT (12) via `SYMBOL_TABLE` (it's +the actual symbol the ZX81 prints over the "0" key), which would delete +the previous character when typing any decimal ending in ".0" (very +common: "3.0", "10.0"...). That value was explicitly excluded in +`input.bas` — RUBOUT is already reachable unambiguously with SHIFT+0 +(which is comfortable to hold at the same time). Verified with a +full-program simulation (scripted typing "1",".","0",ENTER → +`a$="1.0"` correctly, and "."," o",ENTER → `a$=")"`), not just the +isolated function. + +`tests_debug/keytest.bas` (KEYTST) is still the interactive `INKEY$` +tester; `tests_debug/inputtest.bas` (INTEST) is the same old mini +`INPUT()` test, now also useful for testing symbol composition by hand. + +### 2026-07-04 refinement: two dots in a row = a literal dot + +With the previous design, `"."` + a letter with an associated symbol +(e.g. Z → `:`) always resolved to that symbol — there was no way to +literally write a dot followed by that letter. User request: pressing +`"."` twice in a row should confirm the first dot as literal (the +redundant comma that used to come out of `"."+"."`, via +`SYMBOL_TABLE`, is no longer needed — it's already reachable directly +and unambiguously with `SHIFT+"."`), and whatever key comes after is +read as a fresh keypress with no combination. So, to write ".Z" you +type "." "." "Z". + +Implemented in `input.bas`: if the second key read after a "." is also +a ".", the dot is appended, the second keypress is discarded (prints +nothing by itself, `LastK=0`) and the loop goes back to reading a fresh +key. Verified with the same full-program simulation: +`"."+"."` → `a$="."`; `"."+"."+"z"` → `a$=".z"` (without forming the +`:` symbol). + +## scroll.bas — RESOLVED 2026-07-04 (new library, example unchanged) + +| Source | SD81 prefix | What it tests | Result | +|---|---|---|---| +| `examples/scroll.bas` | SCROLL | The 4 pixel-by-pixel scrolls (Right/Down/Left/Up) over a 60×60 px window, 30 rounds | Simulated 1200M ticks with no HALT/RST38; pending viewing on the emulator (the example does 1920 scrolls of up to 100×100 px, takes a while) | + +`src/lib/arch/zx48k/stdlib/scroll.bas` had no zx81sd override — the +zx48k version was used as-is, and its 8 subs (`ScrollRight/Left/Up/ +Down` + their `*Aligned` variants) all call `call 22ACh`, the Spectrum +ROM's *PIXEL-ADD* routine. On zx81sd there's no ROM mapped: that +address lands in the middle of the program's compiled code, and the +reported HALT (`RST 38` in the trace) was just whatever byte happened +to be there. + +Fix: `src/lib/arch/zx81sd/stdlib/scroll.bas`, an identical copy except +the 8 calls to `$22AC` are replaced with `call PIXEL_ADDR` (our own +routine, `runtime/pixel_addr.asm`, already used by `plot.asm`/ +`draw.asm`). The register contract is IDENTICAL to the ROM's (A=191, +B=Y, C=X → HL=offset, A=X AND 7, destroys B, preserves D/E), so not a +single line of the scroll loop bodies needed touching, only the call +site. `SP.PixelDown`/`SP.PixelUp` (from zx48k/runtime/SP/) didn't need +copying: they're pure arithmetic over `SCREEN_ADDR`, no ROM involved, +and already worked the same way on zx81sd (resolved through the normal +zx48k fallback mechanism when there's no override). + +`examples/scroll.bas` itself needed no source change — like +`4inarow.bas`, the problem was entirely in the library, not the +program. Added to `BASIC_CHANGES.md` with the same note. + +## maskedsprites.bas — RESOLVED 2026-07-04 (source change, not a library one) + +| Source | SD81 prefix | What it tests | Result | +|---|---|---|---| +| `examples/maskedsprites.bas` → `examples/sd81/maskedsprites_sd81.bas` | MASKED | Masked sprites (AND+OR) with MSFS, 10 animated sprites | Simulated 1000M ticks with no HALT/RST38/illegal write; PC advances across a wide range of addresses (not stuck) | + +Unlike `scroll.bas`, here the problem WAS in the example itself +(`WaitForNewFrame`, defined directly in `examples/maskedsprites.bas`, +not in the `cb/maskedsprites.bas` library): it does `EI` + `HALT` +waiting for the Spectrum ROM's 50Hz IM1 interrupt, comparing against +the ROM's `FRAMES` counter at absolute address `23672`. On zx81sd +interrupts are permanently disabled (the whole runtime runs with `DI`; +the `$0038` vector is only a `DI;HALT` trap, not a real handler) — that +`HALT` never wakes up. Confirmed by the trace: the simulator ended up +with `m.halted=True` exactly at `WaitForNewFrame`'s `HALT` after ~31M +ticks. + +Fix in `examples/sd81/maskedsprites_sd81.bas`: `WaitForNewFrame` +rewritten to use `VSYNC_TICK` (`runtime/vsync.asm`, already used by +`PAUSE`) instead of `EI+HALT` — polls the SD81 Booster's hardware +VSYNC pulse counter through a port ($AFh), with no dependency on +interrupts. The original algorithm did ONE initial `HALT` and then a +loop that checked `FRAMES` WITHOUT waiting again (trusting the +interrupt to keep incrementing it in the background); since nothing +increments it on its own on zx81sd, the loop calls `VSYNC_TICK` +explicitly on every round it still needs to wait. +`GetInterruptStatusInBorder` was left untouched (never called in the +main loop, only appears commented out — kept only so the end-of-file +compile check doesn't fail with "unused function"). + +### 2026-07-04 update: MSFS genuinely ported to the mapper (block 7) + +The `$5B5C`/`$7FFD` risk from above **no longer applies**: at the +user's request ("why don't we use the block 7 we have for banking?"), +`src/lib/arch/zx81sd/stdlib/cb/maskedsprites.bas` was created, a full +override of the shared library (which stays untouched, Boriel's rule). +Design (see also `BASIC_CHANGES.md`): + +- **Key finding**: MSFS's functions (`RegisterSpriteImageInMSFS`, + `FindFirstUnusedBlockInMSFS`, etc.) are bank/address-agnostic — they + only call `GetBankPreservingRegs`/`SetBankPreservingINTs` and read/ + write the BASIC variable `MaskedSpritesFileSystemStart`. By + rewriting those two primitives (using port `$E7` over **block 7**, + `$E000-$FFFF` — reserved in our memory map exactly for "data + banking, maps, sprites") and that address's calculation in + `InitMaskedSpritesFileSystem()` (fixed at `$E000` instead of + "whatever's left up to `$FFFF`", which assumed flat Spectrum RAM), + the rest of the file (hundreds of lines of block/bitmap arithmetic) + was copied literally without touching a line. +- `CheckMemoryPaging()` returns `0` (honest: zx81sd has no Spectrum + bank-5/7-style dual visible screen) with no effect on MSFS, because + MSFS's functions don't consult that function to decide whether to + use the bank — they always do it unconditionally. +- `SetVisibleScreen`/`GetVisibleScreen`/`ToggleVisibleScreen`/ + `CopyScreen5ToScreen7`/`CopyScreen7ToScreen5`/`SetDrawingScreen5`/ + `SetDrawingScreen7`/`ToggleDrawingScreen` → safe stubs (real + double-screen buffering isn't covered; dead code in this example + since `memoryPaging=0`, but they no longer touch `$5B5C`/`$7FFD` in + case something calls them directly in the future). + +**Real bug found during the implementation** (not by the user, caught +with the simulation harness itself): my first attempt wrote +`SetBankPreservingINTs`/`GetBankPreservingRegs` in plain BASIC instead +of hand-written ASM. That broke the register contract documented in +the original file itself ("Preserves: D, E, H, L") which +`RegisterSpriteImageInMSFS`'s ASM code and friends take for granted +(for example, so as not to lose `spriteImageAddr`, which arrives in +HL) — a compiled BASIC function uses registers freely inside with no +guarantee of preserving them. Result: all 6 test sprites registered at +the SAME address (`$0C07`) instead of distinct ones. Both primitives +were rewritten in hand-written ASM, with the exact same contract as +the original (only touching A, B, C). + +Verified by simulation: the 6 register addresses (`regHero0`, +`regFoe00`, `regFoe20-23`) come out sequential every 96 bytes exactly +(`$E010, $E070, $E0D0, $E130, $E190, $E1F0`, matching +`$E000+n*96+16`), without triggering `STOP`, and the main loop +(`WaitForNewFrame`) is reached repeatedly with no hang after 1000 +million simulation ticks with no `HALT`/illegal write. + +Known limitation of the Python simulator used in this session: it +doesn't model the memory mapper (`OUT` to `$E7` is a no-op in the +simulation, all RAM is treated as flat) — it can't validate that the +*physical* page swap genuinely works, only that the Z80 logic is +correct assuming it does. The definitive validation is on the +emulator/real hardware. + +### 2026-07-04, second round: still going to HALT on real hardware + +With the fix above already compiled, on the real emulator it still +triggered `__STOP` (same symptom: `RegisterSpriteImageInMSFS` returns +0). The trace confirmed the problem was in `SetBankPreservingINTs`, +which did the `OUT` to port `$E7` by hand instead of calling `Map()` +(mcu.bas): it wrote `A=7` (block, without combining it with the page) +with `B=page`. My initial hypothesis was that the hardware might be in +simple mode (where the data byte must carry page AND block combined, +`(page AND 31)<<3 | block`, and with only `A=7` it would be interpreted +as page=0) — **the user corrected this**: the SD81 loader leaves the +mapper in full mode from the `LOAD *MAP 7,63` line onward, until the +next reset, so that specific explanation doesn't fit (in full mode only +A's 3 low bits matter, the same in both versions). The exact cause was +still unconfirmed at the time of writing this. + +`SetBankPreservingINTs` was changed to call `Map()` (code already +proven in other contexts) instead of repeating the port logic by hand, +preserving D,E,H,L around the call with manual `push`/`pop` (`Map()` +itself preserves nothing). This had a side effect: `Map()` was no +longer referenced from BASIC anywhere (only from hand-written ASM), and +the compiler's dead-code eliminator removed it from the binary → +`Undefined GLOBAL label '._Map'`. Solved with an explicit, redundant +BASIC call to `Map(7, MaskedSprites_MSFS_Page)` inside +`InitMaskedSpritesFileSystem()` (commented as such — the compiler's +usage analysis doesn't count calls made from ASM). + +Verified again in simulation (the same 6 sequential addresses, no +`STOP`) — but it still failed on the real emulator. The switch to +`Map()` was, as the user pointed out, a no-op ("the library's Map does +exactly the same thing as the raw out"). + +### REAL root cause (third round, 2026-07-04): the FSB is never initialized + +The user's trace showed `FindFirstUnusedBlockInMSFS` scanning the +ENTIRE free-space bitmap (the FIND-INT loop, 254 lines of RRCA/DEC E) +and exiting through the `full` branch (`SCF/RET` → `JR C` → `LD HL,0`): +**every block appeared "used"**. `__EQ16` (the initial suspect) worked +perfectly — `regHero0` really was 0. + +Cause: **neither our version nor the original zx48k one ever clears the +FSB** (the bitmap bytes at `start+2..start+1+l`). On the Spectrum +that's not needed: the ROM's RAM test leaves all memory zeroed at +boot, so the bitmap is born "all free" for free. On zx81sd, block 7's +page arrives with factory garbage → every bit set to 1 → "no free +blocks" → `RegisterSpriteImageInMSFS` returns 0 → `STOP`. + +**Why the Python simulator gave a false OK twice**: its RAM also starts +at zero, just like a Spectrum's after the ROM test — exactly the +condition that hides the bug. Methodology lesson added to the harness: +to validate code that reads uninitialized memory, fill all unloaded RAM +with garbage (`$FF`) before simulating. With dirty RAM, the unfixed +binary reproduces the `STOP` (the trace's failure mode) and the fixed +binary passes completely (6 correct registers, main loop reached). + +Fix (3 lines + a comment in `InitMaskedSpritesFileSystem`): a +`FOR j = start+2 TO start+1+l: poke j,0: NEXT` loop after computing the +FSB's size. + +Along the way, it was also confirmed with +`examples/sd81/block7test.bas` (BLOCK7 prefix) that the mapper works +perfectly: different patterns written to pages 20 and 63 of block 7 +survive the page switch (independent content per page). The mapper was +never the problem. + +### 2026-07-04, fourth round: sprites as vertical lines — resident page + +With the FSB fix, on the real emulator MSFS now initialized correctly +(screen: `Init MSFS at 57344`, `Free Blocks = 85`, and all 6 registers +with the EXACT values predicted by the simulation) but the sprites were +being drawn as vertical lines instead of their actual graphics. + +Cause (a design oversight of this override, not the original): +`SaveBackgroundAndDrawSpriteRegisteredInMSFS` — the function that draws +in the main loop and manufactures shifted images on demand — accesses +MSFS **without wrapping it in Get/SetBank**. In the original design it +doesn't need to: on 128K, bank 7 stays mapped at `$c000` +(`SetDrawingScreen7`) and on 48K, MSFS is always visible in flat RAM. +Our first version "released" block 7 back to page 63 when restoring +after `Init`/`Register...` → drawing read masks and graphics from page +63 (garbage) → vertical lines. The simulator couldn't detect this: +with no mapper modeled, page 20 and 63 are the same flat RAM. + +Fix: MSFS's page stays **resident** in block 7 from init onward — +`SetBankPreservingINTs` with a value ≠ 7 only records the number, it +doesn't unmap (the "release back to page 63" was an invention of this +override, nothing needs it). Documented in the library's header: if a +program uses block 7 for its own banking, it must remap its own page +and call `SetBankPreservingINTs(7)` before using MSFS again. + +## Chroma81 colour mode not activated by Spectrum mode alone — RESOLVED (2026-07-05) + +Found on real hardware, not caught by the emulator: activating +Superfast HiRes Spectrum mode (`POKE 2045,172` / `ld ($07FD),a`) turns +on the Spectrum-compatible screen layout, but does **not** by itself +turn on Chroma81 colour on a real SD81 Booster — everything showed up +monochrome despite `INK`/`PAPER`/attributes being written correctly. +EightyOne shows colour regardless of this step, which is an **emulator +bug** (color should also require the same explicit activation to match +real hardware) — needs reporting/fixing in the EightyOne repository +separately, not something to work around here. + +Fix: `tools/boot1.asm` (stage 1 bootstrap) now also writes to the +Chroma81 port (`$7FEF`) right after activating Spectrum mode: + +``` +ld bc, $7FEF ; Chroma81 port: set known state +ld a, 39 ; bit5=1 color on, bit4=0 char-code mode +out (c), a +``` + +`tools/boot1.bin` reassembled with `zxbasm.py` (34 bytes, was 27 — +exactly the 3 new instructions' bytes: `01 EF 7F` / `3E 27` / `ED 79`). +This is the shared stage 1 loader used by every zx81sd program, so no +program needs recompiling — just copy the updated `BOOT1.BIN` to the +SD card. + +## Screen/attributes not cleared at program start — RESOLVED (2026-07-06) + +Reported after real-hardware testing: block 6 (screen RAM, $C000-$DFFF) +is physical RAM that survives a reset, unlike a real Spectrum ROM cold +boot which always starts from a cleared display file. Any zx81sd +program that doesn't call `CLS` itself (many quick debug tests just +`PRINT` sequentially) could show leftover pixels/attributes from +whatever ran before it. + +Fix: `runtime/bootstrap.asm`'s `SD81_INIT_SYSVARS` (already run via +`#init` before every program, right after setting `SCREEN_ADDR`/ +`SCREEN_ATTR_ADDR`/`ATTR_P`) now also clears the physical screen: +6144 bytes at `$C000` to 0, 768 bytes at `$D800` to the just-set +`ATTR_P` value ($38 = INK 0/PAPER 7). Same approach as the shared +`cls.asm`, inlined here to avoid depending on CLS being linked into +every binary. + +This is in the shared bootstrap, so every zx81sd binary gets it +automatically on recompilation — no per-test changes needed. Verified +by simulation: pre-filled screen/attr RAM with garbage before boot, +confirmed both regions read back as fully cleared (0 / $38) right +before the program's own first PRINT, via a Python harness tracing +PC/BC/HL through the LDIR loops. + +## SD81DBufOn/SD81DBufOff/SD81WaitVSync — double buffer library, port $E7 (2026-07-07) + +New `stdlib/dbuf.bas`: wraps the SD81 Booster's present-blit double +buffer feature. Ported from `SD81-Booster/EXAMPLES/DBUF/bounce.asm` +(reference demo authored alongside the FPGA feature), adapted for +zxbasic because memory-mapped IO is disabled by `tools/boot1.asm` +(`POKE 2056,0`) before any compiled program runs, so the classic +`POKE 2057` control path is unusable from zxbasic — everything goes +through port `$E7`'s pseudo-block-8 encoding instead (`OUT` with +A=8, B=32+frontBlk to enable, B=0 to disable). `boot1.asm` also leaves +the mapper in full-paging mode permanently, which is exactly the state +this path requires, so it works unconditionally on every zx81sd binary. + +The front buffer is *not* one of the program's own blocks 0-5 (code/ +data, see `_PAGE_MAP` in `backend/main.py`), nor block 6 (the live +screen) nor block 7 (data banking, `Map()` in `mcu.bas`) — it lives in +the FPGA's private screen shadow RAM, addressed with its own +independent 0-7 index. Per the hardware spec, blocks 4/5 are the +recommended choice and 0-3/6-7 should be avoided; the library and demo +use 5. + +`examples/sd81/bounce_sd81.bas` (mirrored to the private repo as +`tests_debug/bounce_sd81.bas`, packaged as `BOUNCE.P`/`BOUNCEP8.BIN`) is +a straight port of the reference demo's logic (erase/~6ms delay/move/ +draw synced to VSYNC, SPACE toggles dbuf live, M freezes movement, Q +quits) — much shorter than the original because zx81sd's boot already +sets up the screen address, Superfast HiRes Spectrum mode, Chroma81 +colour and the initial screen/attribute clear (see the previous +section), none of which bounce.asm can assume when running standalone. + +This is the **first test of the port $E7 dbuf path** — only the +`POKE 2057` path had been validated on real hardware before this. +Verified so far only in simulation (Python `z80` harness): confirmed +`SD81DBufOn(5)` emits exactly `OUT $E7` with A=8/B=37 (bit5 enable, +front=5) and the border changes to green as expected; the main loop +runs correctly through many frames (10M+ simulated T-states) with +`PaintBall` writing the expected 32-byte block each time. The simulator +doesn't model the FPGA's video timing/blit, so whether the dbuf +actually eliminates tearing on screen can only be confirmed on real +hardware — that's the point of this demo. + +### 2026-07-07 fix: `Move()` bouncing early (~y=128 instead of 176) + +Reported after real-hardware testing: the ball in `bounce_sd81.bas` +bounced roughly mid-screen instead of reaching the bottom, as if it +had hit an early floor. + +Root cause: `Move()`'s original version widened `bally (ubyte) + dy +(byte)` to an `integer` for the range check. The generated code +(`ld a,(_bally) / ld h,a / ld a,(_dy) / add a,h / ld l,a / add a,a / +sbc a,a / ld h,a`) computes the 8-bit sum correctly in the low byte, +then **sign-extends that 8-bit result based on its own bit 7** to fill +the high byte. That's correct if both operands were meant to be signed +bytes, but `bally` is genuinely unsigned (0-176) — `bally=126, dy=2` +gives an 8-bit sum of 128 ($80, bit 7 set), which gets sign-extended to +-128 instead of +128. `if newY < 0` then true firing the bounce 48 +pixels early. + +Fix: keep `Move()` entirely in `ubyte` (mod-256) arithmetic, matching +`bounce.asm`'s own `add a,b / cp 177` approach — never widen the +ubyte+byte sum to a signed integer at all, compare unsigned instead +(`if newY >= 177`). Verified by simulation: `bally` now climbs smoothly +through 128, 130, ... past the point where it used to bounce. + +General lesson for this port: **`ubyte + byte` widened to `integer` +sign-extends the 8-bit sum's own bit 7, not the semantically correct +signed value** — safe when both operands are small/signed, wrong when +one operand (like a screen coordinate) is genuinely unsigned and can +exceed 127. Do that arithmetic in a type wide enough from the start +(assign the ubyte to an integer/uinteger variable *before* adding), or +stay in 8-bit space with an unsigned comparison if the range allows it, +as done here. + +### 2026-07-14: RST $38 behaves like a real HALT instead of hanging forever + +User request: interrupts stay disabled for the whole program (by +design), so RST $38/IM1 "should never be reached" — but if something +did reactivate them, the old `di`+`halt` handler locked the machine up +permanently. Changed it to wait for the next VSYNC pulse (port `$AF`, +bits 6-1 = pulse counter, reset on read) and `ret`, approximating what +a real Z80 HALT does (resume after the next interrupt) instead of +hanging. + +**Found along the way: `src/lib/arch/zx81sd/runtime/vectors.asm` was +dead code, never `#include`d by anything.** The actual RST vector table +is emitted directly as Python-generated ASM text in +`backend/main.py::emit_prologue()` (`org $0038` / `di` / `halt`, etc.) +— a duplicate of what `vectors.asm` described, added in the same +original commit but apparently never wired together. First attempt at +this fix edited `vectors.asm` and got silently ignored (compiled +binaries kept the old `di`/`halt` bytes at $0038) until byte-inspecting +the actual output caught it. Fixed the real source in `main.py` instead +and deleted `vectors.asm` to remove the misleading duplicate — if +`vectors.asm` (or any other zx81sd runtime `.asm` file) needs editing +again, first grep for whether it's actually `#include`d/`#require`d by +anything, don't assume a file with a plausible name and location is +live. + +Verified: compiled binary now has `DB AF E6 7E 28 FA C9` at `$0038` +(`in a,($AF)` / `and $7E` / `jr z,-6` / `ret`) instead of `F3 76` +(`di`/`halt`); simulated a stray RST $38 with a port callback that +returns "no pulse" 3 times then "pulse" — confirmed the loop reads the +port exactly 4 times (3 waits + the one that breaks it) before +falling through to `ret`. + +**Also discussed and closed: returning to the ZX81 BASIC prompt after a +program ends**, instead of the permanent `di`/`halt` at +`__END_PROGRAM`. Not feasible, for two independent reasons: + +1. **No software reset path exists in the FPGA at all** (confirmed by + the hardware's author): `nRESET` in `SD81.v` is a plain input wire + driven by the board's own reset circuit — nothing in the Z80-facing + register set can pulse it. There is no port write or mapper page + value that re-triggers the power-on sequence from software. +2. Even if there were, `tools/boot1.asm` remaps block 0 away from the + ZX81's own ROM/RAM to run the compiled program, and `USAGE.md`'s own + loader documentation notes the mapper's full-paging mode "doesn't go + back to simple mode until the next reset" — a one-way transition by + design. And the original BASIC session's own RAM (variables, display + file, stack) has already been overwritten by the compiled program by + the time it runs, so there's no state left to return *to* anyway. + +Bottom line: the closest thing to "exit to BASIC" a zx81sd program can +offer is `RST 0` (restarts the *current* program, already wired in the +vector table above) — a genuine return to a ZX81 BASIC prompt requires +the physical reset. Not revisiting unless the hardware itself gains a +software-triggerable reset line. + +## `array.asm` corrupted code on every multi-dimensional array access — RESOLVED (2026-07-22) + +Found while porting a third-party game (ZXOilPanic, heavy user of 2D +arrays like `sprite(29,3)`, `down(4,1)`, etc.) to zx81sd. Caught live on +real hardware with EightyOne's debugger: a write breakpoint on the Z80 +stack pointer's own descent showed a `PUSH DE` corrupting `FP_STKEND` +(`$8024`), which cascaded into the floating-point calculator operating +on garbage addresses, eventually executing screen/attribute memory as +code and crashing (`RST 8`, `RST 30`, or a wild jump into `$C000+`, +depending on what garbage byte the CPU happened to land on). + +Root cause, once traced back far enough: `src/lib/arch/zx48k/runtime/ +array/array.asm` (`__ARRAY`, the shared N-dimensional array indexing +routine used by **every** zxbasic program that declares a +multi-dimensional array) uses `LBOUND_PTR EQU 23698` — the real +Spectrum's `MEMBOT` system variable — as scratch storage for 4 pointer +pairs (8 bytes: `LBOUND_PTR`/`UBOUND_PTR`/`RET_ADDR`/`TMP_ARR_PTR`). +On a real Spectrum that's safe ROM-reserved RAM. On zx81sd, address +23698 ($5C92) falls **inside the program's own compiled code** (block 2, +$4000-$5FFF) instead of a real sysvar — every array access silently +corrupted a few bytes of code there. This is a **general zx81sd port +bug**, not specific to this game: any zx81sd program using +multi-dimensional arrays was at risk, it just hadn't been exercised +heavily enough by earlier examples/tests to surface (none of them used +2D+ arrays this intensively). + +Fix: new `src/lib/arch/zx81sd/runtime/array/array.asm`, an override of +the shared file (Boriel's rule: the shared one stays untouched) with +the only change being `LBOUND_PTR EQU ARRAY_SCRATCH` instead of `EQU +23698` — `ARRAY_SCRATCH` is a new dedicated 8-byte sysvar +(`sysvars.asm`, `SYSVAR_BASE + $83`, right after `FP_MEM_AREA`, still +well within the $8000-$80FF sysvar block before the heap at $8100). + +Verified by simulation: 1.2 billion T-states with a write-monitor on +both the old MEMBOT address (zero writes now) and the two known +hang-vector addresses (`$0008`/`$0009`, `$0030`/`$0031` — never +reached). Previously, with the same binary before this fix, the +program reliably crashed within a few hundred million T-states via one +of those two RST vectors. + +## Same bug, two more places: `chr.asm` and `arith/divf.asm` — RESOLVED (2026-07-22) + +The array.asm fix above turned out to be one instance of a **systemic +pattern** across the shared zx48k runtime: a project-wide grep for +`EQU 2[34]...` found roughly a dozen files that hardcode raw Spectrum +system-variable addresses as local scratch/temp storage, bypassing +`sysvars.asm` entirely (so zx81sd's own sysvars.asm override never +gets a chance to relocate them). Confirmed two more of these actually +firing while continuing to debug ZXOilPanic on real hardware after the +array.asm fix — the corruption just moved to a different address once +the binary's layout shifted: + +- `runtime/chr.asm` (the `CHR$()` function, called 6 times per score + update in this game) uses `TMP EQU 23629` (Spectrum's `DEST`) to + stash the return address while calling `__MEM_ALLOC`. On zx81sd, + 23629 ($5C4D) landed inside `__DIVU32` (32-bit division) in the + build being tested. +- `runtime/arith/divf.asm` (floating-point division, used by + `score/66.0` in this game) uses `TMP EQU 23629` (same address as + chr.asm) and `ERR_SP EQU 23613` to save/restore a stack recovery + point around the division (a "longjmp" trick for divide-by-zero, + matching the real Spectrum ROM's error-trapping convention). This + code runs on **every** division, not just on error — worth noting + that on zx81sd this recovery mechanism doesn't actually do anything + useful anyway, since `__ERROR` does `DI`+`HALT` directly rather than + restoring `SP` from `ERR_SP` to unwind — but it still unconditionally + writes to both addresses every time regardless. + +Fix, same pattern as array.asm: new `runtime/chr.asm` and +`runtime/arith/divf.asm` overrides, each with only the address changed +(`CHR_SCRATCH` at `$808B`, `DIVF_SCRATCH` — TMP+ERR_SP, 4 bytes — at +`$808D`, both in `sysvars.asm` after `ARRAY_SCRATCH`). + +Verified by simulation with a corrected VSYNC-pulse mock (the earlier +simulations in this session had used an oversimplified port-toggle mock +that doesn't match the real bit-6-1 pulse-counter semantics `PAUSE` +expects, which risked mid-testing false "hangs"): monitored writes +across the **entire** program's address space (not just block 2) for +200 million T-states — zero unexpected corruption, versus reliably +corrupting code within a few hundred thousand T-states before these +two fixes. + +**Not yet audited**: the same grep turned up further untouched +hardcodes in `runtime/arith/modf16.asm` (`TEMP EQU 23698`, same as +array.asm's old MEMBOT) and `runtime/break.asm`'s `PPC` — the former +isn't exercised by this particular game (no `MOD` operator used) so it +was left alone for now; a full systemic audit of every zx48k runtime +file for this pattern is worth doing separately at some point, this +session only fixed the ones actually observed corrupting a real +program. + +## Stray EI in ported code corrupted arithmetic at random — RESOLVED + +**Symptom (ZXOilPanic)**: `sprite(13,2)` — a plain 2D-array read — +returned -1 instead of 2072, but only when the game ran at full speed: +single-stepping in the debugger never reproduced it, the array's data +in RAM was verified intact (no writes ever hit it), and an isolated +test reading the same array/index worked perfectly. The wrong value +(-1) made the sprite blit read from `@sprites - 1`, painting garbage. + +**Root cause**: the game's `tilexy` asm block ends with `EI` (and the +BeepFX engine's exit path too) — normal on the Spectrum, but zx81sd's +runtime runs with interrupts disabled at all times. After the first +`tilexy` call, interrupts stayed enabled, and the ZX81 fires IM1 INTs +continuously (A6 low during refresh), landing on `$0038`. Our +`RST38_WAIT` vector clobbered **A and flags** (`in a,($AF)` / `and`), +so any interrupted computation (e.g. `__ARRAY`'s offset arithmetic) +resumed with corrupted A/F. Classic heisenbug: stepping in EightyOne +doesn't deliver INTs the same way, so it vanished under the debugger. + +**Fix (two layers)**: +1. Game side: `#ifndef __ZX81SD__` around both `EI`s (ported programs + must never re-enable interrupts on zx81sd). +2. Runtime side (defense-in-depth, `backend/main.py`): the `$0038` + vector now does `push af` / wait for VSYNC pulse / `pop af` / `ret`, + so a stray INT costs only time instead of corrupting registers. + +**Lesson**: when porting Spectrum code, grep for `\bei\b` and `\bhalt\b` +in every asm block. A heisenbug that disappears under single-stepping +on zx81sd should immediately suggest interrupts got re-enabled. diff --git a/src/arch/zx81sd/doc/PORTING_GUIDE.md b/src/arch/zx81sd/doc/PORTING_GUIDE.md new file mode 100644 index 000000000..4d4a6b5dc --- /dev/null +++ b/src/arch/zx81sd/doc/PORTING_GUIDE.md @@ -0,0 +1,212 @@ +# Step-by-step guide: porting a Spectrum ZX BASIC program to zx81sd + +This is a checklist, not a tutorial — each step links to the doc that +has the full detail. Work through it roughly in order; most of it only +matters if the specific pattern shows up in the program you're +porting. Everything here was distilled from real ports (official +`examples/`, and a full third-party game, ZXOilPanic) that hit one or +more of these in practice — see [MAP.md](MAP.md) for the investigation +traces if you want the "why" behind any of them. + +## 0. The one rule that makes everything else optional + +**Keep the exact same `.bas` source compiling for both `zx48k` and +`zx81sd`.** Don't fork the file, don't overwrite Spectrum-targeting +code unconditionally — wrap every zx81sd-specific change: + +```basic +#ifdef __ZX81SD__ + ' zx81sd-specific code +#else + ' original Spectrum code +#endif +``` + +This works identically inside `asm ... end asm` blocks (the +preprocessor runs at the text level, before the BASIC/ASM split). +Compile for each target with: + +``` +python zxbc.py --arch=zx48k -o out_spectrum.bin +python zxbc.py --arch=zx81sd -D __ZX81SD__ -o out_zx81sd.bin -M out_zx81sd.map +``` + +(or the one-shot [`build_sd81.py`](../tools/build_sd81.py) for the +zx81sd side, see step 8). `zxbasic` never defines `__ZX81SD__` +automatically — you always pass it by hand with `-D`. + +## 1. There is no ROM: hunt down hardcoded Spectrum addresses + +Grep the source (and any library `.bas`/`.asm` it pulls in) for: +- Raw numeric `POKE`/`PEEK`/`CALL` addresses (4-5 digit decimals, or + hex like `$5C..`/`$22..`). +- Anything that looks like a Spectrum ROM sysvar name in a comment + (`UDG`, `COORDS`, `BORDCR`, `FRAMES`, `ATTR_P`...). + +Every one of these needs translating via the **full equivalence +table** in [SYSVARS.md](SYSVARS.md) (Spectrum address ↔ zx81sd +address, for every sysvar zx81sd exposes). A few you'll hit almost +immediately even in simple programs: + +- **Screen/attribute base address**: Spectrum `$4000`/`$5800` → + zx81sd `$C000`/`$D800`. Common in raw `LDIR`/`PEEK`/`POKE` screen + code that doesn't go through `SCREEN_ADDR`/`SCREEN_ATTR_ADDR`. +- **ULA port** (border color / beeper, `OUT`/`IN` on `$FE`/254): + zx81sd emulates the same border+beeper bit layout at `$FB`/251 + instead. Full detail and the keyboard-port caveat in SYSVARS.md's + "Quick reference" table — **don't** just s/254/251/ on a raw `OUT`, + route it through the shadow-byte mechanism `BORDER`/`BEEP` already + use, or you'll clobber whichever of border/beeper you didn't intend + to touch. +- **AY sound chip ports** (Spectrum 128's `$FFFD`/`$BFFD` register + latch/data) → zx81sd's SD81 Booster ZonX-81 interface at `$CF`/`$0F` + instead — see `stdlib/play.bas`'s `_PLAY_WRITE_TO_REGISTER` for the + reference. If the program pokes raw note dividers into AY registers + directly (not through `PLAY`), also recompute them for the SD81's + 1.625 MHz AY clock (vs the Spectrum 128's 1.7734 MHz) — same + SYSVARS.md section has the formula. + +See [PRECAUTIONS.md](PRECAUTIONS.md) section 1 for the full rationale +and [BASIC_CHANGES.md](BASIC_CHANGES.md) for a line-by-line catalog of +every case already found in the official examples. + +## 2. Never `EI`, never wait on `HALT` + +zx81sd runs the **entire program** with interrupts disabled. Grep +every ASM block (inline `asm...end asm` and any hand-written `.asm` +library) for `\bei\b` and `\bhalt\b`: + +- `EI` (with or without a following `HALT`) is dangerous — see + [PRECAUTIONS.md](PRECAUTIONS.md) section 4 for exactly how bad + (a real, hours-long heisenbug came from a single stray `EI` at the + end of a sprite-blit routine, corrupting unrelated array reads + later on). +- A `HALT`-based wait for the Spectrum's 50Hz interrupt never wakes + up on zx81sd. Replace it with `Pause n` (BASIC) or `call + .core.VSYNC_TICK` (ASM) — see PRECAUTIONS.md section 4 for the + exact substitution and namespace-prefix gotcha. + +**Diagnostic tell**: if a bug reliably disappears while single-stepping +in the debugger but reproduces at full run speed, suspect a stray `EI` +first, before anything else. + +## 3. Register contracts and ASM label namespaces + +If the program includes hand-written `.asm` that calls into runtime +routines directly (not just through BASIC statements), check +[PRECAUTIONS.md](PRECAUTIONS.md) sections 2 and 5: some routines have +non-negotiable register contracts (a replaced ROM routine must +preserve exactly what the original did), and zx81sd's namespace +mangling means a bare label reference from inside an `asm` block that +isn't already inside `push namespace core` needs the full `.core.` +prefix or you'll get `Undefined GLOBAL label`. + +## 4. The keyboard is the ZX81's, not the Spectrum's + +Any code that reads the keyboard by hand (not through `INKEY$`/ +`INPUT`) via raw port I/O needs rework — the ZX81's keyboard matrix +layout is different from the Spectrum's, even where a port number +happens to coincide (see SYSVARS.md's ULA-port note). Full detail in +[PRECAUTIONS.md](PRECAUTIONS.md) section 3. Also remember: **the ZX81 +keyboard has no shift-driven case distinction** — `SHIFT`+letter +produces a symbol, not uppercase, a common surprise when porting a +game that expects to read letter case directly (see +`zx81sd-keyboard-case` project memory if you're the original author +of this port — not a zxbasic-level fact, a physical hardware one). + +## 5. `zxbasm` binary literal syntax + +If the program has hand-written `.asm` using the trailing-`b` Pasmo/ +Zilog binary literal convention (`10110010b`), it won't assemble — +`zxbasm` only accepts the leading form: `%10110010` or `0b10110010`. +Convert with a straightforward `sed`/regex pass before anything else, +it's a pure syntax issue with no semantic risk. + +## 6. Dead-code elimination and `@function` references + +If a subroutine or array is only ever referenced via its **address** +(`@myFunction`, `@myArray`) from inside another function's body — never +called directly, and never referenced from global/top-level code — +zxbasic's dead-code eliminator can fail to see it as "used" and strips +it, causing an `Undefined GLOBAL label` at link time (a real gap: the +optimizer's call-graph walker tracks `CALL`/`FUNCCALL` nodes, not +`ADDRESS` nodes, and address-of-inside-a-function-body is a separate +code path that isn't marked as "accessed"). Workaround: a throwaway +address-of reference at **global** scope keeps it alive: + +```basic +' Compiler dead-code-elimination gap: @sprites is only ever taken from +' inside a function body, which isn't enough to mark it as used. +dim __keep_sprites as uinteger: __keep_sprites = @sprites +``` + +If you're banking a large data table (sprites, a level map...) into +block 7 to fit under the `$8000` budget (step 8) by wrapping a `sub`'s +`asm` body in a manual `ORG $E000`/restore, `@name` stops pointing at +the relocated data entirely (a different, related gap — see +[PRECAUTIONS.md](PRECAUTIONS.md) section 9) — you'll need both this +dead-code workaround *and* to stop using `@name` as an address. + +## 7. Sysvar scratch bugs already fixed in the runtime — no action needed + +Several shared `zx48k/runtime/` files historically hardcoded a raw +Spectrum sysvar address as scratch storage (bypassing `sysvars.asm` +entirely) — on zx81sd that address lands inside the program's own +compiled code, corrupting it. `array.asm` (multi-dimensional array +reads), `chr.asm` (`CHR$()`), and `arith/divf.asm` (floating-point +division) all had this bug and are **already fixed** with zx81sd +overrides — nothing to do here, just don't be surprised if you find +the override files while reading the runtime. `arith/modf16.asm` +(the `MOD` operator) has the same pattern and is **not yet fixed** +(not triggered by any program so far) — if a program using `MOD` +shows array-adjacent corruption, this is the first suspect. Full list +and the exact addresses in [SYSVARS.md](SYSVARS.md)'s hardcoded-scratch +tables. + +## 8. Compile, package, and copy to the right place + +``` +python src/arch/zx81sd/tools/build_sd81.py [PREFIX] --copy-to +``` + +Does the compile (with `-D __ZX81SD__` and `-M` for the symbol map) +and the SD81 page-splitting + `.P`-loader generation in one command. +**Get `--copy-to` right, or point it at the emulator/hardware's real +SD folder by hand afterwards** — a stale package the emulator wasn't +actually reading was itself the cause of a multi-hour false-alarm +debugging session once, see [USAGE.md](USAGE.md) section 3b. If you +only need a fresh `.map` without repackaging (e.g. after hand-editing +the generated `.asm`), see `gen_map.py` in the same section. + +## 9. Test and debug + +- Simulating with Python's `z80` package before touching real + hardware/emulator catches a lot, but has real limits: an + oversimplified VSYNC-port mock can get a program stuck early, and a + bug that depends on genuine interrupt timing (step 2) **cannot** be + reproduced by a simulator that never delivers interrupts in the + first place. See [MAP.md](MAP.md) and USAGE.md section 4 for the + methodology and its known traps. +- Live debugging in EightyOne's Debug Window: execution breakpoints + with hit-counts (`EXE=$addr`), write breakpoints (`WR=$addr`), and + the Memory viewer (cross-referenced against the `.map` file) are the + main tools — see MAP.md for worked examples of narrowing down a bug + this way, including how to avoid re-triggering on the wrong call + when many call sites share the same target address. +- **`.map` must match the binary actually loaded.** Regenerate it on + every rebuild (`build_sd81.py`/`gen_map.py` both do); a mismatched + `.map` gives confidently-wrong addresses that waste debugging time. + +## See also + +- [PRECAUTIONS.md](PRECAUTIONS.md) — the detailed rationale behind + steps 1-4, one section each. +- [SYSVARS.md](SYSVARS.md) — full Spectrum ↔ zx81sd sysvar and I/O + port equivalence table. +- [BASIC_CHANGES.md](BASIC_CHANGES.md) — catalog of source changes + already needed in official examples. +- [USAGE.md](USAGE.md) — compiling, packaging, and simulating in + detail. +- [MAP.md](MAP.md) — full technical log, bug by bug, with the + investigation traces (the primary source almost every step above + was distilled from). diff --git a/src/arch/zx81sd/doc/PRECAUTIONS.md b/src/arch/zx81sd/doc/PRECAUTIONS.md new file mode 100644 index 000000000..b5b41f8f8 --- /dev/null +++ b/src/arch/zx81sd/doc/PRECAUTIONS.md @@ -0,0 +1,293 @@ +# Precautions when writing or porting software for zx81sd + +zx81sd makes ZX BASIC generate binaries that "look like" a Spectrum +(the SD81 Booster interface emulates its screen, and much of the +shared stdlib assumes Spectrum conventions), but **there is no +Spectrum ROM anywhere**: no ROM `RST $28`, no routines at fixed +addresses, no Spectrum sysvars at `$5C00+`. Almost every bug in this +port has come from code (official `examples/` or the shared stdlib) +that silently assumes one of these things. Check this list before +porting anything. + +## 1. There's never a ROM: watch out for absolute addresses and sysvars + +Any `POKE`/`PEEK`/`CALL` to a fixed numeric address (23675, 23658, +$22AC, $0DFE...) is almost certainly a **Spectrum ROM** sysvar or +routine, which doesn't exist in zx81sd: that address lands in free RAM, +or worse, in the middle of the program's own compiled code — executing +or interpreting it as data causes silent corruption, wrong graphics, or +a runaway `HALT`/reset that's very hard to trace back to its cause +(several bugs in this port took whole sessions to diagnose because of +this). + +- **Spectrum sysvars → zx81sd sysvars**: the full equivalence table + (every symbol, its Spectrum ROM address, and its zx81sd address) is + in [SYSVARS.md](SYSVARS.md) — check it first when a numeric address + turns up while porting code. The zx81sd definitions themselves live + in [`../../../lib/arch/zx81sd/runtime/sysvars.asm`](../../../lib/arch/zx81sd/runtime/sysvars.asm) + (all at `$8000+`, not `$5C00+`). Examples already solved: + `UDG` (23675 → `$8002`), `COORDS` (23677/23678 → `$8004`/`$8005`). + See [BASIC_CHANGES.md](BASIC_CHANGES.md) for the line-by-line detail + of every case found so far. +- **ROM routines called directly** (`call $22AC` = PIXEL-ADD, + `call $0DFE` = CL-SC-ALL/scroll, `RST $28` = FP calculator...): if the + source or a shared library does this, it needs an override in + `src/lib/arch/zx81sd/` that replaces the call with our own routine, + keeping the **same register contract** as the ROM one (see below). + Example already solved: `stdlib/scroll.bas`. +- **Preventive `grep`**: when porting a file from `zx48k/` to + `zx81sd/`, search for `EQU 0[0-9A-F]` / `call 0x` / suspicious + 4-5-digit literals before signing off on it, not only once something + actually fails. + +## 2. ASM routine register contracts are sacred + +Several runtime routines have explicit, non-negotiable register +preservation contracts, because the code that calls them (inherited +from zx48k, not something we can touch) depends on them to the letter. +Examples: + +- `PIXEL_ADDR` (`runtime/pixel_addr.asm`): A=191, B=Y, C=X → HL=offset, + A=X AND 7; **destroys B, preserves D and E**. `draw.asm` stores the + Bresenham coordinate in D around the call, trusting this literally — + breaking it (as happened once with an attempt that used D as + scratch) corrupts every line with a vertical component while leaving + horizontal ones untouched, which is very misleading to diagnose. +- `GetBankPreservingRegs`/`SetBankPreservingINTs` (MSFS, + `cb/maskedsprites.bas`): documented contract "preserves D,E,H,L". + Writing the replacement in plain BASIC instead of hand-written ASM + breaks this with no warning from the compiler (compiled BASIC code + uses registers freely internally) — a real bug of this kind made all + 6 test sprites register at the same wrong address. **Note**: the + MSFS/`maskedsprites.bas` port is still in progress (still not fully + working) — this specific example is here to illustrate the class of + bug, not to confirm the library is finished. + +**Practical rule**: if you're going to replace an ASM routine that has +a documented register contract (or one that can be inferred from +looking at its callers and what they assume), reimplement it in +hand-written ASM preserving that exact contract. A BASIC function +(`SUB`/`FUNCTION`), however simple it looks, is NOT a valid replacement +unless the contract is "none". + +## 3. The keyboard is the ZX81's, not the Spectrum's + +The SD81 Booster doesn't have a Spectrum keyboard: it rescans the +ZX81's own physical 40-key keyboard (`runtime/io/keyboard/keyscan.asm`). +Differences that matter when porting/writing code: + +- Direct key press: lowercase. `SHIFT+letter`: UPPERCASE of that letter + — exactly like a real Spectrum, unlike the original ZX81 (where + `SHIFT+letter` produced a symbol, not an uppercase letter). This + redefinition is a design decision of this port, see [MAP.md](MAP.md) + section "New keyboard scheme". As a result, **you can indeed + scan/compare `INKEY$` against uppercase letters** + (`IF INKEY$="S"`, meant to be played holding `SHIFT` Spectrum-style) + with no hardware limitation: just press `SHIFT+S`. +- `SHIFT+"2"` toggles a persistent CAPS LOCK. +- `"."` is a normal key (`.` unshifted, `,` shifted). The original + ZX81 symbols associated with each key (`:` on Z, `)` on O, etc.) are + reached with the `"." + key` sequence **only from `INPUT()`** (a + dead-key handled in `stdlib/input.bas`), not from raw `INKEY$` — there + is no reliable way to "hold both at once" on this keyboard, so the + composition is done by pressing `.` first and the second key after. + Pressing `.` **twice in a row** confirms the first dot as a literal + and cancels any pending combo: the key that comes after that second + dot is read as a fresh keypress, with no combination (lets you type + any key right after a dot without risking forming a symbol by + accident). + +## 4. There are no interrupts: never `EI`, never wait on `HALT` to synchronize + +The zx81sd runtime always runs with interrupts disabled (`DI`) for the +**entire program**, not just around specific routines. Any ported code +(typically inline ASM copied from a Spectrum source, not the stdlib) +that does `EI` — anywhere, even if it's never followed by a `HALT` — +is dangerous on zx81sd in two different ways: + +- `EI` followed by `HALT` waiting for the Spectrum ROM's 50Hz pulse + **hangs forever** — nothing ever wakes it up. +- `EI` **on its own**, with no following `HALT`, is more insidious: + interrupts stay enabled for the rest of the program's execution, and + the ZX81 fires IM1 INTs continuously (roughly every scanline, tied + to video refresh). Every one of those jumps to `$0038`. That vector + preserves `AF` (`push af` / wait for VSYNC / `pop af` / `ret` — see + `backend/main.py`) precisely because this bug was found the hard + way, but it does **not** preserve anything else — if the interrupt + lands mid-computation in code that's mid-flight on `BC`/`DE`/`HL` + (typical of `__ARRAY`'s offset arithmetic, which uses the shadow + register set across an extended, uninterruptible-on-a-real-Spectrum + span), the result is silent, apparently-random data corruption whose + symptom shows up somewhere completely unrelated later. This exact + bug cost a multi-hour live-hardware debugging session on a ported + game (`sprite(N, col)` reading `-1` instead of its real value) before + being traced back to a stray `EI` at the end of an XOR-blit routine + — full writeup in [MAP.md](MAP.md) and the `oilpanic-portability` + project memory. **Grep every ported ASM block for `\bei\b` before + testing, the same way you'd grep for hardcoded addresses (section + 1).** A bug that reliably disappears when single-stepping in the + debugger but reproduces at full speed is the signature to watch for + — EightyOne doesn't deliver INTs identically while paused. + +- **Replacement**: `VSYNC_TICK` (namespace `core`, in + `runtime/vsync.asm`) polls the SD81 Booster hardware's real VSYNC + pulse counter through a port. `PAUSE` already uses it internally. +- When calling it from an `ASM ... END ASM` block that isn't already + inside `push namespace core`, you need the full prefix: + `call .core.VSYNC_TICK` (omitting the prefix gives + `Undefined GLOBAL label '.VSYNC_TICK'` — an error already seen more + than once in this port). +- A counter that used to be incremented only by the background + interrupt (`FRAMES`/23672 on the Spectrum) needs to be updated by + hand, calling `VSYNC_TICK` explicitly on every loop iteration that + still needs to wait, not just once at the start. + +## 5. ASM label namespaces and mangling + +A BASIC `DIM X` or `SUB`/`FUNCTION X` translates to the ASM label `_X` +(a single leading underscore), **unless** the file wraps its code in +`push namespace core ... pop namespace`, in which case you must +reference it from outside as `.core._X` (variables) or `.core.X` +(functions/routines). Getting this wrong in either direction produces +`Undefined GLOBAL label`. If a file in this port doesn't use +namespacing anywhere else, there's no need to wrap a new `ASM` block in +`push namespace core` just because another file (like `vsync.asm`) +does — just prefix the specific reference. + +## 6. The dead-code eliminator can't see calls made from hand-written ASM + +The compiler's "is this used?" analysis only counts calls made with +BASIC call syntax (`Foo(x)`). A BASIC `SUB`/`FUNCTION`/variable +referenced **only** from an `ASM ... END ASM` block (e.g. `call _Foo`) +can be eliminated as dead code, producing `Undefined GLOBAL label +'._Foo'` at link time — the symbol never made it into the final +binary. Two ways out: + +- If it's pure data (a state byte, for example), declare it as raw ASM + (`ASM \n _Label: \n DEFB 0 \n END ASM` at file scope), not as `DIM`. +- If it's a function that genuinely needs to be BASIC (because it + calls other stdlib things), add one real BASIC-level call (even if + redundant/not strictly needed at that point) somewhere reachable in + the code, so the usage analysis counts it. + +## 7. Debugging methodology without hardware + +To diagnose things without burning trial-and-error cycles on the +emulator or real hardware, this port uses direct simulation of the +binary with Python's `z80` package. Two lessons already learned the +hard way (documented in more detail in [MAP.md](MAP.md)): + +- Checking the PC periodically every N coarse ticks can give a false + "stuck" reading if it just happens to always land on the same point + of a loop; use real breakpoints (compare `m.pc` against the exact + address, taken from the `.map`) or finer tick chunks. +- The simulator's RAM starts at **zero**, just like a Spectrum's after + its ROM's RAM test. This hides uninitialized-memory bugs (it gave two + false "OK" results in a row in the MSFS free-space-bitmap bug). To + validate code that reads memory it doesn't initialize itself, fill + all unloaded RAM with `0xFF` before loading the binary, to reproduce + real hardware/card conditions. +- The simulator **doesn't model the memory mapper** (`OUT` to port + `$E7` is a no-op): it can validate that the Z80 logic is + self-consistent, but not that the actual physical page-swapping + works — that's only confirmed on the emulator (EightyOne) or real + hardware. +- If a binary fails on EightyOne but the Python simulation runs it + clean, suspect the emulator first (see the tape-trap bug already + found and fixed in `Eightyone2/src/ZX81/rompatch.cpp`, in the + emulator's own repository, not this one) before the runtime — those + traps don't exist on real hardware. + +## 8. Never do `#include once ` from your own library + +If you need zx81sd's own sysvars (`CHARS`/`UDG`/`ATTR_P`/ +`SCREEN_ADDR`/`SCREEN_ATTR_ADDR`...) from a new `stdlib/*.bas` file, +**don't include it yourself with `#include once `** — just +reference the symbols with the `.core.` prefix (see point 5) and trust +that the rest of the runtime already brought it in. + +`sysvars.asm` drags in `bootstrap.asm` → `charset.asm`, and the latter +does `INCBIN "specfont.bin"` (the complete font, binary bytes, not +assembly text). If your file turns out to be the **first** one to +include `sysvars.asm` in the whole compiled program (easy to happen: a +`#include ` at the top of the user's source is +processed before any `CLS`/`PRINT` that comes later in the text), that +`INCBIN` gets emitted **right at the point in the source file where you +put the `#include`**: + +- Placed at BASIC level (outside an `ASM ... END ASM` block): the + BASIC lexer tries to tokenize those binary bytes as if they were + source text → `error: illegal preprocessor character` on lines that + have nothing to do with the real problem (misattributed to the start + of the file). +- Placed inside an `ASM` block (e.g. at the start of a function body): + the font binary gets emitted literally in the middle of that + function's compiled code — it compiles without error, but the CPU + "executes" those font bytes as if they were instructions the moment + control flow falls there, producing a `HALT` or erratic behavior at + an address that appears completely unrelated to the bug (found while + porting `print42.bas`/`print64.bas`, see + [BASIC_CHANGES.md](BASIC_CHANGES.md)). + +Any real program using your library will almost certainly also use +`CLS`/`PRINT` somewhere, and those routines already require +`sysvars.asm` — so leaving out the `#include` in your file is safe in +practice, not a hack. + +## 9. Relocating a `sub`'s content with a manual `ORG` leaves `@name` behind + +If a program is too big to fit under the `$8000` sysvar/heap boundary +(step 8 of [PORTING_GUIDE.md](PORTING_GUIDE.md)), a large constant data +block (a sprite bitmap table, a level map...) can be banked out of the +way by wrapping it in `ORG $E000` / restore-the-previous-`ORG` inside +its `sub`'s `asm ... end asm` body, moving it into block 7 instead of +the code/sysvar/heap area. This works for the **data bytes** — but +`@name` (used from BASIC to get the sub's address, e.g. `poke @name,x`) +keeps pointing at the sub's **original**, un-relocated position, not +the `ORG`'d one. + +The reason: `@name` resolves to wherever zxbasic's own auto-generated +`_name:` entry label ends up, and that label is emitted as literal ASM +text **immediately before** your `asm` block's own content — i.e. +before your `ORG` line has had any effect. Labels bind to the +assembler's address counter at the point they're textually defined +(ordinary, correct assembler behavior — `zxbasm` isn't doing anything +wrong here), so redirecting the content elsewhere doesn't drag the +label along with it. If nothing else ever gets emitted at the sub's +"natural" position (because everything real was redirected away by the +`ORG`), the auto-generated `_name` and `_name__leave` (end-of-function) +labels end up at the very same address — a telltale sign in the +debugger/disassembly that `@name` is stale: it'll resolve to +`._name_leave`, not to your relocated data. + +Found while banking Mario GW's sprite bitmap table (`sub sprite()`, +~5.4KB) into block 7: `init_sprites()`/`tilexy()`/`tilexyaddr()` all +used `@sprite+N` to read/poke into it, silently computing addresses +against the *old* position once the data itself moved to `$E000`. +Symptom: no error at compile or load time — the game ran, the intro +screen and music played fine, and it hung in an apparently unrelated +infinite loop deep inside the sprite-blit routine (`tylexyasm`, stuck +forever on `jr nz, sigline`), because the wrong addresses eventually +corrupted the `(IX+n)` scratch fields the blit routine's own loop +termination depends on — several steps removed from the actual bug. + +**Workaround**: don't use `@name` for a sub whose content you've +manually `ORG`'d elsewhere — use the literal target address directly +(e.g. `$E000`) everywhere `@name` was used, under `#ifdef __ZX81SD__`. +The `sub` declaration itself must stay (it's still what emits the +relocated bytes), but since nothing references it via `@name` any more, +guard it against the dead-code eliminator (section 6 above) with a +throwaway global reference, e.g. +`dim __keep as uinteger: __keep = @name`. + +## See also + +- [PORTING_GUIDE.md](PORTING_GUIDE.md) — step-by-step checklist for + porting a Spectrum program, built from everything in this file. +- [SYSVARS.md](SYSVARS.md) — full Spectrum ↔ zx81sd sysvar and I/O + port equivalence table. +- [BASIC_CHANGES.md](BASIC_CHANGES.md) — catalog of source changes + already needed in official examples, with the general pattern to look + for in any new example. +- [MAP.md](MAP.md) — full technical log, bug by bug, with the + investigation traces. diff --git a/src/arch/zx81sd/doc/SYSVARS.md b/src/arch/zx81sd/doc/SYSVARS.md new file mode 100644 index 000000000..85fdf411e --- /dev/null +++ b/src/arch/zx81sd/doc/SYSVARS.md @@ -0,0 +1,165 @@ +# zx81sd system variables — equivalence table with the Spectrum ROM + +zx81sd has no ROM: every "system variable" the shared `zx48k/runtime/` +code expects at a fixed Spectrum address is instead a real RAM +variable allocated by zx81sd at `SYSVAR_BASE` (`$8000`) upwards, laid +out in [`src/lib/arch/zx81sd/runtime/sysvars.asm`](../../../lib/arch/zx81sd/runtime/sysvars.asm). +This table is the single place that maps each one back to the +Spectrum ROM address it substitutes for, so a search for a Spectrum +address (`23698`, `$5C7B`, etc.) found while porting code has +somewhere to land. + +## Quick reference (the things every port hits first) + +| Concept | Spectrum | zx81sd | Source | +|---------|:--------:|:------:|--------| +| Pixel screen base | `$4000` (16384) | `$C000` (49152) | `SCREEN_ADDR`, see below | +| Attribute screen base | `$5800` (22528) | `$D800` (55296) | `SCREEN_ATTR_ADDR`, see below | +| ULA port (border color, `OUT`) | `$FE` (254) | `$FB` (251) | `border.asm` — `SD81_ULA_PORT` | +| ULA port (beeper, `OUT`) | `$FE` (254), bit 4 | `$FB` (251), bits 4-3 | `beep.asm` — same port as border, see note below | +| AY register-latch port (`OUT`) | `$FFFD` (65533, Spectrum 128) | `$CF` (207) | `stdlib/play.bas` — `_PLAY_WRITE_TO_REGISTER` | +| AY data port (`OUT`) | `$BFFD` (49149, Spectrum 128) | `$0F` (15) | ditto — SD81 Booster's ZonX-81 AY interface | + +The ULA port note: on the real Spectrum, border color (bits 2-0) and +beeper (bit 4) share the **same** write-only port `$FE`, so any code +poking a raw byte to it needs to preserve the other's bits. zx81sd's +SD81 Booster (Superfast HiRes Spectrum mode) emulates this exact +sharing at `$FB` instead, bits laid out the same way (2-0 border, +4-3 beeper) — `border.asm`/`beep.asm` keep a shadow byte +(`__ZX81SD_ULA_SHADOW`) for the same reason the real ROM effectively +needs one. **If a ported program pokes the ULA port directly instead +of using `BORDER`/`BEEP`, change `$FE`→`$FB` (or `254`→`251`) and route +it through the shadow byte, not a raw `OUT`** — see +[PORTING_GUIDE.md](PORTING_GUIDE.md) step 3. + +Note: `$FE` is *also* a real, working port on zx81sd — but for the +**native ZX81 keyboard matrix** (`break.asm` reads it directly for the +BREAK key), not the Spectrum ULA. A Spectrum program that does its own +manual keyboard scanning via `IN A,($FE)` (bypassing `INKEY$`) will +read a **different** row/bit layout on zx81sd's actual hardware — that +port number being valid on both machines for unrelated purposes is a +coincidence worth being aware of, not a compatibility shortcut. + +The AY port note: besides the port numbers themselves, the AY chip's +**clock speed differs too** — the SD81 Booster's FPGA clocks its AY at +1.625 MHz (3.25 MHz / 2) versus the Spectrum 128's 1.7734 MHz. A +program that pokes raw note-divider values into AY registers 0-13 +(rather than going through `PLAY`/`SetChipTone` etc.) needs those +dividers recalculated for the new clock (`divider = round(1625000 / +16 / frequency)`), or every note will play at the wrong pitch — see +`stdlib/play.bas`'s `_Play_NoteDividers` table for the reference +conversion, and the closed `sd81-sound-calibration` project memory for +how this was measured and confirmed on real hardware (BEEP runs off a +separate, unrelated 3.25 MHz clock — don't confuse the two chips' +clocks when converting timings). + +See [PRECAUTIONS.md](PRECAUTIONS.md) section 1 for *why* this matters +(zx81sd has no ROM at all — a numeric literal like `23675` is never +valid on zx81sd, it lands inside the program's own compiled code) and +section 8 for how to safely reference these from a new library file +(`#require "sysvars.asm"`, never `#include once`). + +## Main block (mirrors `zx48k/runtime/sysvars.asm`) + +These are the ones the shared zx48k runtime/stdlib code accesses **by +symbolic name** (`CHARS`, `UDG`, `ATTR_P`...) — since the symbol +resolves against whichever `sysvars.asm` got linked in, zx81sd's own +definitions satisfy them automatically, no override file needed. + +| Symbol | Spectrum ROM addr | zx81sd addr | Size | Purpose | +|-------------|:-----------------:|:-----------:|:----:|---------| +| `CHARS` | 23606 (`$5C36`) | `$8000` | 2B (DW) | Pointer to charset (8×8) | +| `UDG` | 23675 (`$5C7B`) | `$8002` | 2B (DW) | Pointer to UDG charset | +| `COORDS` | 23677 (`$5C7D`) | `$8004` | 2B (DW) | Last `PLOT` coordinate (X,Y) | +| `FLAGS2` | 23681 (`$5C81`) | `$8006` | 1B | Screen flags (OVER/INVERSE/etc.) | +| `ECHO_E` | 23682 (`$5C82`) | `$8007` | 1B | (reserved, unused by zx81sd) | +| `DFCC` | 23684 (`$5C84`) | `$8008` | 2B (DW) | Next screen (bitmap) address for `PRINT` | +| `DFCCL` | 23686 (`$5C86`) | `$800A` | 2B (DW) | Next screen-attribute address for `PRINT` | +| `S_POSN` | 23688 (`$5C88`) | `$800C` | 2B | Cursor position (H=row, L=col) | +| `ATTR_P` | 23693 (`$5C8D`) | `$800E` | 1B | Permanent attribute (INK/PAPER/...) | +| `MASK_P` | 23694 (`$5C8E`) | `$800F` | 1B | Permanent mask (implicit `ATTR_P+1`, not a separate EQU on either side) | +| `ATTR_T` | 23695 (`$5C8F`) | `$8010` | 1B | Temporary attribute | +| `MASK_T` | 23696 (`$5C90`) | `$8011` | 1B | Temporary mask (implicit `ATTR_T+1`) | +| `P_FLAG` | 23697 (`$5C91`) | `$8012` | 1B | Print flags (permanent OVER/INVERSE) | +| `MEM0`/`MEMBOT` | 23698 (`$5C92`) | `$8013` | 5B | Temp buffer used by ROM char routines. **Same physical Spectrum address as `MEMBOT`** below — the real ROM reuses this scratch for two unrelated purposes depending on context; zx81sd keeps them as two clearly-named, non-overlapping zones instead (`MEM0` here, `ARRAY_SCRATCH` separately) | +| `TV_FLAG` | 23612 (`$5C3C`) | `$8018` | 1B | Output-to-screen control flags | + +Not in the shared file, zx81sd-only additions used by the same +symbolic-name mechanism: + +| Symbol | Nearest Spectrum concept | zx81sd addr | Size | Notes | +|-------------|--------------------------|:-----------:|:----:|-------| +| `ERR_NR` | `ERR_NR` — 23610 (`$5C3A`) | `$8019` | 1B | Error code (-1 = no error) | +| `FRAMES` | `FRAMES` — 23672 (`$5C58`), 3B on real ROM | `$801A` | 2B (DW) | Software VSYNC frame counter — zx81sd has no hardware interrupt to auto-increment it (see `VSYNC_TICK`/`PAUSE`), only needs 16 bits | +| `RANDOM_SEED_LOW` | `SEED` low word — 23670 (`$5C76`) | `$801C` | 2B (DW) | RNG seed | +| `SCREEN_ADDR` | not a ROM sysvar — zxbasic's own indirection variable (`zx48k/runtime/sysvars.asm`: `DW 16384`) | `$801E` | 2B (DW) | Framebuffer pointer, init `$C000` (vs `16384` on Spectrum) | +| `SCREEN_ATTR_ADDR` | ditto (`DW 22528`) | `$8020` | 2B (DW) | Attribute pointer, init `$D800` (vs `22528`) | + +## Floating-point calculator (`fp_calc.asm`) + +Real Spectrum ROM sysvars, but zx81sd points them at a **fixed** +buffer instead of the ROM's dynamically-growing area (there's no +"free memory between program and stack" concept here): + +| Symbol | Spectrum ROM addr | zx81sd addr | Size | Notes | +|---------------|:-------------------:|:-----------:|:----:|-------| +| `FP_STKBOT` | `STKBOT` — 23651 (`$5C63`) | `$8022` | 2B (DW) | Base of the FP number stack | +| `FP_STKEND` | `STKEND` — 23653 (`$5C65`) | `$8024` | 2B (DW) | Next free slot in the FP stack | +| `FP_BREG` | `BREG` — 23655 (`$5C67`) | `$8026` | 1B | Literal currently being parsed | +| `FP_MEM` | `MEM` — 23656 (`$5C68`) | `$8027` | 2B (DW) | Pointer to the MEM area (6×5B cells) | +| `FP_CALC_STACK` | n/a (dynamic on real ROM) | `$8029` | 60B | Fixed FP number stack (12 numbers max) | +| `FP_MEM_AREA` | n/a (dynamic on real ROM) | `$8065` | 30B | Fixed MEM area (6×5B cells) | + +`FP_BREG` **must** sit immediately after `FP_STKEND` — the CALCULATE +engine (`ENT-TABLE`/`fp-calc-2`/`dec-jr-nz`) exploits the real ROM's +memory contiguity (`STKEND_hi` immediately followed by `BREG`) to load +both with a single `LD BC,(FP_STKEND+1)`. Don't reorder these two. + +## Hardcoded-address scratch fixes (not exposed by symbolic name) + +These shared `zx48k/runtime/` files bypass `sysvars.asm` entirely and +hardcode a raw Spectrum address as scratch storage — on zx81sd that +address lands inside the program's own compiled code (block 2, +`$4000-$5FFF`) instead of safe RAM, silently corrupting it. Each one +needed its own zx81sd **override file** (only the address changed, see +[MAP.md](MAP.md) for the full incident writeups) rather than a plain +`sysvars.asm` entry, since the shared code never references them by a +name `sysvars.asm` could satisfy: + +| Symbol (shared file's own name) | Spectrum addr | zx81sd override | zx81sd addr | +|----------------------------------|:-------------:|------------------|:-----------:| +| `LBOUND_PTR`/`UBOUND_PTR`/`RET_ADDR`/`TMP_ARR_PTR` (`array.asm`, aliases `MEMBOT`) | 23698 (`$5C92`) | `runtime/array/array.asm` | `ARRAY_SCRATCH` = `$8083` (8B) | +| `TMP` (`chr.asm`, alias `DEST`) | 23629 (`$5C4D`) | `runtime/chr.asm` | `CHR_SCRATCH` = `$808B` (2B) | +| `TMP`/`ERR_SP` (`arith/divf.asm`, aliases `DEST`/`ERR_SP`) | 23629 (`$5C4D`) / 23613 (`$5C3D`) | `runtime/arith/divf.asm` | `DIVF_SCRATCH` = `$808D` (4B: TMP 2B + ERR_SP 2B) | + +Total sysvar block size: `$91` bytes (`$8000`-`$8090`), well before the +heap start at `$8100`. + +**Not yet audited / no override exists** (same antipattern, not yet +observed corrupting a real program — see `oilpanic-portability.md` +project memory and `MAP.md` for the hunting method if one of these +ever bites): + +| Symbol | Spectrum addr | Shared file | Only triggered by | +|--------|:-------------:|-------------|--------------------| +| `TEMP` (alias `MEMBOT`) | 23698 (`$5C92`) | `runtime/arith/modf16.asm` | the `MOD` operator | + +A quick grep for the pattern across all of `zx48k/runtime`: +`EQU 2[34][0-9][0-9][0-9]` — anything that turns up and *doesn't* +already have a zx81sd override (see next section) is a candidate. + +## Shared files with hardcoded Spectrum addresses that zx81sd already +## bypasses entirely (full rewrite, not just an address patch) + +These also matched the grep above, but zx81sd ships a **complete** +override with no numeric Spectrum address left at all (not just a +scratch-address swap) — listed here so they're not mistaken for +unaudited risks: + +| Shared file | Hardcoded Spectrum addr(s) | zx81sd override | +|-------------|------------------------------|------------------| +| `runtime/break.asm` (`PPC`) | 23621 (`$5C45`) | `zx81sd/runtime/break.asm` | +| `runtime/load.asm` (`BREG` alias) | 23655 (`$5C67`) | `zx81sd/runtime/load.asm` | +| `runtime/save.asm` (`MEMBOT` alias) | 23698 (`$5C92`) | `zx81sd/runtime/save.asm` | +| `runtime/val.asm` (`STKBOT`/`ERR_SP`/`CH_ADD`) | 23651/23613/23645 | `zx81sd/runtime/val.asm` | +| `runtime/random.asm` (`RANDOM_SEED_LOW`) | 23670 (`$5C76`) | `zx81sd/runtime/random.asm` | diff --git a/src/arch/zx81sd/doc/USAGE.md b/src/arch/zx81sd/doc/USAGE.md new file mode 100644 index 000000000..a7c33137d --- /dev/null +++ b/src/arch/zx81sd/doc/USAGE.md @@ -0,0 +1,133 @@ +# Usage: compiling, packaging and loading a program on zx81sd + +## 1. Compile + +From the root of this repository: + +``` +python -m src.zxbc.zxbc --arch zx81sd -o -M +``` + +- `--arch zx81sd` selects this architecture's backend and overrides + (see [../README.md](../README.md) — the port's golden rule). +- `-M ` is optional but strongly recommended: it generates + the symbol map (address of every ASM/BASIC label), essential for + debugging with an emulator or simulating the binary (see + [MAP.md](MAP.md) for examples of Python simulation harnesses). +- Examples transcribed from classic Sinclair BASIC (1-based + arrays/strings) usually also need `--string-base 1 --array-base 1` + (see [BASIC_CHANGES.md](BASIC_CHANGES.md), `comecoquitos.bas` example). + +There's also `python zxbc.py --arch zx81sd -f bin -o ` +(the traditional entry-point script); both forms are equivalent. + +## 2. Package for the ZX81 + +The binary produced by the compiler is a flat image of up to 48KB +(blocks 0-5 of the SD81 mapper). The ZX81 can only load, in one go, +whatever fits in its own visible RAM, so that binary has to be split +into 8KB pages and a BASIC loader generated that feeds them one by one +into the SD81 Booster card, remapping the memory mapper between pages. + +That's what [`../tools/split_sd81.py`](../tools/split_sd81.py) does: + +``` +python src/arch/zx81sd/tools/split_sd81.py [PREFIX] +``` + +- `PREFIX` (optional) is the base name for the output files, in the + ZX81 charset (letters, digits, no underscore). If omitted, it's + derived from the input `.bin` file name. +- It generates: + - `P8.BIN`, `P9.BIN`, ... — one per 8KB of the binary + (SD81 page 8 = block 0, page 9 = block 1, etc.) + - `_loader.txt` — the loader's BASIC listing in plain text + (for reading/debugging). + - `.P` — the same loader, already tokenized, ready to load + and run on the ZX81 from the SD card. + +The generated loader uses `LOAD THEN CLEAR`, `LOAD *MAP` and +`LOAD FAST ... CODE`, extensions provided by the SD81 Booster firmware +(they don't exist in the original ZX81 ROM). It does the following, in +order: + +1. Reserves memory (`CLEAR`) and loads `BOOT1.BIN` (the stage 1 loader, + fixed for every program — source in + [`../tools/boot1.asm`](../tools/boot1.asm), already-assembled binary + in [`../tools/boot1.bin`](../tools/boot1.bin)). +2. For each page of the binary: maps block 7 to that physical page + (`LOAD *MAP 7,`) and dumps the data there (`LOAD FAST ... CODE + 57344`, block 7's window at `$E000`). +3. When done, leaves the mapper in "full page" mode (`LOAD *MAP + 7,63`) — from this point on the mapper doesn't go back to simple + mode until the next reset — and jumps to `BOOT1.BIN` (`RAND USR + 24576`), which does the final mapping of blocks 0-5 and starts the + program. + +## 3. Copy to the SD card + +Copy to the SD card, alongside the rest of your collection: + +- `BOOT1.BIN` (only once, it's the same for every program) +- All the `P.BIN` files for the program +- `.P` + +Then on the ZX81 (or in EightyOne pointing at the SD image): load and +run it with `LOAD FAST ""` — no need to select anything +afterwards, it runs straight from there. + +## 3b. Steps 1+2 in one command: `build_sd81.py` + +[`../tools/build_sd81.py`](../tools/build_sd81.py) chains the compile +(step 1, including `-M`) and the packaging (step 2) into a single +call, and can optionally copy the result straight to an emulator's +virtual SD folder: + +``` +python src/arch/zx81sd/tools/build_sd81.py [PREFIX] [--copy-to DIR] +``` + +`__ZX81SD__` is always passed as `-D` (needed for game sources that +guard architecture-specific code with `#ifdef __ZX81SD__`, see +[BASIC_CHANGES.md](BASIC_CHANGES.md)); extra `-D MACRO` or unrecognized +flags are forwarded to `zxbc.py` as-is. `--copy-to` doesn't copy +`BOOT1.BIN` (shared across every program, usually already in place). + +A stale package the emulator doesn't actually see was itself the cause +of a multi-hour false-alarm debugging session once (see MAP.md / +`oilpanic-portability` project memory) — `--copy-to` exists mainly to +make that mistake harder to repeat. + +## 3c. Regenerating just the `.map`, via the `.asm` stage: `gen_map.py` + +Sometimes the `.asm` intermediate is worth keeping around for +inspection or hand-editing, and only a fresh, matching `.map` is +needed afterwards — without paying for `split_sd81.py`'s page-split + +`.P`-tokenize step again. [`../tools/gen_map.py`](../tools/gen_map.py) +does the two-stage version of step 1 explicitly: + +``` +python src/arch/zx81sd/tools/gen_map.py [output_base] +``` + +1. `zxbc.py --arch=zx81sd -D __ZX81SD__ -f asm` (BASIC → `.asm`) +2. `zxbasm.py -M` (`.asm` → `.bin` + `.map`; a `.bin` comes out too, + zxbasm always needs some output format) + +Note: `zxbasm.py`, unlike `zxbc.py`, has no `--arch`/include-path flag +of its own, so it can't resolve `charset.asm`'s bare +`INCBIN "specfont.bin"` through the normal architecture search path — +`gen_map.py` works around this by temporarily copying `specfont.bin` +next to the generated `.asm` before invoking `zxbasm.py`, then +removing it again. + +## 4. Debugging without hardware: simulation with Python + +To diagnose hangs, HALTs or incorrect results without having to test on +the emulator or real hardware every time, this port's development has +used Python's `z80` package (`pip install z80`) to simulate the flat +binary directly. The full methodology (including one important trap: +the simulator's RAM starts at zero, which can hide uninitialized-memory +bugs that do show up on real hardware) is documented in +[MAP.md](MAP.md) — the "Heap at $8100 + EightyOne tape traps" section +and the methodology notes on the MSFS/maskedsprites bug. diff --git a/src/arch/zx81sd/tools/boot1.asm b/src/arch/zx81sd/tools/boot1.asm new file mode 100644 index 000000000..6ed9ea28c --- /dev/null +++ b/src/arch/zx81sd/tools/boot1.asm @@ -0,0 +1,104 @@ +; =========================================================================== +; bootstrap_stage1.asm — Stage 1 Bootstrap ZX81 + SD81 Booster +; +; Resides at $6000 (block 3). The BASIC loader sends it there before +; anything else because it's a neutral area: the ZX81's BASIC doesn't +; need it, and no block has been remapped yet. +; +; Startup sequence (see ZX81-SD81-Adaptation-Plan.md): +; 1. DI — disable interrupts (ZX81 FAST mode already does this, but +; it's repeated as a precaution before touching paging) +; 2. Activate Superfast HiRes Spectrum mode via the FPGA (POKE 2045 = $07FD) +; 3. Activate Chroma81 colour mode (port $7FEF) -- required separately +; from step 2 on real hardware, see note below +; 4. Disable memory-mapped IO (POKE 2056 = $0808) +; 5. Map block 0 -> page 8 (OUT ($E7), page=8, block=0) +; Stage 2 ($0100-$0FFF on page 8) is now ready to run. +; 6. JP $0100 — jumps to stage 2 in the freshly mapped RAM +; +; Notes: +; - HFILE=$C000 is the FPGA's default value when activating mode 172. +; If your hardware requires setting it explicitly, uncomment the +; HFILE block at the end. +; - Chroma81 colour (step 3): activating Spectrum mode (step 2) does +; NOT turn color on by itself on real hardware -- confirmed on a +; real SD81 Booster (2026-07-05); the EightyOne emulator shows +; color regardless of this step, which is a known emulator bug +; (color should also require this OUT, to match real hardware). +; - Stage 1 does NOT initialize SP; stage 2 does (ld sp, $7FFF). +; - Blocks 1-5 are mapped in stage 2 (already running from page 8). +; +; Compile: +; zxbasm bootstrap_stage1.asm -o bootstrap_stage1.bin +; or with pasmo: +; pasmo --bin bootstrap_stage1.asm bootstrap_stage1.bin +; +; Load on the emulator / hardware at address $6000 (24576 decimal). +; Run from BASIC with: +; RANDOMIZE USR 24576 +; =========================================================================== + + org $6000 + +SD81_STAGE1: + + di ; Interrupts disabled + + ; -------------------------------------------------------------------------- + ; Set the framebuffer address (HFILE) + ; high=2044 low=(2043) + ; + + ld hl, $C000 + ld ($07FB), hl + + ; -------------------------------------------------------------------------- + ; ------------------------------------------------------------------ + ; Activate Superfast HiRes Spectrum mode + ; POKE 2045, 172 -> ld a, 172 / ld ($07FD), a + ; The SD81 Booster's FPGA interprets this as: + ; mode 172 ($AC) = Spectrum 256x192 from HFILE=$C000 + ; ------------------------------------------------------------------ + ld a, 172 + ld ($07FD), a + + ; ------------------------------------------------------------------ + ; Activate Chroma81 colour mode + ; Activating Spectrum mode above does NOT turn color on by itself + ; on real hardware (confirmed on real SD81 Booster; the EightyOne + ; emulator shows color regardless, which is an emulator bug to + ; report/fix there). Chroma81 is a separate port ($7FEF) that must + ; be set explicitly. + ; ------------------------------------------------------------------ + ld bc, $7FEF ; Chroma81 port: set known state + ld a, 39 ; bit5=1 color on, bit4=0 char-code mode + out (c), a + + ; ------------------------------------------------------------------ + ; Disable memory-mapped IO + ; POKE 2056, 0 -> ld ($0808), a + ; Avoids collisions between IN/OUT instructions and the RAM space + ; ------------------------------------------------------------------ + xor a + ld ($0808), a + + ; ------------------------------------------------------------------ + ; Map block 0 ($0000-$1FFF) -> SD81 page 8 + ; Port $E7: full 64-page mode, OUT (C), A with B=page, A=block + ; Page 8 holds the compiled binary (vectors + stage 2 + runtime) + ; ------------------------------------------------------------------ + ld b, 8 ; destination SD81 page (8 = first user page) + ld a, 0 ; Z80 block to remap (block 0 = $0000-$1FFF) + ld c, $E7 ; SD81 paging port + out (c), a ; map (B=page, A=block) + + ; ------------------------------------------------------------------ + ; Jump to stage 2 in the freshly mapped RAM + ; From here on, block 0 holds page 8: + ; $0000-$0067 RST vectors + ; $0100 __START_PROGRAM (stage 2's entry point) + ; ------------------------------------------------------------------ + jp $0100 + + + end SD81_STAGE1 diff --git a/src/arch/zx81sd/tools/boot1.bin b/src/arch/zx81sd/tools/boot1.bin new file mode 100644 index 000000000..bbaa78a56 Binary files /dev/null and b/src/arch/zx81sd/tools/boot1.bin differ diff --git a/src/arch/zx81sd/tools/build_sd81.py b/src/arch/zx81sd/tools/build_sd81.py new file mode 100755 index 000000000..8ce978cb1 --- /dev/null +++ b/src/arch/zx81sd/tools/build_sd81.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +""" +build_sd81.py — one-shot build: BASIC source -> zx81sd binary -> SD81 +Booster package (page files + tokenized .P loader), in a single command. + +Chains the two steps that had to be run by hand throughout zx81sd +development: + 1. zxbc.py --arch=zx81sd -D __ZX81SD__ -o .bin -M .map + (compiles the BASIC source; -M keeps the symbol map in sync with + the binary — a stale/missing .map was a repeated source of + confusion when live-debugging on real hardware/emulator) + 2. split_sd81.py .bin (splits into 8KB SD81 pages and + generates .P, the tokenized BASIC loader) + +Usage: + python build_sd81.py [output_base] [--copy-to DIR] [--asm] + [-O LEVEL] [-D MACRO ...] [zxbc.py extra args...] + + output_base defaults to the source file's name (without extension), + uppercased — same convention split_sd81.py already enforces (must be + representable in the ZX81 charset: letters/digits, no underscore). + + --copy-to DIR: after building, also copy the page files, the .P + loader, and boot1.bin (from this tools/ folder) to DIR (e.g. an + emulator's virtual-SD test folder). + + --asm: also emit .asm, the generated assembly listing (a second + zxbc.py pass with -f asm, same source/optimize/defines). Off by + default (it's a much bigger file than the .map and rarely needed) — + turn it on when tracing a bug live against the .map/disassembly, to + see the actual instructions around a given address instead of just + its label. The .map (symbol addresses) is still always generated, + unconditionally, as before. + + __ZX81SD__ is always defined; pass more -D flags to add extras. + Any unrecognized argument is forwarded to zxbc.py as-is (e.g. to + tweak heap size, warnings, etc.). + +Example: + python build_sd81.py C:/ClaudeCode/ZXOilPanic/oilpanic.bas OILPANIC ^ + --copy-to C:/ClaudeCode/Eightyone2/EightyOne/SD81/test +""" + +import argparse +import shutil +import subprocess +import sys +from pathlib import Path + +TOOLS_DIR = Path(__file__).resolve().parent +ZXBASIC_ROOT = TOOLS_DIR.parents[3] # tools -> zx81sd -> arch -> src -> +ZXBC = ZXBASIC_ROOT / "zxbc.py" +SPLIT_SCRIPT = TOOLS_DIR / "split_sd81.py" + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("source", help="BASIC source file (.bas)") + ap.add_argument("output_base", nargs="?", help="Output base name (default: source file name)") + ap.add_argument("--copy-to", metavar="DIR", help="Also copy the page files + .P loader here") + ap.add_argument("--asm", action="store_true", help="Also emit .asm (the generated assembly listing)") + ap.add_argument("-O", "--optimize", default="2", help="zxbc.py optimization level (default 2)") + ap.add_argument( + "-D", + "--define", + action="append", + default=[], + metavar="MACRO", + help="Extra -D macro for zxbc.py (repeatable). __ZX81SD__ is always defined.", + ) + args, extra = ap.parse_known_args() + + if not ZXBC.is_file(): + sys.exit(f"Error: zxbc.py not found at {ZXBC} (unexpected repo layout)") + if not SPLIT_SCRIPT.is_file(): + sys.exit(f"Error: split_sd81.py not found at {SPLIT_SCRIPT}") + + source = Path(args.source).resolve() + if not source.is_file(): + sys.exit(f"Error: source file not found: {source}") + + base = args.output_base or source.stem + workdir = source.parent + bin_path = workdir / f"{base}.bin" + map_path = workdir / f"{base}.map" + + zxbc_cmd = [ + sys.executable, + str(ZXBC), + str(source), + "--arch=zx81sd", + "-O", + args.optimize, + "-o", + str(bin_path), + "-M", + str(map_path), + ] + for d in ["__ZX81SD__"] + args.define: + zxbc_cmd += ["-D", d] + zxbc_cmd += extra + + print("== zxbc.py ==") + print(" ".join(zxbc_cmd)) + subprocess.run(zxbc_cmd, check=True) + + if args.asm: + asm_path = workdir / f"{base}.asm" + asm_cmd = [ + sys.executable, + str(ZXBC), + str(source), + "--arch=zx81sd", + "-O", + args.optimize, + "-f", + "asm", + "-o", + str(asm_path), + ] + for d in ["__ZX81SD__"] + args.define: + asm_cmd += ["-D", d] + asm_cmd += extra + + print() + print("== zxbc.py (asm) ==") + print(" ".join(asm_cmd)) + subprocess.run(asm_cmd, check=True) + + print() + print("== split_sd81.py ==") + split_cmd = [sys.executable, str(SPLIT_SCRIPT), str(bin_path), base] + print(" ".join(split_cmd)) + subprocess.run(split_cmd, check=True, cwd=workdir) + + if args.copy_to: + dest = Path(args.copy_to) + dest.mkdir(parents=True, exist_ok=True) + base_upper = base.upper() + copied = [] + for pattern in (f"{base_upper}P*.BIN", f"{base_upper}.P"): + for f in workdir.glob(pattern): + shutil.copy2(f, dest / f.name) + copied.append(f.name) + + boot1 = TOOLS_DIR / "boot1.bin" + if boot1.is_file(): + shutil.copy2(boot1, dest / boot1.name) + copied.append(boot1.name) + else: + print(f" Warning: boot1.bin not found at {boot1}") + + print() + print(f"== copied to {dest} ==") + if copied: + for name in sorted(copied): + print(f" {name}") + else: + print(f" Warning: nothing matched {base_upper}P*.BIN / {base_upper}.P in {workdir}") + + +if __name__ == "__main__": + main() diff --git a/src/arch/zx81sd/tools/gen_map.py b/src/arch/zx81sd/tools/gen_map.py new file mode 100755 index 000000000..07158114a --- /dev/null +++ b/src/arch/zx81sd/tools/gen_map.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +""" +gen_map.py — regenerate a zx81sd .map symbol file from BASIC source via +the intermediate .asm stage, instead of compiling straight to .bin. + +Two-stage pipeline (this is what zxbc.py does internally in one step +when given -f bin -M map; this script exposes the split explicitly): + 1. zxbc.py --arch=zx81sd -D __ZX81SD__ -f asm -o .asm + (BASIC -> assembly, kept around for inspection/hand-editing) + 2. zxbasm.py -M .map -o .bin .asm + (assembly -> binary + .map; zxbasm always needs *some* output + format, so a .bin comes out too as a byproduct) + +Useful when the .asm is being hand-inspected or edited between steps +(a common retro workflow) and only a fresh, consistent .map is needed +to match it — skips split_sd81.py's page-splitting / .P-tokenizing +step entirely, since a plain zxbc.py -f bin -M compile already covers +the common "just rebuild everything" case (see build_sd81.py). + +Usage: + python gen_map.py [output_base] [-O LEVEL] [-D MACRO ...] + [--keep-asm] [zxbc.py extra args...] + + output_base defaults to the source file's name (without extension). + --keep-asm: don't delete the intermediate .asm afterwards (kept by + default anyway — flag exists only for symmetry/clarity, see below). + +Example: + python gen_map.py C:/ClaudeCode/ZXOilPanic/oilpanic.bas oilpanic +""" + +import argparse +import shutil +import subprocess +import sys +from pathlib import Path + +TOOLS_DIR = Path(__file__).resolve().parent +ZXBASIC_ROOT = TOOLS_DIR.parents[3] # tools -> zx81sd -> arch -> src -> +ZXBC = ZXBASIC_ROOT / "zxbc.py" +ZXBASM = ZXBASIC_ROOT / "zxbasm.py" + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("source", help="BASIC source file (.bas)") + ap.add_argument("output_base", nargs="?", help="Output base name (default: source file name)") + ap.add_argument("-O", "--optimize", default="2", help="zxbc.py optimization level (default 2)") + ap.add_argument( + "-D", + "--define", + action="append", + default=[], + metavar="MACRO", + help="Extra -D macro for zxbc.py (repeatable). __ZX81SD__ is always defined.", + ) + ap.add_argument( + "--keep-asm", + action="store_true", + help="No-op (the .asm is always kept) — accepted for clarity/symmetry only.", + ) + args, extra = ap.parse_known_args() + + if not ZXBC.is_file(): + sys.exit(f"Error: zxbc.py not found at {ZXBC} (unexpected repo layout)") + if not ZXBASM.is_file(): + sys.exit(f"Error: zxbasm.py not found at {ZXBASM} (unexpected repo layout)") + + source = Path(args.source).resolve() + if not source.is_file(): + sys.exit(f"Error: source file not found: {source}") + + base = args.output_base or source.stem + workdir = source.parent + asm_path = workdir / f"{base}.asm" + bin_path = workdir / f"{base}.bin" + map_path = workdir / f"{base}.map" + + zxbc_cmd = [ + sys.executable, + str(ZXBC), + str(source), + "--arch=zx81sd", + "-O", + args.optimize, + "-f", + "asm", + "-o", + str(asm_path), + ] + for d in ["__ZX81SD__"] + args.define: + zxbc_cmd += ["-D", d] + zxbc_cmd += extra + + print("== zxbc.py (BASIC -> asm) ==") + print(" ".join(zxbc_cmd)) + subprocess.run(zxbc_cmd, check=True) + + zxbasm_cmd = [ + sys.executable, + str(ZXBASM), + str(asm_path), + "-o", + str(bin_path), + "-M", + str(map_path), + ] + + # zxbasm has no --arch/--include-path flag at all (unlike zxbc.py), so + # it never learns zx81sd's architecture-specific include search path. + # A bare INCBIN filename (e.g. "specfont.bin", pulled in by + # charset.asm) is resolved with local_first=True against the .asm + # file's OWN directory — so temporarily drop a copy of specfont.bin + # next to the generated .asm and remove it again afterwards. + specfont_src = ZXBASIC_ROOT / "src" / "lib" / "arch" / "zx81sd" / "runtime" / "specfont.bin" + specfont_dst = workdir / "specfont.bin" + specfont_copied = False + if specfont_src.is_file() and not specfont_dst.exists(): + shutil.copy2(specfont_src, specfont_dst) + specfont_copied = True + + print() + print("== zxbasm.py (asm -> bin + map) ==") + print(" ".join(zxbasm_cmd)) + try: + subprocess.run(zxbasm_cmd, check=True) + finally: + if specfont_copied: + specfont_dst.unlink(missing_ok=True) + + print() + print(f"Generated: {asm_path}") + print(f"Generated: {bin_path}") + print(f"Generated: {map_path}") + + +if __name__ == "__main__": + main() diff --git a/src/arch/zx81sd/tools/split_sd81.py b/src/arch/zx81sd/tools/split_sd81.py new file mode 100755 index 000000000..c478d7f4e --- /dev/null +++ b/src/arch/zx81sd/tools/split_sd81.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +""" +split_sd81.py — ZX81+SD81 Booster binary splitter + loader generator + +Splits the flat binary generated by zxbc.py (--arch zx81sd, -f bin) +into 8KB pages for loading from the ZX81 BASIC loader, and generates +the BASIC listing (plain text, not tokenized) needed to load them. + +Page map: + Page 8 -> block 0 ($0000-$1FFF): vectors + stage2 + runtime + user code + Page 9 -> block 1 ($2000-$3FFF): continuation if the binary exceeds 8KB + Page 10 -> block 2 ($4000-$5FFF): same + Page 11 -> block 3 ($6000-$7FFF): same (external stage 1 resides here before the jump) + Page 12 -> block 4 ($8000-$9FFF): sysvars + heap (data, not executable without MC45) + Page 13 -> block 5 ($A000-$BFFF): heap continuation + Page 15 -> block 7 ($E000-$FFFF): data banking (large static tables placed + here via explicit ORG, out of the code/sysvar/heap budget) + +Any page that comes out 100% zero bytes is the assembler's own gap filler +(zxbasm zero-fills address ranges nothing was ever written to — see +src/zxbasm/memory.py), not real content: typically the unused span between +the end of code and a data table deliberately ORG'd into block 7. Sysvars +and heap are always freshly initialized at runtime, so this is safe to +skip entirely — the page is neither packaged nor loaded (see split()). + +The binary starts at $0000 and includes no .tap/.tzx header. Each +output file is named P.BIN (uppercase), where N is the page. + +The generated BASIC loader (plain text, not yet tokenized to .p) +follows the sequence manually validated on hardware/emulator: + + 2 FAST + 5 LOAD THEN CLEAR 24575 + 10 LOAD FAST "BOOT1.BIN"CODE 24576 + 20 LOAD *MAP 7,8 + 25 LOAD FAST "P8.BIN"CODE 57344 + 30 LOAD *MAP 7,9 + 35 LOAD FAST "P9.BIN"CODE 57344 + ... + LOAD *MAP 7,63 + RAND USR 24576 + +Note: "LOAD THEN CLEAR", "LOAD *MAP" and "LOAD FAST ... CODE" are +extensions of the SD81 Booster firmware over the ZX81's BASIC (they +don't exist in the original ROM). Block 7 ($E000/57344) is used as a +temporary paging window to dump each physical page before BOOT1.BIN +does the final mapping of blocks 0-5 through port $E7. The final +"LOAD *MAP 7,63" forces the switch to full-paging mode (64 pages) and +leaves block 7 in a neutral state before jumping to the program. + +Besides the plain-text listing, this also generates the real tokenized +.p file (ready to load/run) using zx81_p_loader.py, a Python port of +EightyOne's tokenizer (zx81BasicLoader.cpp / IBasicLoader.cpp). + +To load and run the resulting package on the ZX81 (or in EightyOne +pointing at the SD image): `LOAD FAST ""` — nothing else needs +selecting afterwards, it runs straight from there. + +Usage: + python split_sd81.py [output_base] + + If output_base isn't given, the input's name without extension is + used. + +Example: + python split_sd81.py test_sd81.bin + -> TEST_SD81P8.BIN (first page, always present) + -> TEST_SD81P9.BIN (only if the binary exceeds 8 KB) + -> ... + -> test_sd81_loader.txt (loader's BASIC listing, plain text) + -> TEST_SD81.P (the same listing, tokenized) +""" + +import os +import sys + +from zx81_p_loader import build_p_file + +PAGE_SIZE = 8192 # 8KB per page +FIRST_PAGE = 8 # SD81 page assigned to block 0 +CLEANUP_PAGE = 63 # neutral page: forces full-paging mode (64 pages) +BOOT1_FILE = "BOOT1.BIN" +BOOT1_ADDR = 24576 # $6000 +CLEAR_ADDR = 24575 # protects BOOT1.BIN (reserved right below it) +LOAD_WINDOW_ADDR = 57344 # $E000, block 7's window + + +def split(input_path: str, output_base: str) -> list[tuple[int, str]]: + with open(input_path, "rb") as f: + data = f.read() + + if not data: + sys.exit(f"Error: {input_path} is empty") + + pages_written: list[tuple[int, str]] = [] + page_num = FIRST_PAGE + offset = 0 + + while offset < len(data): + raw_chunk = data[offset : offset + PAGE_SIZE] + + # A full (unpadded) page that's 100% zero bytes is the assembler's own + # gap filler (zxbasm's MEMORY.dump() zero-fills any address range + # nothing was ever written to — see src/zxbasm/memory.py), not real + # program content. This shows up when a data table is deliberately + # ORG'd far away from the code (e.g. into block 7, $E000+, to keep + # it out of the code/sysvar/heap budget below $8000): everything in + # between is a zero-filled gap that never needs to reach the SD card, + # since sysvars/heap are freshly initialized at runtime anyway and + # nothing reads that memory before writing it first. Skip packaging + # and loading it — the runtime page-mapping (OUT to port $E7) for + # that block is unaffected, it just won't have been pre-loaded with + # this particular (irrelevant) content. + if len(raw_chunk) == PAGE_SIZE and raw_chunk.count(0) == PAGE_SIZE: + print( + f" Page {page_num} (block {page_num - FIRST_PAGE}): " + f"[{offset:#06x} - {offset + PAGE_SIZE - 1:#06x}] " + "all zero filler, skipped (not packaged/loaded)" + ) + offset += PAGE_SIZE + page_num += 1 + continue + + chunk = raw_chunk + # Pad up to PAGE_SIZE with 0xFF (undefined value for unwritten FLASH/RAM) + if len(chunk) < PAGE_SIZE: + chunk = chunk + b"\xff" * (PAGE_SIZE - len(chunk)) + + out_path = f"{output_base}P{page_num}.BIN" + with open(out_path, "wb") as f: + f.write(chunk) + + pages_written.append((page_num, out_path)) + print( + f" Page {page_num} (block {page_num - FIRST_PAGE}): " + f"{out_path} " + f"[{offset:#06x} - {min(offset + PAGE_SIZE - 1, len(data) - 1):#06x}]" + ) + + offset += PAGE_SIZE + page_num += 1 + + return pages_written + + +def generate_loader_lines(pages: list[tuple[int, str]]) -> list[tuple[int, str]]: + lines = [] + lines.append((2, "FAST")) + lines.append((5, f"LOAD THEN CLEAR {CLEAR_ADDR}")) + lines.append((10, f'LOAD FAST "{BOOT1_FILE}"CODE {BOOT1_ADDR}')) + + line_no = 20 + for page_num, page_file in pages: + lines.append((line_no, f"LOAD *MAP 7,{page_num}")) + lines.append((line_no + 5, f'LOAD FAST "{page_file}"CODE {LOAD_WINDOW_ADDR}')) + line_no += 10 + + lines.append((line_no, f"LOAD *MAP 7,{CLEANUP_PAGE}")) + lines.append((line_no + 10, f"RAND USR {BOOT1_ADDR}")) + + return lines + + +def generate_loader_text(lines: list[tuple[int, str]]) -> str: + return "\n".join(f"{n} {cmd}" for n, cmd in lines) + "\n" + + +def main(): + if len(sys.argv) < 2: + print(__doc__) + sys.exit(1) + + input_path = sys.argv[1] + output_base = sys.argv[2] if len(sys.argv) > 2 else os.path.splitext(input_path)[0] + output_base = os.path.basename(output_base).upper() + + # Page and loader.p file names are written in the ZX81 charset + # (letters, digits and a few symbols: no underscore or other + # non-representable characters). Fail early and clearly instead of + # blowing up later while tokenizing the loader. + from zx81_p_loader import ascii_to_zx + + for c in output_base: + try: + ascii_to_zx(c) + except ValueError: + sys.exit( + f"Error: base name {output_base!r} contains {c!r}, " + "not representable in the ZX81 charset (avoid '_' and similar)." + ) + + if not os.path.isfile(input_path): + sys.exit(f"Error: {input_path!r} not found") + + size = os.path.getsize(input_path) + num_pages = (size + PAGE_SIZE - 1) // PAGE_SIZE + print(f"Binary: {input_path} ({size} bytes, {size / 1024:.1f} KB, {num_pages} page(s))") + print(f"Splitting into {PAGE_SIZE}-byte pages (starting SD81 page = {FIRST_PAGE})...") + print() + + pages = split(input_path, output_base) + + print() + print(f"Generated {len(pages)} file(s).") + if len(pages) == 1: + print("The program fits in a single page (8 KB).") + elif pages: + page_numbers = [p for p, _ in pages] + print(f"Pages {min(page_numbers)} to {max(page_numbers)} (some may have been skipped as filler).") + + loader_lines = generate_loader_lines(pages) + loader_text = generate_loader_text(loader_lines) + + # The .p is a real file name for the ZX81 (charset with no + # underscore): derived from output_base (same prefix as the + # pages), concatenated with no separator, just like "P8.BIN". + base_no_ext = os.path.splitext(input_path)[0] + loader_txt_path = f"{base_no_ext}_loader.txt" + loader_p_path = f"{output_base}.P" + + with open(loader_txt_path, "w", newline="\n") as f: + f.write(loader_text) + + p_data = build_p_file(loader_lines) + with open(loader_p_path, "wb") as f: + f.write(p_data) + + print() + print(f"BASIC loader (plain text): {loader_txt_path}") + print("---") + print(loader_text, end="") + print("---") + print(f"Tokenized BASIC loader (.p): {loader_p_path} ({len(p_data)} bytes)") + print() + print("NOTE: 'LOAD THEN CLEAR', 'LOAD *MAP' and 'LOAD FAST ... CODE' are") + print("extensions of the SD81 Booster firmware, encoded with the same token") + print("mechanism as standard ZX81 BASIC (see zx81_p_loader.py).") + print(f"Copy {BOOT1_FILE}, the page files and {os.path.basename(loader_p_path)}") + print("to the SD card.") + + +if __name__ == "__main__": + main() diff --git a/src/arch/zx81sd/tools/zx81_p_loader.py b/src/arch/zx81sd/tools/zx81_p_loader.py new file mode 100755 index 000000000..5b10bcdb2 --- /dev/null +++ b/src/arch/zx81sd/tools/zx81_p_loader.py @@ -0,0 +1,577 @@ +#!/usr/bin/env python3 +""" +zx81_p_loader.py — .p file (tokenized BASIC) generator for the ZX81 + +Direct Python port of the tokenization algorithm used by the EightyOne +emulator (zx81BasicLoader.cpp / IBasicLoader.cpp), reference: + C:\\ClaudeCode\\Eightyone2\\src\\zx81\\zx81BasicLoader.cpp + C:\\ClaudeCode\\Eightyone2\\src\\BasicLoader\\IBasicLoader.cpp + +The SD81 Booster firmware's extended commands (LOAD THEN CLEAR, +LOAD *MAP, LOAD FAST ... CODE, etc.) need no special handling: even +though the ZX81 ROM interpreter doesn't accept that syntax, they're +made up of: + - Standard ZX81 tokens (THEN, CLEAR, FAST, CODE, LOAD...) in + positions the ROM wouldn't use but that CAN be typed (e.g. + SHIFT+3 = THEN). + - Standalone words after an asterisk (e.g. *MAP, *VER) which are + plain letters, not tokens. +So it's enough to tokenize the text just like any standard BASIC: the +generic algorithm itself already produces the correct encoding. + +Not implemented (not needed for this project): @label tags, numeric +blocks [DEC:...]/[HEX:...]/[BIN:...], \\xx escape sequences, graphics +codes, or "alternate keyword spelling". Documented in IBasicLoader.cpp +in case they're ever needed. +""" + +import math +import re +import struct + +BLANK = "\x01" # internal marker: position already consumed +NEWLINE = 0x76 +NUMBER_MARK = 0x7E # precedes the 5 bytes of an embedded number +DOUBLE_QUOTE = 0xC0 # escaped quote ("") inside a string + +# Standard ZX81 tokens (from zx81BasicLoader::ExtractTokens, without +# "alternate spelling" variants or ZXpand extensions) +TOKENS = { + 64: "RND", + 65: "INKEY$", + 66: "PI", + 193: "AT ", + 194: "TAB ", + 196: "CODE ", + 197: "VAL ", + 198: "LEN ", + 199: "SIN ", + 200: "COS ", + 201: "TAN ", + 202: "ASN ", + 203: "ACS ", + 204: "ATN ", + 205: "LN ", + 206: "EXP ", + 207: "INT ", + 208: "SQR ", + 209: "SGN ", + 210: "ABS ", + 211: "PEEK ", + 212: "USR ", + 213: "STR$ ", + 214: "CHR$ ", + 215: "NOT ", + 216: "**", + 217: " OR ", + 218: " AND ", + 219: "<=", + 220: ">=", + 221: "<>", + 222: " THEN ", + 223: " TO ", + 224: " STEP ", + 225: " LPRINT ", + 226: " LLIST ", + 227: " STOP ", + 228: " SLOW ", + 229: " FAST ", + 230: " NEW ", + 231: " SCROLL ", + 232: " CONT ", + 233: " DIM ", + 234: " REM ", + 235: " FOR ", + 236: " GOTO ", + 237: " GOSUB ", + 238: " INPUT ", + 239: " LOAD ", + 240: " LIST ", + 241: " LET ", + 242: " PAUSE ", + 243: " NEXT ", + 244: " POKE ", + 245: " PRINT ", + 246: " PLOT ", + 247: " RUN ", + 248: " SAVE ", + 249: " RAND ", + 250: " IF ", + 251: " CLS ", + 252: " UNPLOT ", + 253: " CLEAR ", + 254: " RETURN ", + 255: " COPY ", +} + + +def ascii_to_zx(c: str) -> int: + """Port of zx81BasicLoader::AsciiToZX.""" + if c.isalpha(): + return (ord(c.upper()) - ord("A")) + 38 + if c.isdigit(): + return (ord(c) - ord("0")) + 28 + + table = { + " ": 0, + '"': 11, + "#": 12, + "$": 13, + ":": 14, + "?": 15, + "(": 16, + ")": 17, + "-": 22, + "+": 21, + "*": 23, + "/": 24, + "=": 20, + ">": 18, + "<": 19, + ";": 25, + ",": 26, + ".": 27, + } + if c in table: + return table[c] + + raise ValueError(f"Invalid character: {c!r}") + + +def zx81_float(value: float) -> bytes: + """Port of zx81BasicLoader::OutputFloatingPointEncoding.""" + exponent = 0 + mantissa = 0 + + if value != 0: + neg = value < 0 + if neg: + value = -value + + exponent = math.floor(1e-12 + (math.log(value) / math.log(2.0))) + if exponent < -129 or exponent > 126: + raise OverflowError("Number out of range") + + mantissa_val = (value / (2.0**exponent)) - 1 + mantissa_val *= 0x80000000 + mantissa = math.floor(mantissa_val) + + exponent += 129 + + return bytes( + [ + exponent & 0xFF, + (mantissa >> 24) & 0xFF, + (mantissa >> 16) & 0xFF, + (mantissa >> 8) & 0xFF, + mantissa & 0xFF, + ] + ) + + +class _Line: + """Working state for a BASIC line (without a line number), mirroring + the mLineBuffer / mLineBufferOutput / mLineBufferPopulated buffers. + + All arrays have length self.n + 1: the last slot (index n) is a + safety pad for when a token consumes the artificial space added at + the end to detect tokens with no closing space in the original + text (same as the oversized C++ buffer). + """ + + def __init__(self, text: str): + content = " " + text # ReadLine always prepends a space + self.n = len(content) + self.buf = list(content) + [BLANK] # +1 safety slot + self.out = [BLANK] * (self.n + 1) + self.populated = [False] * (self.n + 1) + # BlankLineStart: no embedded line number, leaves 1 real space + self.buf[0] = " " + + +def _mask_copy(chars: list) -> list: + return list(chars) + + +def _mask_out_strings(tokenised: list): + """Port of IBasicLoader::MaskOutStrings.""" + text = "".join(tokenised) + q1 = text.find('"') + if q1 == -1: + return + rem = " REM " + r1 = text.find(rem) + if r1 != -1 and r1 < q1: + return + + within = False + i = q1 + n = len(tokenised) + while i < n: + if tokenised[i] == '"': + within = not within + elif within: + tokenised[i] = BLANK + else: + rest = "".join(tokenised[i + 1 :]) + nq = rest.find('"') + if nq == -1: + return + nr = rest.find(rem) + if nr != -1 and nr < nq: + return + i += 1 + + +def _mask_out_rem_contents(tokenised: list): + """Port of IBasicLoader::MaskOutRemContents.""" + text = "".join(tokenised) + rem = " REM " + pos = 0 + while True: + r = text.find(rem, pos) + if r == -1: + return + q = text.find('"', pos) + if q != -1 and q < r: + q2 = text.find('"', q + 1) + if q2 == -1: + return + pos = q2 + 1 + continue + start = r + len(rem) + for i in range(start, len(tokenised)): + tokenised[i] = BLANK + return + + +def _extract_double_quote_characters(line: _Line): + """Port of zx81BasicLoader::ExtractDoubleQuoteCharacters (without + tokenized-REM support or alternate spelling, not needed here).""" + buf = line.buf + n = line.n + within_quote = False + i = 0 + while i < n: + if not within_quote: + if buf[i] == '"': + within_quote = True + else: + chr1 = buf[i] + if chr1 == '"': + if i + 1 < n and buf[i + 1] == '"': + buf[i] = BLANK + line.out[i] = DOUBLE_QUOTE + line.populated[i] = True + i += 1 + buf[i] = BLANK + else: + within_quote = False + i += 1 + + +def _do_tokenise(line: _Line, tokenised: list): + """Port of IBasicLoader::DoTokenise. Walks tokens from highest to + lowest code (equivalent to the reverse_iterator over a std::map), + mutating `tokenised` (search) and line.buf/out/populated (result).""" + for token_code in sorted(TOKENS.keys(), reverse=True): + token = TOKENS[token_code] + len_token = len(token) + + start_char = token[0] + end_char = token[-1] + + token_begins_with_space = start_char == " " + token_begins_with_alpha = start_char.isalpha() + token_ends_with_space = end_char == " " + token_ends_with_alpha = end_char.isalpha() + + len_adjustment = 0 + eff_len_token = len_token + if ( + end_char in ("(", ")", "!", '"', "'", ",", ";", ":") + or (end_char == "#" and (len_token < 2 or token[-2] != " ")) + or (end_char == "*" and token != "**") + ): + eff_len_token -= 1 + + guard = 0 + while True: + guard += 1 + if guard > 10000: + raise RuntimeError(f"Endless tokenizing loop for {token!r}") + + text = "".join(tokenised) + pos = text.find(token) + if pos == -1: + break + + prev_ok = ( + token_begins_with_space + or not token_begins_with_alpha + or (pos == 0) + or (not tokenised[pos - 1].isalnum()) + ) + end_pos = pos + eff_len_token + next_ok = ( + token_ends_with_space + or not token_ends_with_alpha + or (end_pos >= len(tokenised)) + or (not tokenised[end_pos].isalnum()) + ) + + if not (prev_ok and next_ok): + # Match glued to an identifier: not the token, "break" + # it locally so the search keeps going further ahead. + tokenised[pos] = "\x02" + continue + + start_offset = 1 if token_begins_with_space else 0 + end_offset = (eff_len_token - 1) if token_ends_with_space else (eff_len_token + len_adjustment) + + for b in range(start_offset, end_offset): + tokenised[pos + b] = BLANK + + for b in range(eff_len_token + len_adjustment): + idx = pos + b + if idx < len(line.buf): + line.buf[idx] = BLANK + + output_index = pos + start_offset + if output_index < len(line.out): + line.out[output_index] = token_code + line.populated[output_index] = True + + +def _start_of_number(line: _Line, tokenised: list, index: int) -> bool: + """Determines whether `index` is the start of a numeric literal. + + Deliberately differs from the original + IBasicLoader::StartOfNumber on one point: here only the character + IMMEDIATELY before is checked (it walks back over real spaces but + stops as soon as it finds ANY non-space character, without walking + back further). The original walked back over ALL consecutive + spaces and checked the character before all of them, which in + standard BASIC is equivalent (tokens always consume their own + trailing space as BLANK, not as a literal ' ') but fails on SD81 + custom commands like "*MAP 7,8": "MAP" isn't a recognized token, + so the space after it stays as a literal ' ', and the original + algorithm ended up looking at the (alphabetic) 'P' of MAP and + decided "7" was NOT a number. On a real ZX81, any digit typed after + a space generates its own embedded float regardless of the word + preceding it, so that behavior is replicated here (simpler and more + correct for this case). + """ + if not (line.buf[index] == "." or line.buf[index].isdigit()): + return False + + if index == 0: + return True + + prev = line.buf[index - 1] + if prev == " ": + return True + + return not (prev.isalpha() or prev.isdigit()) + + +_NUMBER_RE = re.compile(r"[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?") + + +def _output_embedded_number(line: _Line, index: int, out: bytearray) -> int: + """Port of IBasicLoader::OutputEmbeddedNumber. Returns the new + index (already advanced past the number, ready for `i += 1` + outside).""" + n = line.n + + text_no_spaces = "" + for j in range(index, n): + if line.buf[j] != BLANK: + text_no_spaces += line.buf[j] + + m = _NUMBER_RE.match(text_no_spaces) + if not m or m.end() == 0: + raise ValueError(f"Invalid number at position {index}: {text_no_spaces!r}") + number_len_no_spaces = m.end() + value = float(m.group(0)) + + with_spaces_index = 0 + for _ in range(number_len_no_spaces): + while line.buf[index + with_spaces_index] in (" ", BLANK): + with_spaces_index += 1 + with_spaces_index += 1 + + end_index = index + with_spaces_index + + i = index + while i < end_index: + chr_code = line.out[i] + if chr_code != BLANK and line.populated[i]: + out.append(chr_code) + i += 1 + + while i < n and line.buf[i] == " ": + out.append(ascii_to_zx(" ")) + i += 1 + + out.append(NUMBER_MARK) + out.extend(zx81_float(value)) + + return i - 1 + + +def process_line_body(text: str) -> bytes: + """Tokenizes a BASIC line (without a line number) and returns the + already-encoded bytes (without the final NEWLINE, which is added + in encode_line).""" + line = _Line(text) + n = line.n + + _extract_double_quote_characters(line) + + # Working copy for token boundary detection: the real content + # (without line.buf's safety slot) + one extra real space to + # detect tokens with no closing space in the original text. + # Length n+1, same as line.buf, so indices stay aligned. + tokenised = list(line.buf[:n]) + [" "] + + _mask_out_strings(tokenised) + _mask_out_rem_contents(tokenised) + + _do_tokenise(line, tokenised) + + # Fill any unconsumed position with its direct ZX81 code + for i in range(1, n): + if line.buf[i] != BLANK and not line.populated[i]: + line.out[i] = ascii_to_zx(line.buf[i]) + line.populated[i] = True + + body = bytearray() + i = 0 + while i < n: + if line.populated[i]: + if _start_of_number(line, tokenised, i): + i = _output_embedded_number(line, i, body) + 1 + continue + body.append(line.out[i]) + i += 1 + + return bytes(body) + + +def encode_line(line_number: int, text: str) -> bytes: + body = process_line_body(text) + bytes([NEWLINE]) + return struct.pack(">H", line_number) + struct.pack(" bytes: + """lines: list of (line_number:int, text:str) tuples.""" + return b"".join(encode_line(n, t) for n, t in lines) + + +def build_p_file(lines: list) -> bytes: + """Port of zx81BasicLoader::OutputStartOfProgramData + + ProcessLine(*) + OutputEndOfProgramData. Returns the complete + contents of the .p file (from 0x4009).""" + data = bytearray() + + def out_byte(b): + data.append(b & 0xFF) + + def out_word(w): + data.append(w & 0xFF) + data.append((w >> 8) & 0xFF) + + def change_word(offset, w): + data[offset] = w & 0xFF + data[offset + 1] = (w >> 8) & 0xFF + + # --- OutputStartOfProgramData --- + out_byte(0x00) # VERSN + out_word(0x0000) # E_PPC + out_word(0x0000) # D_FILE + out_word(0x0000) # DF_CC + out_word(0x0000) # VARS + out_word(0x0000) # DEST + out_word(0x0000) # E_LINE + out_word(0x0000) # CH_ADD + out_word(0x0000) # X_PTR + out_word(0x0000) # STKBOT + out_word(0x0000) # STKEND + out_byte(0x00) # BERG + out_word(0x405D) # MEM + out_byte(0x00) # SPARE1 + out_byte(0x02) # DF_SZ + out_word(0x0000) # S_TOP + out_word(0xFFFF) # LAST_K + out_byte(0x00) # DBOUNC + out_byte(0x37) # MARGIN + out_word(0x0000) # NXTLIN + out_word(0x0000) # OLDPPC + out_byte(0x00) # FLAGX + out_word(0x0000) # STRLEN + out_word(0x0C8D) # T_ADDR + out_word(0x4321) # SEED + out_word(0xE6E0) # FRAMES + out_word(0x0000) # COORDS + out_byte(0xBC) # PR_CC + out_word(0x1821) # S_POSN + out_byte(0x40) # CDFLAG + data.extend([0x00] * 32) # PRBUFF (32 empty bytes) + out_byte(0x76) # PRBUFF (byte 33, NEWLINE) + data.extend([0x00] * 30) # MEMBOT + out_word(0x0000) # SPARE + + # --- BASIC lines --- + for line_number, text in lines: + data.extend(encode_line(line_number, text)) + + # --- OutputEndOfProgramData --- + START_OF_RAM = 16393 # 0x4009 + + dfile_address = START_OF_RAM + len(data) + data.extend([NEWLINE] * 25) # collapsed display file (empty) + + vars_address = START_OF_RAM + len(data) + out_byte(0x80) # end of VARS (no variables) + + eline_address = START_OF_RAM + len(data) + + change_word(3, dfile_address) # D_FILE + change_word(5, dfile_address + 1) # DF_CC + change_word(7, vars_address) # VARS + change_word(11, vars_address + 1) # E_LINE + change_word(13, vars_address + 5) # CH_ADD + change_word(17, eline_address + 5) # STKBOT + change_word(19, eline_address + 5) # STKEND + change_word(32, vars_address) # NXTLIN (= VARS => no autorun) + + return bytes(data) + + +if __name__ == "__main__": + import sys + + # Quick self-test with the manually validated loader + lines = [ + (2, "FAST"), + (5, "LOAD THEN CLEAR 24575"), + (10, 'LOAD FAST "BOOT1.BIN"CODE 24576'), + (20, "LOAD *MAP 7,8"), + (25, 'LOAD FAST "TESTSD81P8.BIN"CODE 57344'), + (30, "LOAD *MAP 7,9"), + (35, 'LOAD FAST "TESTSD81P9.BIN"CODE 57344'), + (50, "LOAD *MAP 7,63"), + (60, "RAND USR 24576"), + ] + + p_data = build_p_file(lines) + out_path = sys.argv[1] if len(sys.argv) > 1 else "loader_test.p" + with open(out_path, "wb") as f: + f.write(p_data) + + print(f"Generated {out_path} ({len(p_data)} bytes)") + for i in range(0, len(p_data), 16): + chunk = p_data[i : i + 16] + hexpart = " ".join(f"{b:02X}" for b in chunk) + print(f"{i:04X}: {hexpart}") diff --git a/src/arch/zxnext/__init__.py b/src/arch/zxnext/__init__.py index 9a0377e6b..c79c6b5f5 100644 --- a/src/arch/zxnext/__init__.py +++ b/src/arch/zxnext/__init__.py @@ -1,4 +1,9 @@ -# vim:ts=4:et:sw=4: +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import src.api.global_ from src.api.constants import TYPE diff --git a/src/arch/zxnext/backend/_8bit.py b/src/arch/zxnext/backend/_8bit.py index b21e1e9e5..25b5d34d4 100644 --- a/src/arch/zxnext/backend/_8bit.py +++ b/src/arch/zxnext/backend/_8bit.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.arch.interface.quad import Quad from src.arch.z80.backend import Bits8 as Z80Bits8 from src.arch.z80.backend.common import _int_ops diff --git a/src/arch/zxnext/backend/__init__.py b/src/arch/zxnext/backend/__init__.py index f71e1c5f7..e5731cc25 100644 --- a/src/arch/zxnext/backend/__init__.py +++ b/src/arch/zxnext/backend/__init__.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.arch.z80.backend import ( HI16, INITS, diff --git a/src/arch/zxnext/backend/generic.py b/src/arch/zxnext/backend/generic.py index 688c62852..7666a2864 100644 --- a/src/arch/zxnext/backend/generic.py +++ b/src/arch/zxnext/backend/generic.py @@ -1,6 +1,9 @@ -# ---------------------------------------------------------- -# Generic instructions -# ---------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.api.config import OPTIONS from src.arch.interface.quad import Quad diff --git a/src/arch/zxnext/backend/main.py b/src/arch/zxnext/backend/main.py index 21d2f0ddb..60e9b3d58 100644 --- a/src/arch/zxnext/backend/main.py +++ b/src/arch/zxnext/backend/main.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api.config import OPTIONS from src.api.options import Action from src.arch.z80.backend import Backend as Z80Backend diff --git a/src/arch/zxnext/optimizer/__init__.py b/src/arch/zxnext/optimizer/__init__.py index 91259bc01..912fe0e82 100644 --- a/src/arch/zxnext/optimizer/__init__.py +++ b/src/arch/zxnext/optimizer/__init__.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from .main import Optimizer __all__ = ("Optimizer",) diff --git a/src/arch/zxnext/optimizer/basicblock.py b/src/arch/zxnext/optimizer/basicblock.py index 6942163c1..2ad85e277 100644 --- a/src/arch/zxnext/optimizer/basicblock.py +++ b/src/arch/zxnext/optimizer/basicblock.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from collections.abc import Iterable from src.arch.z80.optimizer import Optimizer diff --git a/src/arch/zxnext/optimizer/cpustate.py b/src/arch/zxnext/optimizer/cpustate.py index 1b4154d87..f5c85ad72 100644 --- a/src/arch/zxnext/optimizer/cpustate.py +++ b/src/arch/zxnext/optimizer/cpustate.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.arch.z80.optimizer.asm import Asm from src.arch.z80.optimizer.cpustate import CPUState as CPUStateZ80 diff --git a/src/arch/zxnext/optimizer/main.py b/src/arch/zxnext/optimizer/main.py index f2b7efd90..aa3fe91a7 100644 --- a/src/arch/zxnext/optimizer/main.py +++ b/src/arch/zxnext/optimizer/main.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.arch.z80.optimizer import Optimizer as OptimizerZ80 from .basicblock import BasicBlock diff --git a/src/arch/zxnext/optimizer/memcell.py b/src/arch/zxnext/optimizer/memcell.py index b3190c36b..81047e2cd 100644 --- a/src/arch/zxnext/optimizer/memcell.py +++ b/src/arch/zxnext/optimizer/memcell.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from functools import cached_property from src.arch.z80.optimizer.memcell import MemCell as MemCellZ80 diff --git a/src/arch/zxnext/peephole/__init__.py b/src/arch/zxnext/peephole/__init__.py index 834f1263a..a1cb75f89 100644 --- a/src/arch/zxnext/peephole/__init__.py +++ b/src/arch/zxnext/peephole/__init__.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import os # Optimization recipes paths for this arch diff --git a/src/arch/zxnext/peephole/opts/060_o1_mul_de.opt b/src/arch/zxnext/peephole/opts/060_o1_mul_de.opt index 962b9bc3a..75f4b0133 100644 --- a/src/arch/zxnext/peephole/opts/060_o1_mul_de.opt +++ b/src/arch/zxnext/peephole/opts/060_o1_mul_de.opt @@ -1,25 +1,25 @@ -;; Change sequence: -;; ld h, XX -;; ld d, h - -;; into: - -;; ld d, XX - -;; if it's used for an 8 bit multiplication (mul d, e) - -OLEVEL: 1 -OFLAG: 1 - -REPLACE {{ - ld h, $1 - ld d, h - ld e, a - mul d, e -}} - -WITH {{ - ld d, $1 - ld e, a - mul d, e -}} +;; Change sequence: +;; ld h, XX +;; ld d, h + +;; into: + +;; ld d, XX + +;; if it's used for an 8 bit multiplication (mul d, e) + +OLEVEL: 1 +OFLAG: 1 + +REPLACE {{ + ld h, $1 + ld d, h + ld e, a + mul d, e +}} + +WITH {{ + ld d, $1 + ld e, a + mul d, e +}} diff --git a/src/ast/__init__.py b/src/ast/__init__.py deleted file mode 100644 index cf950f96f..000000000 --- a/src/ast/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from .ast import Ast, NodeVisitor, types -from .tree import Tree - -__all__ = ( - "Ast", - "NodeVisitor", - "Tree", - "types", -) diff --git a/src/ast/ast.py b/src/ast/ast.py deleted file mode 100644 index 0ad86e4b3..000000000 --- a/src/ast/ast.py +++ /dev/null @@ -1,75 +0,0 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- - -import types -from collections.abc import Callable -from typing import Any - -from .tree import Tree - - -# ---------------------------------------------------------------------- -# Abstract Syntax Tree class -# ---------------------------------------------------------------------- -class Ast(Tree): - """Adds some methods for easier coding...""" - - __slots__: tuple[str, ...] = () - - @property - def token(self): - return self.__class__ - - -class NodeVisitor: - node_type: type = Ast - - def visit(self, node): - stack = [node] - last_result = None - - while stack: - try: - last = stack[-1] - if isinstance(last, types.GeneratorType): - stack.append(last.send(last_result)) - last_result = None - elif isinstance(last, self.node_type): - stack.append(self._visit(stack.pop())) - else: - last_result = stack.pop() - except StopIteration: - stack.pop() - - return last_result - - def _visit(self, node): - meth = getattr(self, f"visit_{node.token}", self.generic_visit) - return meth(node) - - def generic_visit(self, node: Ast): - raise RuntimeError(f"No visit_{node.token}() method defined") - - def filter_inorder( - self, node, filter_func: Callable[[Any], bool], child_selector: Callable[[Ast], bool] = lambda x: True - ): - """Visit the tree inorder, but only those that return true for filter_func and visiting children which - return true for child_selector. - """ - visited = set() - stack = [node] - while stack: - node = stack.pop() - if node in visited: - continue - visited.add(node) - if filter_func(node): - yield self.visit(node) - if isinstance(node, Ast) and child_selector(node): - stack.extend(node.children[::-1]) diff --git a/src/ast_/__init__.py b/src/ast_/__init__.py new file mode 100644 index 000000000..221568150 --- /dev/null +++ b/src/ast_/__init__.py @@ -0,0 +1,15 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + +from .ast import Ast, NodeVisitor +from .tree import Tree + +__all__ = ( + "Ast", + "NodeVisitor", + "Tree", +) diff --git a/src/ast_/ast.py b/src/ast_/ast.py new file mode 100644 index 000000000..69da2bb68 --- /dev/null +++ b/src/ast_/ast.py @@ -0,0 +1,42 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- +from collections.abc import Callable, Generator +from typing import Any, Final + +from .tree import Tree +from .visitor import GenericNodeVisitor + +__all__: Final[tuple[str, ...]] = "Ast", "NodeVisitor" + + +# ---------------------------------------------------------------------- +# Abstract Syntax Tree class +# ---------------------------------------------------------------------- +class Ast(Tree): + """Adds some methods for easier coding...""" + + __slots__: tuple[str, ...] = () + + @property + def token(self): + return self.__class__ + + +class NodeVisitor(GenericNodeVisitor[Ast]): + def _visit(self, node: Ast): + meth: Callable[[Ast], Generator[Ast | Any, Any]] = getattr( + self, + f"visit_{node.token}", + self.generic_visit, + ) + return meth(node) + + def generic_visit(self, node: Ast) -> Generator[Ast | Any, Any]: + for i, child in enumerate(node.children): + node.children[i] = yield self.visit(child) + + yield node diff --git a/src/ast_/exceptions.py b/src/ast_/exceptions.py new file mode 100644 index 000000000..23102a633 --- /dev/null +++ b/src/ast_/exceptions.py @@ -0,0 +1,18 @@ +from typing import Final + +from src.api.exception import Error + +__all__: Final[tuple[str, ...]] = ("NotAnAstError",) + + +class NotAnAstError(Error): + """Thrown when the "pointer" is not + an AST, but another thing. + """ + + def __init__(self, instance): + self.instance = instance + self.msg = "Object '%s' is not an Ast instance" % str(instance) + + def __str__(self): + return self.msg diff --git a/src/ast/tree.py b/src/ast_/tree.py similarity index 89% rename from src/ast/tree.py rename to src/ast_/tree.py index db2dd3678..d65e384e3 100644 --- a/src/ast/tree.py +++ b/src/ast_/tree.py @@ -1,25 +1,17 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from __future__ import annotations import collections.abc from collections.abc import Iterable, Iterator from typing import Any -from src.api.exception import Error - -__all__ = "ChildrenList", "NotAnAstError", "Tree" - - -class NotAnAstError(Error): - """Thrown when the "pointer" is not - an AST, but another thing. - """ - - def __init__(self, instance): - self.instance = instance - self.msg = "Object '%s' is not an Ast instance" % str(instance) - - def __str__(self): - return self.msg +__all__ = "ChildrenList", "Tree" class Tree: diff --git a/src/ast_/visitor.py b/src/ast_/visitor.py new file mode 100644 index 000000000..e6aea3ffd --- /dev/null +++ b/src/ast_/visitor.py @@ -0,0 +1,40 @@ +__doc__ = "Implements a generic visitor class for Trees" + +from abc import abstractmethod +from collections.abc import Generator +from types import GeneratorType +from typing import Final, NamedTuple + +__all__: Final[tuple[str, ...]] = ("GenericNodeVisitor",) + + +class ToVisit[T](NamedTuple): + obj: T + + +class GenericNodeVisitor[T]: + def visit(self, node: T | None) -> T | Generator[T | None] | None: + stack: list[T | GeneratorType] = [ToVisit[T](node) if node is not None else None] + last_result: T | None = None + + while stack: + try: + stack_top = stack[-1] + if isinstance(stack_top, GeneratorType): + stack.append(stack_top.send(last_result)) + last_result = None + elif isinstance(stack_top, ToVisit): + stack.pop() + stack.append(self._visit(stack_top.obj)) + else: + last_result = stack.pop() + except StopIteration: + stack.pop() + + return last_result + + @abstractmethod + def _visit(self, node: T): ... + + @abstractmethod + def generic_visit(self, node: T) -> Generator[T | None]: ... diff --git a/src/lib/arch/zx48k/runtime/arith/mul32.asm b/src/lib/arch/zx48k/runtime/arith/mul32.asm index fc6430221..641bc1efb 100644 --- a/src/lib/arch/zx48k/runtime/arith/mul32.asm +++ b/src/lib/arch/zx48k/runtime/arith/mul32.asm @@ -24,6 +24,3 @@ __TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) ret pop namespace - - -f diff --git a/src/lib/arch/zx48k/runtime/spectranet.inc b/src/lib/arch/zx48k/runtime/spectranet.inc index 052e61f22..73fbd2c1e 100644 --- a/src/lib/arch/zx48k/runtime/spectranet.inc +++ b/src/lib/arch/zx48k/runtime/spectranet.inc @@ -1,143 +1,143 @@ -;The MIT License -; -;Copyright (c) 2008 Dylan Smith -; -;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. - -; This file can be included in assembly language programs to give -; symbolic access to the public jump table entry points. - -; Avoid double inclusion -#ifndef __SPECTRANET_INC__ -#define __SPECTRANET_INC__ - -NAMESPACE Spectranet ; NAME PREFIX to avoid naming clash - -; Hardware page-in entry points -MODULECALL equ $3FF8 -MODULECALL_NOPAGE equ $28 -PAGEIN equ $3FF9 -PAGEOUT equ $007C -HLCALL equ $3FFA -IXCALL equ $3FFD - -; Port defines -CTRLREG equ $033B -CPLDINFO equ $023B - -; Jump table entry points -SOCKET equ $3E00 ; Allocate a socket -CLOSE equ $3E03 ; Close a socket -LISTEN equ $3E06 ; Listen for incoming connections -ACCEPT equ $3E09 ; Accept an incoming connection -BIND equ $3E0C ; Bind a local address to a socket -CONNECT equ $3E0F ; Connect to a remote host -SEND equ $3E12 ; Send data -RECV equ $3E15 ; Receive data -SENDTO equ $3E18 ; Send data to an address -RECVFROM equ $3E1B ; Receive data from an address -POLL equ $3E1E ; Poll a list of sockets -POLLALL equ $3E21 ; Poll all open sockets -POLLFD equ $3E24 ; Poll a single socket -GETHOSTBYNAME equ $3E27 ; Look up a hostname -PUTCHAR42 equ $3E2A ; 42 column print write a character -PRINT42 equ $3E2D ; 42 column print a null terminated string -CLEAR42 equ $3E30 ; Clear the screen and reset 42-col print -SETPAGEA equ $3E33 ; Sets page area A -SETPAGEB equ $3E36 ; Sets page area B -LONG2IPSTRING equ $3E39 ; Convert a 4 byte big endian long to an IP -IPSTRING2LONG equ $3E3C ; Convert an IP to a 4 byte big endian long -ITOA8 equ $3E3F ; Convert a byte to ascii -RAND16 equ $3E42 ; 16 bit PRNG -REMOTEADDRESS equ $3E45 ; Fill struct sockaddr_in -IFCONFIG_INET equ $3E48 ; Set IPv4 address -IFCONFIG_NETMASK equ $3E4B ; Set netmask -IFCONFIG_GW equ $3E4E ; Set gateway -INITHW equ $3E51 ; Set the MAC address and initial hw registers -GETHWADDR equ $3E54 ; Read the MAC address -DECONFIG equ $3E57 ; Deconfigure inet, netmask and gateway -MAC2STRING equ $3E5A ; Convert 6 byte MAC address to a string -STRING2MAC equ $3E5D ; Convert a hex string to a 6 byte MAC address -ITOH8 equ $3E60 ; Convert accumulator to hex string -HTOI8 equ $3E63 ; Convert hex string to byte in A -GETKEY equ $3E66 ; Get a key from the keyboard, and put it in A -KEYUP equ $3E69 ; Wait for key release -INPUTSTRING equ $3E6C ; Read a string into buffer at DE -GET_IFCONFIG_INET equ $3E6F ; Gets the current IPv4 address -GET_IFCONFIG_NETMASK equ $3E72 ; Gets the current netmask -GET_IFCONFIG_GW equ $3E75 ; Gets the current gateway address -SETTRAP equ $3E78 ; Sets the programmable trap -DISABLETRAP equ $3E7B ; Disables the programmable trap -ENABLETRAP equ $3E7E ; Enables the programmable trap -PUSHPAGEA equ $3E81 ; Pages a page into area A, pushing the old one -POPPAGEA equ $3E84 ; Restores the previous page in area A -PUSHPAGEB equ $3E87 ; Pages into area B pushing the old one -POPPAGEB equ $3E8A ; Restores the previous page in area B -PAGETRAPRETURN equ $3E8D ; Returns from a trap to page area B -TRAPRETURN equ $3E90 ; Returns from a trap that didn't page area B -ADDBASICEXT equ $3E93 ; Adds a BASIC command -STATEMENT_END equ $3E96 ; Check for statement end, exit at syntax time -EXIT_SUCCESS equ $3E99 ; Use this to exit successfully after cmd -PARSE_ERROR equ $3E9C ; Use this to exit to BASIC with a parse error -RESERVEPAGE equ $3E9F ; Reserve a page of static RAM -FREEPAGE equ $3EA2 ; Free a page of static RAM -REPORTERR equ $3EA5 ; report an error via BASIC - -; Filesystem functions -MOUNT equ $3EA8 -UMOUNT equ $3EAB -OPENDIR equ $3EAE -OPEN equ $3EB1 -UNLINK equ $3EB4 -MKDIR equ $3EB7 -RMDIR equ $3EBA -SIZE equ $3EBD -FREE equ $3EC0 -STAT equ $3EC3 -CHMOD equ $3EC6 -READ equ $3EC9 -WRITE equ $3ECC -LSEEK equ $3ECF -VCLOSE equ $3ED2 -VPOLL equ $3ED5 -READDIR equ $3ED8 -CLOSEDIR equ $3EDB -CHDIR equ $3EDE -GETCWD equ $3EE1 -RENAME equ $3EE4 -SETMOUNTPOINT equ $3EE7 -FREEMOUNTPOINT equ $3EEA -RESALLOC equ $3EED - - -; Definitions -ALLOCFD equ 1 -FREEFD equ 0 -ALLOCDIRHND equ 3 -FREEDIRHND equ 2 - -; POLL status bits -BIT_RECV equ 2 -BIT_DISCON equ 1 -BIT_CONN equ 0 - -NAMESPACE DEFAULT ; Clears namespace - -#endif - +;The MIT License +; +;Copyright (c) 2008 Dylan Smith +; +;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. + +; This file can be included in assembly language programs to give +; symbolic access to the public jump table entry points. + +; Avoid double inclusion +#ifndef __SPECTRANET_INC__ +#define __SPECTRANET_INC__ + +NAMESPACE Spectranet ; NAME PREFIX to avoid naming clash + +; Hardware page-in entry points +MODULECALL equ $3FF8 +MODULECALL_NOPAGE equ $28 +PAGEIN equ $3FF9 +PAGEOUT equ $007C +HLCALL equ $3FFA +IXCALL equ $3FFD + +; Port defines +CTRLREG equ $033B +CPLDINFO equ $023B + +; Jump table entry points +SOCKET equ $3E00 ; Allocate a socket +CLOSE equ $3E03 ; Close a socket +LISTEN equ $3E06 ; Listen for incoming connections +ACCEPT equ $3E09 ; Accept an incoming connection +BIND equ $3E0C ; Bind a local address to a socket +CONNECT equ $3E0F ; Connect to a remote host +SEND equ $3E12 ; Send data +RECV equ $3E15 ; Receive data +SENDTO equ $3E18 ; Send data to an address +RECVFROM equ $3E1B ; Receive data from an address +POLL equ $3E1E ; Poll a list of sockets +POLLALL equ $3E21 ; Poll all open sockets +POLLFD equ $3E24 ; Poll a single socket +GETHOSTBYNAME equ $3E27 ; Look up a hostname +PUTCHAR42 equ $3E2A ; 42 column print write a character +PRINT42 equ $3E2D ; 42 column print a null terminated string +CLEAR42 equ $3E30 ; Clear the screen and reset 42-col print +SETPAGEA equ $3E33 ; Sets page area A +SETPAGEB equ $3E36 ; Sets page area B +LONG2IPSTRING equ $3E39 ; Convert a 4 byte big endian long to an IP +IPSTRING2LONG equ $3E3C ; Convert an IP to a 4 byte big endian long +ITOA8 equ $3E3F ; Convert a byte to ascii +RAND16 equ $3E42 ; 16 bit PRNG +REMOTEADDRESS equ $3E45 ; Fill struct sockaddr_in +IFCONFIG_INET equ $3E48 ; Set IPv4 address +IFCONFIG_NETMASK equ $3E4B ; Set netmask +IFCONFIG_GW equ $3E4E ; Set gateway +INITHW equ $3E51 ; Set the MAC address and initial hw registers +GETHWADDR equ $3E54 ; Read the MAC address +DECONFIG equ $3E57 ; Deconfigure inet, netmask and gateway +MAC2STRING equ $3E5A ; Convert 6 byte MAC address to a string +STRING2MAC equ $3E5D ; Convert a hex string to a 6 byte MAC address +ITOH8 equ $3E60 ; Convert accumulator to hex string +HTOI8 equ $3E63 ; Convert hex string to byte in A +GETKEY equ $3E66 ; Get a key from the keyboard, and put it in A +KEYUP equ $3E69 ; Wait for key release +INPUTSTRING equ $3E6C ; Read a string into buffer at DE +GET_IFCONFIG_INET equ $3E6F ; Gets the current IPv4 address +GET_IFCONFIG_NETMASK equ $3E72 ; Gets the current netmask +GET_IFCONFIG_GW equ $3E75 ; Gets the current gateway address +SETTRAP equ $3E78 ; Sets the programmable trap +DISABLETRAP equ $3E7B ; Disables the programmable trap +ENABLETRAP equ $3E7E ; Enables the programmable trap +PUSHPAGEA equ $3E81 ; Pages a page into area A, pushing the old one +POPPAGEA equ $3E84 ; Restores the previous page in area A +PUSHPAGEB equ $3E87 ; Pages into area B pushing the old one +POPPAGEB equ $3E8A ; Restores the previous page in area B +PAGETRAPRETURN equ $3E8D ; Returns from a trap to page area B +TRAPRETURN equ $3E90 ; Returns from a trap that didn't page area B +ADDBASICEXT equ $3E93 ; Adds a BASIC command +STATEMENT_END equ $3E96 ; Check for statement end, exit at syntax time +EXIT_SUCCESS equ $3E99 ; Use this to exit successfully after cmd +PARSE_ERROR equ $3E9C ; Use this to exit to BASIC with a parse error +RESERVEPAGE equ $3E9F ; Reserve a page of static RAM +FREEPAGE equ $3EA2 ; Free a page of static RAM +REPORTERR equ $3EA5 ; report an error via BASIC + +; Filesystem functions +MOUNT equ $3EA8 +UMOUNT equ $3EAB +OPENDIR equ $3EAE +OPEN equ $3EB1 +UNLINK equ $3EB4 +MKDIR equ $3EB7 +RMDIR equ $3EBA +SIZE equ $3EBD +FREE equ $3EC0 +STAT equ $3EC3 +CHMOD equ $3EC6 +READ equ $3EC9 +WRITE equ $3ECC +LSEEK equ $3ECF +VCLOSE equ $3ED2 +VPOLL equ $3ED5 +READDIR equ $3ED8 +CLOSEDIR equ $3EDB +CHDIR equ $3EDE +GETCWD equ $3EE1 +RENAME equ $3EE4 +SETMOUNTPOINT equ $3EE7 +FREEMOUNTPOINT equ $3EEA +RESALLOC equ $3EED + + +; Definitions +ALLOCFD equ 1 +FREEFD equ 0 +ALLOCDIRHND equ 3 +FREEDIRHND equ 2 + +; POLL status bits +BIT_RECV equ 2 +BIT_DISCON equ 1 +BIT_CONN equ 0 + +NAMESPACE DEFAULT ; Clears namespace + +#endif + diff --git a/src/lib/arch/zx48k/stdlib/README b/src/lib/arch/zx48k/stdlib/README index 401199747..7d24b7e8a 100644 --- a/src/lib/arch/zx48k/stdlib/README +++ b/src/lib/arch/zx48k/stdlib/README @@ -1,4 +1,4 @@ -This directory contains a library of "basic" BASIC functions. -Some of them are related to ZX Spectrum BASIC (i.e. ATTR) while -others are more standard to FREE BASIC (csrlin or pos) - +This directory contains a library of "basic" BASIC functions. +Some of them are related to ZX Spectrum BASIC (i.e. ATTR) while +others are more standard to FREE BASIC (csrlin or pos) + diff --git a/src/lib/arch/zx48k/stdlib/SP/Fill.bas b/src/lib/arch/zx48k/stdlib/SP/Fill.bas index ab1a4ea39..3c979aee7 100644 --- a/src/lib/arch/zx48k/stdlib/SP/Fill.bas +++ b/src/lib/arch/zx48k/stdlib/SP/Fill.bas @@ -115,11 +115,6 @@ SPPFill: call SPGetScrnAddr ; de = screen address, b = pixel byte ex de,hl ; hl = screen address -; ld b, h -; ld c, l -; call 22B0h ; Uses ROM Pixel ADDR -; ld b, a - call bytefill ; b = fill byte jr c, viable pop bc diff --git a/src/lib/arch/zx48k/stdlib/cb/maskedsprites.bas b/src/lib/arch/zx48k/stdlib/cb/maskedsprites.bas new file mode 100644 index 000000000..27f390869 --- /dev/null +++ b/src/lib/arch/zx48k/stdlib/cb/maskedsprites.bas @@ -0,0 +1,1306 @@ +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyright (C) 2026 Conrado Badenas +' Ideas taken from +' https://github.com/boriel-basic/zxbasic/blob/main/src/lib/arch/zx48k/stdlib/memorybank.bas +' by Juan Segura (a.k.a. Duefectu), +' https://github.com/oisee/antique-toy/blob/main/chapters/ch16-sprites/draft.md +' by Alice Vinogradova (a.k.a. oisee), and +' https://youtu.be/nBHXtI1Y-xU?t=434 and https://youtu.be/-AUmmzDiGlE?t=434 +' by Benjamín (a.k.a. RetrobenSoft) +' +' Print Masked (AND+OR) Sprites, version 2026.04.05 +' ---------------------------------------------------------------- + +#ifndef __CB_MASKEDSPRITES__ + +REM Avoid recursive / multiple inclusion + +#define __CB_MASKEDSPRITES__ + +#include +#include + + +' ---------------------------------------------------------------- +' MaskedSprites_OUT_7FFD is a MACRO of ASM code, +' which uses a hack to ensure a good reading of the IFF2 flip-flop. +' Hack was found thanks to Pedro Picapiedra aka ProgramadorHedonista, +' who pointed me to this line of the Z80 User Manual: +' "If an interrupt occurs during execution of this instruction +' [LD A,I or LD A,R], the Parity flag contains a 0." +' ---------------------------------------------------------------- +#define MaskedSprites_OUT_7FFD \ + ld c,a ; save A in C \ + ld a,i ; IFF2=0/1=DI/EI is saved in PF=0/1=Odd/Even \ + jp pe,1f ; if PF=Even=1, it is sure that IFF2=1=EI \ + ld a,i ; read IFF2 again to ensure that IFF2=0=DI \ +1: ld a,c \ + ld bc,$7ffd ; Memory Bank control port \ + di ; Disable interrupts (IFF1,2=0) \ + ld ($5b5c),a ; Update BANKM system variable \ + out (c),a ; Set the bank \ + ret po ; Return with DI if IFF2=0=DI at the beginning \ + ei ; Return with EI if IFF2=1=EI at the beginning + + +' ---------------------------------------------------------------- +' Set a RAM bank in addresses $c000-$ffff, update BANKM, +' and return with INTerrupts preserved (unchanged) +' Only works on 128K and compatible models. +' Parameters: +' Ubyte: bank number 0,1,2,3,4,5,6,7 +' Changes: +' A, B, C +' Preserves: +' D, E, H, L are not used +' ---------------------------------------------------------------- +SUB FASTCALL SetBankPreservingINTs(bankNumber AS UByte) +ASM + and 7 ; make sure bankNumber = 0,1,2,3,4,5,6,7 + ld c,a ; C = A = bankNumber + ld a,($5b5c) ; Read BANKM system variable + and %11111000 ; Reset bank bits + or c ; Set bank bits to bankNumber + MaskedSprites_OUT_7FFD +END ASM +END SUB + + +' ---------------------------------------------------------------- +' Get which RAM bank is set in addresses $c000-$ffff +' Only works on 128K and compatible models. +' Preserves: +' B, C, D, E, H, L are not used +' Returns: +' UByte: bank number 0,1,2,3,4,5,6,7 +' ---------------------------------------------------------------- +#define MaskedSprites_GETBANK \ + ld a,($5b5c) ; Read BANKM system variable \ + and 7 ; Obtain bank bits +FUNCTION FASTCALL GetBankPreservingRegs() AS UByte +ASM + MaskedSprites_GETBANK +END ASM +END FUNCTION + + +' ---------------------------------------------------------------- +' Check whether memory paging works (128,+2,...) or not (16,48) +' Returns: +' UByte: 1 if paging works, 0 if it does not +' ---------------------------------------------------------------- +FUNCTION FASTCALL CheckMemoryPaging() AS UByte + DIM b,i AS UByte + DIM p AS UInteger + CONST address AS UInteger = $fffe + + b = GetBankPreservingRegs() + p = peek(UInteger, address) + for i=0 to 7 + SetBankPreservingINTs(i) + if p<>peek(UInteger, address) then 'there is another bank at address + if i<>b then SetBankPreservingINTs(b) + return 1 + end if + next i +' Here, we are in an unpaged Spectrum (48), or maybe... +' ...all banks hold the same value p at address. + + poke UInteger address, p bXOR $5555 ' we change value at address in RAM7 + SetBankPreservingINTs(0) + if p=peek(UInteger, address) then ' value at address in RAM0 is as before + SetBankPreservingINTs(7) + poke UInteger address, p + if 7<>b then SetBankPreservingINTs(b) + return 1 + end if + poke UInteger address, p ' memory paging does not work, we restore value at address + return 0 +ASM + +END ASM +END FUNCTION + + +' ---------------------------------------------------------------- +' Set the visible screen (either in bank5 or bank7) +' and updates the system variable BANKM. +' Only works on 128K and compatible models. +' Parameters: +' Ubyte: bank number 5 or 7 +' Preserves: +' D, E, H, L are not used +' ---------------------------------------------------------------- +SUB FASTCALL SetVisibleScreen(bankNumber AS UByte) +ASM + ; A = bankNumber 5 or 7 with screen to be visible + and %00000010 ; bit1 = 0/1 for bank5/7 + rla + rla ; bit3 = 0/1 for bank5/7 + ld c,a + ld a,($5b5c) ; Read BANKM system variable + and %11110111 ; Reset bit3 + or c ; Set screen in bank5/7 + MaskedSprites_OUT_7FFD +END ASM +END SUB + + +' ---------------------------------------------------------------- +' Returns the bank of visible screen (either 5 or 7) +' according to system variable BANKM. +' Only works on 128K and compatible models. +' Returns: +' UByte: bank 5 or 7 +' ---------------------------------------------------------------- +FUNCTION FASTCALL GetVisibleScreen() AS UByte +ASM + ld a,($5b5c) ; Read BANKM system variable + and %00001000 ; Get bit3 = 0/1 for bank5/7 + rra + rra ; bit1 = 0/1 for bank5/7 + or %00000101 ; A = 5/7 for bank5/7 +END ASM +END FUNCTION + + +' ---------------------------------------------------------------- +' Toggles the visible screen (from 5 to 7, or from 7 to 5) +' and updates the system variable BANKM. +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB FASTCALL ToggleVisibleScreen() + SetVisibleScreen(2 bXOR GetVisibleScreen() ) + '2 bXOR 5/7 = 7/5 +END SUB + + +' ---------------------------------------------------------------- +' Copy contents of screen5 to screen7 (display file + attribs) +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB FASTCALL CopyScreen5ToScreen7() + DIM b AS UByte + + b = GetBankPreservingRegs() + if b<>7 then SetBankPreservingINTs(7) + MemCopy($4000,$c000,$1b00) + if b<>7 then SetBankPreservingINTs(b) +END SUB + + +' ---------------------------------------------------------------- +' Copy contents of screen7 to screen5 (display file + attribs) +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB FASTCALL CopyScreen7ToScreen5() + DIM b AS UByte + + b = GetBankPreservingRegs() + if b<>7 then SetBankPreservingINTs(7) + MemCopy($c000,$4000,$1b00) + if b<>7 then SetBankPreservingINTs(b) +END SUB + + +' ---------------------------------------------------------------- +' Set ScreenBufferAddr and AttrBufferAddr to screen5 +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB FASTCALL SetDrawingScreen5() + SetScreenBufferAddr($4000)' ld (.core.SCREEN_ADDR),hl + SetAttrBufferAddr($5800)' ld (.core.SCREEN_ATTR_ADDR),hl +END SUB + + +' ---------------------------------------------------------------- +' Put screen7 at $c000 (in case it is not), and +' Set ScreenBufferAddr and AttrBufferAddr to screen7 +' Only works on 128K and compatible models. +' Returns: +' Bank7 is set at $c000, old bank is removed +' UByte: bank that was at $c000 (to restore it manually IYW) +' ---------------------------------------------------------------- +FUNCTION FASTCALL SetDrawingScreen7() AS UByte + DIM b AS UByte + + b = GetBankPreservingRegs() + if b<>7 then SetBankPreservingINTs(7) + SetScreenBufferAddr($c000)' ld (.core.SCREEN_ADDR),hl + SetAttrBufferAddr($d800)' ld (.core.SCREEN_ATTR_ADDR),hl + RETURN b +END FUNCTION + + +' ---------------------------------------------------------------- +' Toggle ScreenBufferAddr and AttrBufferAddr between screen5,7 +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB FASTCALL ToggleDrawingScreen() + SetScreenBufferAddr($8000 bXOR GetScreenBufferAddr() ) + SetAttrBufferAddr($8000 bXOR GetAttrBufferAddr() ) + '$8000 bXOR $4000/$c000 = $c000/$4000 + '$8000 bXOR $5800/$d800 = $d800/$5800 +END SUB + + +' ---------------------------------------------------------------- +' MaskedSpritesBackgroundSet = 0 or 1 is the Set of Backgrounds +' +' MaskedSpritesBackground(i) is the address where Background i begins +' +' NumberofMaskedSprites is a MACRO that should be #define-d +' before #include-ing this file +' +' MaskedSprites_USE_STACK_TRANSFER is a MACRO that should be #define-d +' if you want this library to use Stack PUSH+POP instructions to speed-up +' transfer of information between different parts of the RAM +' (this library will disable interrupts before using Stack Transfer) +' +' ChangeMaskedSpritesBackgroundSet() changes the Set of Backgrounds +' Returns: +' Byte: new value of MaskedSpritesBackgroundSet (IYW to use it) +' ---------------------------------------------------------------- +dim MaskedSpritesBackgroundSet AS UByte = 0 + +#define MaskedSpritesBackground(i) ( $db00+48*CAST(UInteger,i)+48*CAST(UInteger,NumberofMaskedSprites)*MaskedSpritesBackgroundSet ) + +FUNCTION FASTCALL ChangeMaskedSpritesBackgroundSet() AS UByte + MaskedSpritesBackgroundSet = MaskedSpritesBackgroundSet bXOR 1 + RETURN MaskedSpritesBackgroundSet +END FUNCTION + + +' ---------------------------------------------------------------- +' MaskedSprites_NEXT_ROW is a MACRO of ASM code, based on code from +' https://zonadepruebas.com/viewtopic.php?f=15&t=8372&start=40#p81507 +' and found by Joaquin Ferrero +' ---------------------------------------------------------------- +#define MaskedSprites_NEXT_ROW \ + ld a,e ; 4 A = E \ + sub 224 ; 7 A = E + 32 (SUB 224 is similar to +32) \ + ; CF = 0/1 iff E >=/< 224 iff a third is/isn't crossed \ + ld e,a ; 4 \ + sbc a,a ; 4 A = 0/255 \ + and 248 ; 7 A = 0/248 (248 = -8) \ + add a,d ; 4 A = D/D-8 iff a third is/isn't crossed \ + ld d,a ; 4 += 34 Ts + + +' ---------------------------------------------------------------- +' Save background and Draw sprite in screen +' Parameters: +' UByte: X coordinate (0:left to 240:right) +' UByte: Y coordinate (0:up to 176:down) +' UInteger: Address where background will be saved +' UInteger: Address where sprite image begins +' ---------------------------------------------------------------- +SUB FASTCALL SaveBackgroundAndDrawSprite(X AS UByte, Y AS UByte, backgroundAddr AS UInteger, spriteImageAddr AS UInteger) +ASM + PROC + LOCAL shiftright, shiftleft, noshift + LOCAL loopSR, loopSL, loopNS, loopR, loopL, branchSR, branchSL, branchNS + ; A = X + pop de ; returnAddr + exx + pop bc ; B = Y + ld c,a ; C = X +; BEGIN code from https://skoolkid.github.io/rom/asm/22AA.html + rlca + rlca + rlca ; A = %c4c3c2c1c0c7c6c5 + xor b + and %11000111 + xor b ; A = %c4c3b5b4b3c7c6c5 + rlca + rlca + ld e,a ; E = %b5b4b3c7c6c5c4c3 + ld a,b + and %11111000 + rra + rra + rra ; A = %.0.0.0b7b6b5b4b3 + xor b + and %11111000 + xor b + ld d,a ; D = %.0.0.0b7b6b2b1b0 +; END code from https://skoolkid.github.io/rom/asm/22AA.html + ld hl,(.core.SCREEN_ADDR) + add hl,de + ex de,hl ; DE = screenAddr where drawing will start + ld a,c; ; A = X + and 7 + jr z,noshift ; jump if X is a multiple of 8 (unlikely) + ; continue if sprite must be shifted + cp 4 ; is >= 4 ? + jp nc,shiftleft ; shift left if X MOD 8 = 4,5,6,7 + ; shift right if X MOD 8 = 1,2,3 +shiftright: + pop bc ; backgroundAddr + exx + pop hl ; spriteImageAddr + push de ; returnAddr + push ix + ld ixh,16 ; 16 scanlines + ld ixl,a ; IXl = X MOD 8 = 1,2,3,4 +loopSR: + ld a,(hl) ; mask1 + inc hl + ld c,(hl) ; graph1 + inc hl + ld d,(hl) ; mask2 + inc hl + ld e,(hl) ; graph2 + inc hl + push hl ; spriteImageAddr + + ld hl,$FF00 ; H = 255 , L = 0 + ld b,ixl +loopR: + scf ; 4 + rra ; 4; SCF + RRA injects a 1 in bit7 of A + rr d ; 8 + rr h ; 8 + srl c ; 8; ShiftRightLogical injects a 0 in bit7 of C + rr e ; 8 + rr l ; 8 + djnz loopR ; 4+4+8+8+8+8+8 = 48 Ts + ld b,a + push hl ; H,L = mask,graph 3rd byte + push de ; D,E = mask,graph 2nd byte + push bc ; B,C = mask,graph 1st byte + exx + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 1st byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 2nd byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 3rd byte done + dec e + dec e + + inc d + ld a,d + and 7 + jr z,branchSR ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + exx + pop hl ; spriteImageAddr + dec ixh + jp nz,loopSR + pop ix + ret +branchSR: + MaskedSprites_NEXT_ROW + exx + pop hl ; spriteImageAddr + dec ixh + jp nz,loopSR + pop ix + ret + +noshift: + pop bc ; backgroundAddr + pop hl ; spriteImageAddr + exx + push de ; returnAddr + exx + push ix + ld ixh,16 ; 16 scanlines +loopNS: + ld a,(de) ; screen + ld (bc),a; ; save + inc bc + and (hl); ; mask + inc hl + or (hl) ; graph + inc hl + ld (de),a ; 1st byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + and (hl); ; mask + inc hl + or (hl) ; graph + inc hl + ld (de),a ; 2nd byte done + dec e + + inc d + ld a,d + and 7 + jr z,branchNS ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + dec ixh + jp nz,loopNS + pop ix + ret +branchNS: + MaskedSprites_NEXT_ROW + dec ixh + jp nz,loopNS + pop ix + ret + +shiftleft: + pop bc ; backgroundAddr + exx + pop hl ; spriteImageAddr + push de ; returnAddr + push ix + ld ixh,16 ; 16 scanlines + sub 8 + neg ; A = 8 - oldA + ld ixl,a ; IXl = 8 - (X MOD 8) = 8 - 4,5,6,7 = 4,3,2,1 +loopSL: + ld a,(hl) ; mask1 + inc hl + ld c,(hl) ; graph1 + inc hl + ld d,(hl) ; mask2 + inc hl + ld e,(hl) ; graph2 + inc hl + push hl ; spriteImageAddr + + ld hl,$FF00 ; H = 255 , L = 0 + ld b,ixl +loopL: + sll d ; 8; ShiftLeftLogical injects a 1 in bit0 of D + rla ; 4 + rl h ; 8 + sla e ; 8; ShiftLeftArithmetic injects a 0 in bit0 of E + rl c ; 8 + rl l ; 8 + djnz loopL ; 8+4+8+8+8+8 = 44 Ts + ld b,a + push de ; D,E = mask,graph 3rd byte + push bc ; B,D = mask,graph 2nd byte + push hl ; H,L = mask,graph 1st byte + exx + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 1st byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 2nd byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 3rd byte done + dec e + dec e + + inc d + ld a,d + and 7 + jr z,branchSL ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + exx + pop hl ; spriteImageAddr + dec ixh + jp nz,loopSL + pop ix + ret +branchSL: + MaskedSprites_NEXT_ROW + exx + pop hl ; spriteImageAddr + dec ixh + jp nz,loopSL + pop ix + ret + ENDP +END ASM +END SUB + + +' ---------------------------------------------------------------- +' Restore background in screen +' Parameters: +' UByte: X coordinate (0:left to 240:right) +' UByte: Y coordinate (0:up to 176:down) +' UInteger: Address where saved background begins +' ---------------------------------------------------------------- +SUB FASTCALL RestoreBackground(X AS UByte, Y AS UByte, backgroundAddr AS UInteger) +ASM + PROC + LOCAL loop2b, loop3b, branch2b, branch3b + ; A = X + pop de ; returnAddr + exx + pop bc ; B = Y + ld c,a ; C = X +; BEGIN code from https://skoolkid.github.io/rom/asm/22AA.html + rlca + rlca + rlca ; A = %c4c3c2c1c0c7c6c5 + xor b + and %11000111 + xor b ; A = %c4c3b5b4b3c7c6c5 + rlca + rlca + ld e,a ; E = %b5b4b3c7c6c5c4c3 + ld a,b + and %11111000 + rra + rra + rra ; A = %.0.0.0b7b6b5b4b3 + xor b + and %11111000 + xor b + ld d,a ; D = %.0.0.0b7b6b2b1b0 +; END code from https://skoolkid.github.io/rom/asm/22AA.html + ld hl,(.core.SCREEN_ADDR) + add hl,de + ex de,hl ; DE = screenAddr where restoring will start + pop hl ; backgroundAddr + exx + push de ; returnAddr + exx + ld a,c; ; A = X + ld bc,$10FF ; B = 16, C = 255 (up to 255 LDIs do not change B) + and 7 + jr z,loop2b ; jump if X is a multiple of 8 (unlikely) + ; continue if restoring 3 bytes per scanline +; 3bytes per scanline +loop3b: + ldi ; 16 Ts vs 7+7+6+4=24 Ts + ldi + ldi ; 3 bytes background restored to screen + dec de ; last LDI could have increased D if initially E=253... + dec e ; ...so DEC DE restores D in that case + dec e + + inc d + ld a,d + and 7 + jr z,branch3b ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + djnz loop3b + ret +branch3b: + MaskedSprites_NEXT_ROW + djnz loop3b + ret +; 2bytes per scanline +loop2b: + ldi ; 16 Ts vs 7+7+6+4=24 Ts + ldi ; 2 bytes background restored to screen + dec de ; last LDI could have increased D if initially E=254... + dec e ; ...so DEC DE restores D in that case + + inc d + ld a,d + and 7 + jr z,branch2b ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + djnz loop2b + ret +branch2b: + MaskedSprites_NEXT_ROW + djnz loop2b + ret + ENDP +END ASM +END SUB + + +' ---------------------------------------------------------------- +' Structure of the Masked Sprites FileSystem (MSFS): +' +' MSFS consists of many blocks of 96 bytes +' MSFS starts at address stored in MaskedSpritesFileSystemStart, e.g., 56736 +' MSFS length is a multiple of 96 bytes, e.g., 8736 = 91*96 bytes +' With that length, MSFS ranges from 56736 to 65471 +' MSFS stores Images (mask+graph) of Masked Sprites +' Blocks used are marked in the FSB (https://en.wikipedia.org/wiki/Free-space_bitmap) +' +' First block of the MSFS (superblock, block number = 0) +' start+0 DEFB number of blocks in MSFS = bits of the FSB, e.g., 91 +' start+1 DEFB number of bytes of the FSB, e.g., 12 (91/8 = 11.4) +' start+2-13 DEFS 12 is the FSB (12 bytes = 96 bits is enough for 91 blocks) +' start+14-15 unused +' +' Block of an unshifted image (block number n = 0,...,90) +' start+n*96 \ +' ... | 16 bytes unused for n>0, superblock for n=0 +' start+n*96+15 / +' start+n*96+16-17 DEFW start+n*96+32 = start of unshifted image +' start+n*96+18-19 DEFW address of block for image shifted 1 pixel, or 0 if not used +' start+n*96+20-21 DEFW address of block for image shifted 2 pixels, or 0 if not used +' ... +' start+n*96+30-31 DEFW address of block for image shifted 7 pixels, or 0 if not used +' start+n*96+32-95 DEFS 64 the unshifted image (mask+graph) +' +' Block number 0 (n=0) is special because it contains +' 16 bytes for the superblock (including 2 unused bytes) + +' 16 bytes for addressing unshifted and shifted versions of the first image + +' 64 bytes for the first unshifted image +' +' Block of a shifted image (block number m = 1,...,90, note m>0) +' start+m*96+0 DEFS 96 the shifted image (mask+graph) +' ---------------------------------------------------------------- +dim MaskedSpritesFileSystemStart AS UInteger = 0 +' ---------------------------------------------------------------- +' MaskedSpritesFileSystemStart = address where Masked Sprites FileSystem starts +' +' InitMaskedSpritesFileSystem() inits the MSFS +' Returns: +' UInteger: value of MaskedSpritesFileSystemStart (IYW to use it) +' ---------------------------------------------------------------- +FUNCTION FASTCALL InitMaskedSpritesFileSystem() AS UInteger + DIM b,c AS UByte + DIM l AS UInteger + + c = CheckMemoryPaging() + if c then + b = GetBankPreservingRegs() + if b<>7 then SetBankPreservingINTs(7) + MaskedSpritesFileSystemStart = $db00+96*NumberofMaskedSprites + else + MaskedSpritesFileSystemStart = $db00+48*NumberofMaskedSprites + end if + l = -MaskedSpritesFileSystemStart + l = Int(l/96) + poke MaskedSpritesFileSystemStart, l ' MSFS blocks = FSB bits + if l=0 then STOP + l = 1+Int((l-1)/8) + poke MaskedSpritesFileSystemStart+1,l ' FSB bytes + if c then + if b<>7 then SetBankPreservingINTs(b) + end if + return MaskedSpritesFileSystemStart + +' Now, some assembly routines needed for next SUB/FUNCTIONs +ASM +; ---------------------------------------------------------------- +; Find memory addres in MSFS for the start of a block +; Parameters: +; L = blocknumber = n = 0,1,2,... (probably less than 200) +; Preserves: +; A, B, C are not used +; Returns: +; HL = start+n*96 +; DE = start +; ---------------------------------------------------------------- +FindMemoryAdressForBlockInMSFS: + PROC + ld h,0 + ld d,h + ld e,l ; HL = DE = n = blocknumber + add hl,de + add hl,de ; HL = DE*3 + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl ; HL = DE*3*(2^5) = DE*96 + ld de,(_MaskedSpritesFileSystemStart) + add hl,de; ; HL = start+n*96 + ret + ENDP + +; ---------------------------------------------------------------- +; Find First Unused Block in MSFS and (optionally) Book it +; Parameters: +; CarryFlag = 0/1 don't/do Book it +; Preserves: +; C is not used +; Returns: +; CarryFlag = 0 if found, 1 if not found +; A = FirstUnusedBlock if found +; HL = start+A*96 if found +; ---------------------------------------------------------------- +FindFirstUnusedBlockInMSFS: + PROC + LOCAL loop1, loop2, full, found, loop3, compute + ex af,af' ; saves CarryFlag + ld hl,(_MaskedSpritesFileSystemStart) + ld d,(hl) + ld e,d ; D = E = number of bits in the FSB = N + inc hl + inc hl ; HL points to the first byte in the FSB +loop1: + ld a,(hl) + ld b,8 +loop2: + rrca + jr nc,found + dec e + jr z,full + djnz loop2 + inc hl + jp loop1 +full: ; E = 0 + scf ; CarryFlag=1 = ERROR + ret +found: ; E = N,N-1,N-2,...,1 + ex af,af' + jr nc,compute + ex af,af' + rlca ; undo last RRCA + or 1 ; mark this block as used +loop3: + rrca ; finish the 8-bit rotation to... + djnz loop3 ; leave bits where they were + ld (hl),a ; effective booking +compute: + ld a,d ; A = N + sub e ; A = 0,1,2,...,N-1 = blocknumber + ld l,a + call FindMemoryAdressForBlockInMSFS + and a ; CarryFlag=0 = OK + ret ; A = blocknumber, HL = start+A*96 + ENDP +END ASM + +END FUNCTION + + +' ---------------------------------------------------------------- +' Get Number of Free Blocks in MSFS +' Returns: +' UByte: number of free blocks in MSFS +' ---------------------------------------------------------------- +FUNCTION FASTCALL GetNumberofFreeBlocksInMSFS() AS UByte +ASM + PROC + LOCAL loop1, loop2, exit + call _GetBankPreservingRegs + cp 7 ; ZeroFlag=0 (NZ) iff _GetBankPreservingRegs returns A<>7 + push af ; ZF and original RAM bank when FUNCTION was called + ld a,7 + call nz,_SetBankPreservingINTs ; set RAM7 if _GetBankPreservingRegs returns A<>7 + xor a ; A = 0 = number of reset bits in the FSB + ld hl,(_MaskedSpritesFileSystemStart) + ld d,(hl) ; D = number of bits in the FSB = N + ld e,a ; E = 0 always + inc hl + inc hl ; HL points to the first byte in the FSB +loop1: + ld c,(hl) + ld b,8 +loop2: + rr c + ccf ; CarryFlag = 0/1 = bit in the FSB is set/reset + adc a,e ; A += CarryFlag = number of reset bits in the FSB + dec d + jr z,exit ; return A if all bits in FSB have been checked + djnz loop2 + inc hl + jr loop1 +exit: + ex af,af' ; A' = number of free blocks in MSFS + pop af ; ZF and original RAM bank when FUNCTION was called + call nz,_SetBankPreservingINTs + ex af,af' ; A = number of free blocks in MSFS + ret + ENDP +END ASM +END FUNCTION + + +' ---------------------------------------------------------------- +' Register spriteImage in MSFS +' Parameters: +' UInteger: address where spriteImage begins +' Returns: +' UInteger: registry number in the MSFS = start+n*96+16 if OK +' 0 if not OK +' ---------------------------------------------------------------- +FUNCTION FASTCALL RegisterSpriteImageInMSFS(spriteImageAddr AS UInteger) AS UInteger +ASM + PROC + LOCAL full, exit + call _GetBankPreservingRegs + cp 7 ; ZeroFlag=0 (NZ) iff _GetBankPreservingRegs returns A<>7 + push af ; ZF and original RAM bank when FUNCTION was called + push hl ; spriteImageAddr + ld a,7 + call nz,_SetBankPreservingINTs ; set RAM7 if it was not set + scf + call FindFirstUnusedBlockInMSFS ; and book it (SCF) + jr c,full + ld bc,16 + add hl,bc + push hl ; HL = start+A*96+16 + ld d,h + ld e,l + inc de ; DE = start+A*96+17 + dec bc ; BC = 15 + ld (hl),0 + ldir ; reset RAM from start+A*96+16 to start+A*96+31 (incl.) + pop hl ; HL = start+A*96+16 + ld b,h + ld c,l ; BC = start+A*96+16 + ld (hl),e ; DE = start+A*96+32 (after last LDIR) + inc hl + ld (hl),d ; start+A*96+16-17 DEFW start+A*96+32 + pop hl ; spriteImageAddr + push bc ; BC = start+A*96+16 + ld bc,64 + ldir ; transfer from spriteImageAddr to start+A*96+32 + pop hl ; HL = start+A*96+16 +exit: + pop af ; ZF and original RAM bank when FUNCTION was called + call nz,_SetBankPreservingINTs + ret +full: + pop hl ; spriteImageAddr + ld hl,0 + jr exit + ENDP +END ASM +END FUNCTION + + +' ---------------------------------------------------------------- +' Register spriteGraph and spriteMask in MSFS +' (useful when different Graphs share the same Mask) +' +' Data in spriteGraph and spriteMask MUST be in "putchars format" +' +' Parameters: +' UInteger: address where spriteGraph begins +' UInteger: address where spriteMask begins +' Returns: +' UInteger: registry number in the MSFS = start+n*96+16 if OK +' 0 if not OK +' ---------------------------------------------------------------- +FUNCTION FASTCALL RegisterSpriteGraphAndMaskInMSFS(spriteGraphAddr AS UInteger,spriteMaskAddr AS UInteger) AS UInteger +ASM + PROC + LOCAL full, exit, loop1, loop2 + pop de ; returnAddr + pop bc ; spriteMaskAddr + push de ; returnAddr +; stack is empty. Now we will push data to be preserved + exx ; HL' = spriteGraphAddr, BC' = spriteMaskAddr + call _GetBankPreservingRegs + cp 7 ; ZeroFlag=0 (NZ) iff _GetBankPreservingRegs returns A<>7 + push af ; ZF and original RAM bank when FUNCTION was called + ld a,7 + call nz,_SetBankPreservingINTs ; set RAM7 if it was not set + scf + call FindFirstUnusedBlockInMSFS ; and book it (SCF) + jr c,full + ld bc,16 + add hl,bc + push hl ; HL = start+A*96+16 + ld d,h + ld e,l + inc de ; DE = start+A*96+17 + dec bc ; BC = 15 + ld (hl),0 + ldir ; reset RAM from start+A*96+16 to start+A*96+31 (incl.) + pop hl ; HL = start+A*96+16 + ld (hl),e ; DE = start+A*96+32 (after last LDIR) + inc hl + ld (hl),d ; start+A*96+16-17 DEFW start+A*96+32 + dec hl + push hl ; return value HL = start+A*96+16 + push de ; DE = start+A*96+32 DEFS 64 the unshifted image (mask+graph) + exx ; HL = spriteGraphAddr, BC = spriteMaskAddr + pop de ; DE = DEFS 64 the unshifted image (mask+graph) + push ix + push de ; DE = start+A*96+32 + ld ixl,16 +loop1: + ld a,(bc) ; mask + ld (de),a + inc bc + inc de + ld a,(hl) ; graph + ld (de),a + inc hl + inc de + inc de + inc de + dec ixl + jp nz,loop1 + pop de ; DE = start+A*96+32 + inc de + inc de ; DE = start+A*96+32 +2 + ld ixl,16 +loop2: + ld a,(bc) ; mask + ld (de),a + inc bc + inc de + ld a,(hl) ; graph + ld (de),a + inc hl + inc de + inc de + inc de + dec ixl + jp nz,loop2 + pop ix + pop hl ; return value HL = start+A*96+16 +exit: + pop af ; ZF and original RAM bank when FUNCTION was called + call nz,_SetBankPreservingINTs + ret +full: + ld hl,0 + jr exit + ENDP +END ASM +END FUNCTION + + +' ---------------------------------------------------------------- +' Save background and Draw sprite registered in the MSFS +' Parameters: +' UByte: X coordinate (0:left to 240:right) +' UByte: Y coordinate (0:up to 176:down) +' UInteger: address where background will be saved +' UInteger: registry number in the MSFS for the spriteImage +' ---------------------------------------------------------------- +SUB FASTCALL SaveBackgroundAndDrawSpriteRegisteredInMSFS(X AS UByte, Y AS UByte, backgroundAddr AS UInteger, spriteImageReg AS UInteger) +ASM + PROC + LOCAL full, makeShiftedImage, loopMSI, loopMSI1 + LOCAL useShiftedImage, loopUSI, branchUSI, exitUSI + LOCAL noshift, loopNS, branchNS, exitNS + ; A = X + pop de ; returnAddr + exx + pop bc ; B = Y + ld c,a ; C = X +; BEGIN code from https://skoolkid.github.io/rom/asm/22AA.html + rlca + rlca + rlca ; A = %c4c3c2c1c0c7c6c5 + xor b + and %11000111 + xor b ; A = %c4c3b5b4b3c7c6c5 + rlca + rlca + ld e,a ; E = %b5b4b3c7c6c5c4c3 + ld a,b + and %11111000 + rra + rra + rra ; A = %.0.0.0b7b6b5b4b3 + xor b + and %11111000 + xor b + ld d,a ; D = %.0.0.0b7b6b2b1b0 +; END code from https://skoolkid.github.io/rom/asm/22AA.html + ld hl,(.core.SCREEN_ADDR) + add hl,de + ex de,hl ; DE = screenAddr where drawing will start + ld a,c; ; A = X, keep it secret, keep it safe + pop bc ; BC = backgroundAddr + pop hl ; HL = spriteImageReg + exx + push de ; returnAddr + exx +; stack is empty. Now we will push data to be preserved + push ix + ld ixh,16 ; 16 scanlines + and 7 + jr z,noshift ; jump if X is a multiple of 8 (unlikely) + push de ; DE = screenAddr where drawing will start + push bc ; BC = backgroundAddr + ld b,0 + ld c,a ; BC = A = 1,2,3,4,5,6,7 + add hl,bc + add hl,bc ; HL = start+n*96+16+C*2 DEFW address of block for image shifted C pixels, or 0 if not used + ld e,(hl) + inc hl + ld d,(hl) ; DE = address for image shifted C pixels, or 0 if not used + ld a,d + or e + jp z,makeShiftedImage +;UseShiftedImage (USI) +useShiftedImage: + ex de,hl ; HL = address for image shifted C pixels + pop bc ; BC = backgroundAddr + pop de ; DE = screenAddr where drawing will start +#ifdef MaskedSprites_USE_STACK_TRANSFER + ld a,i ; IFF2=0/1=DI/EI is saved in PF=0/1=Odd/Even + jp pe,1f ; if PF=Even=1, it is sure that IFF2=1=EI + ld a,i ; read IFF2 again to ensure that IFF2=0=DI +1: ex af,af' + di + ld (exitUSI+1),sp + ld sp,hl +#endif +loopUSI: + ld a,(de) ; screen + ld (bc),a; ; save + inc bc +#ifdef MaskedSprites_USE_STACK_TRANSFER + pop hl + and l ; mask + or h ; graph +#else + and (hl); ; mask + inc l + or (hl) ; graph + inc hl +#endif + ld (de),a ; 1st byte done + inc e + + ld a,(de) ; screen + ld (bc),a; ; save + inc bc +#ifdef MaskedSprites_USE_STACK_TRANSFER + pop hl + and l ; mask + or h ; graph +#else + and (hl); ; mask + inc l + or (hl) ; graph + inc hl +#endif + ld (de),a ; 2nd byte done + inc e + + ld a,(de) ; screen + ld (bc),a; ; save + inc bc +#ifdef MaskedSprites_USE_STACK_TRANSFER + pop hl + and l ; mask + or h ; graph +#else + and (hl); ; mask + inc l + or (hl) ; graph + inc hl +#endif + ld (de),a ; 3rd byte done + dec e + dec e + + inc d + ld a,d + and 7 + jr z,branchUSI ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + dec ixh + jp nz,loopUSI +exitUSI: +#ifdef MaskedSprites_USE_STACK_TRANSFER + ld sp,$1234 + pop ix + ex af,af' + ret po ; Return with DI if IFF2=0=DI at the beginning + ei ; Return with EI if IFF2=1=EI at the beginning + ret +#else + pop ix + ret +#endif +branchUSI: + MaskedSprites_NEXT_ROW + dec ixh + jp nz,loopUSI +#ifdef MaskedSprites_USE_STACK_TRANSFER + jp exitUSI +#else + pop ix + ret +#endif +;NoShift (NS) +noshift: + ld a,(hl) ; HL = spriteImageReg + inc hl + ld h,(hl) + ld l,a ; HL = start of unshifted image +#ifdef MaskedSprites_USE_STACK_TRANSFER + ld a,i ; IFF2=0/1=DI/EI is saved in PF=0/1=Odd/Even + jp pe,1f ; if PF=Even=1, it is sure that IFF2=1=EI + ld a,i ; read IFF2 again to ensure that IFF2=0=DI +1: ex af,af' + di + ld (exitNS+1),sp + ld sp,hl +#endif +loopNS: + ld a,(de) ; screen + ld (bc),a; ; save + inc bc +#ifdef MaskedSprites_USE_STACK_TRANSFER + pop hl + and l ; mask + or h ; graph +#else + and (hl); ; mask + inc l + or (hl) ; graph + inc hl +#endif + ld (de),a ; 1st byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc +#ifdef MaskedSprites_USE_STACK_TRANSFER + pop hl + and l ; mask + or h ; graph +#else + and (hl); ; mask + inc l + or (hl) ; graph + inc hl +#endif + ld (de),a ; 2nd byte done + dec e + + inc d + ld a,d + and 7 + jr z,branchNS ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + dec ixh + jp nz,loopNS +exitNS: +#ifdef MaskedSprites_USE_STACK_TRANSFER + ld sp,$1234 + pop ix + ex af,af' + ret po ; Return with DI if IFF2=0=DI at the beginning + ei ; Return with EI if IFF2=1=EI at the beginning + ret +#else + pop ix + ret +#endif +branchNS: + MaskedSprites_NEXT_ROW + dec ixh + jp nz,loopNS +#ifdef MaskedSprites_USE_STACK_TRANSFER + jp exitNS +#else + pop ix + ret +#endif +;MakeShiftedImage (MSI) +makeShiftedImage: + ld a,8 + sub c ; C = X MOD 8 = 1,2,3,4,5,6,7 to the right + ld ixl,a ; IXl = 8 - C = 7,6,5,4,3,2,1 to the left + push hl ; HL = start+n*96+16+C*2 + 1 + scf + call FindFirstUnusedBlockInMSFS ; and book it (SCF) + jr c,full + ex de,hl ; DE = start+m*96 = address for the shiftedimage-to-be + pop hl ; HL = start+n*96+16+C*2 + 1 + ld (hl),d + dec hl + ld (hl),e; ; HL = start+n*96+16+C*2 DEFW address for the shifted image + ld b,0 + sbc hl,bc + sbc hl,bc + ld a,(hl) ; HL = spriteImageReg + inc hl + ld h,(hl) + ld l,a ; HL = start of unshifted image + push de ; DE = address for image shifted C pixels + push de ; two PUSH because we will POP one just before useShiftedImage + exx + pop hl ; HL' = address for the shiftedimage-to-be + exx +loopMSI: + ld a,(hl) ; mask1 + inc hl + ld c,(hl) ; graph1 + inc hl + ld d,(hl) ; mask2 + inc hl + ld e,(hl) ; graph2 + inc hl + push hl ; spriteImageAddr += 4 + ld hl,$FF00 ; H = 255 , L = 0 + ld b,ixl +loopMSI1: + sll d ; 8; ShiftLeftLogical injects a 1 in bit0 of D + rla ; 4 + rl h ; 8 + sla e ; 8; ShiftLeftArithmetic injects a 0 in bit0 of E + rl c ; 8 + rl l ; 8 + djnz loopMSI1; 8+4+8+8+8+8 = 44 Ts + ld b,a + push de ; D,E = mask,graph 3rd byte + push bc ; B,D = mask,graph 2nd byte + push hl ; H,L = mask,graph 1st byte + exx + pop de ; D',E' = mask,graph 1st byte + ld (hl),d + inc hl + ld (hl),e + inc hl + pop de ; D',E' = mask,graph 2nd byte + ld (hl),d + inc hl + ld (hl),e + inc hl + pop de ; D',E' = mask,graph 3rd byte + ld (hl),d + inc hl + ld (hl),e + inc hl ; HL' += 6 in this loop + exx + pop hl ; spriteImageAddr + dec ixh + jp nz,loopMSI + pop de ; DE = address for image shifted C pixels + ld ixh,16 ; 16 scanlines + jp useShiftedImage +full: + pop hl ; HL = start+n*96+16+C*2 + 1 + pop bc ; BC = backgroundAddr + pop de ; DE = screenAddr where drawing will start + pop ix + ret + ENDP +END ASM +END SUB + + +#endif + diff --git a/src/lib/arch/zx48k/stdlib/hex.bas b/src/lib/arch/zx48k/stdlib/hex.bas index be29fea23..a92f4f175 100644 --- a/src/lib/arch/zx48k/stdlib/hex.bas +++ b/src/lib/arch/zx48k/stdlib/hex.bas @@ -21,10 +21,10 @@ REM Avoid recursive / multiple inclusion ' function HEX ' ' Parameters: -' num : 32 bit unsigned integer numbre +' num : 32 bit unsigned integer number ' ' Returns: -' 4 chars str containing the HEX string representation +' 8 chars str containing the HEX string representation ' ---------------------------------------------------------------- function FASTCALL hex(num as ULong) as String asm diff --git a/src/lib/arch/zx48k/stdlib/mj/fourspriter.bas b/src/lib/arch/zx48k/stdlib/mj/fourspriter.bas index 1bb8beecd..0fa7e1793 100644 --- a/src/lib/arch/zx48k/stdlib/mj/fourspriter.bas +++ b/src/lib/arch/zx48k/stdlib/mj/fourspriter.bas @@ -8,7 +8,7 @@ '' ------------------------------------------------------------------------- '' Use/modify as you like. You may want to try Fourspriter MK3 by Britlion, '' which works differently and is really faster. Check ZX Basic forums at -'' http://www.boriel.com/forum/how-to-tutorials/topic400.html +'' https://forum.boriel.com/how-to-tutorials/topic400.html '' ------------------------------------------------------------------------- '' ------------------------------------------------------------------------- diff --git a/src/lib/arch/zx48k/stdlib/play.bas b/src/lib/arch/zx48k/stdlib/play.bas new file mode 100644 index 000000000..7d19d43e9 --- /dev/null +++ b/src/lib/arch/zx48k/stdlib/play.bas @@ -0,0 +1,661 @@ +' ---------------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyright (C) 2026 +' by Oleg S. Kostenko (a.k.a. Ollibony) +' ---------------------------------------------------------------------- + +#pragma once + +#pragma push(explicit) +#pragma push(strict) + +#pragma explicit = true +#pragma strict = true + +' --------------------------------------------------------------------------------------------------------------------- +' Plays the given MML strings on the AY music chip. +' The syntax is compatible with the Sinclair Basic Play routine. +' +' The documentation can be found here: https://fizyka.umk.pl/~jacek/zx/doc/man128/sp128p09.html +' +' More accurate (although not as well structured) documentation can be found in ZX Spectrum +3 manual here: +' https://zxspectrumvault.github.io/Manuals/Hardware/SpectrumPlus3Manual.html (see Chapter 8 Part 19) +' +' This is work in progress. + +' The following commands are already implemented: +' - cdefgab, CDEFGAB - gives pitch of note within current octave range +' - $ - flattens note following it +' - # - sharpens note following it +' - & - denotes a rest +' - 1-12 - sets length of notes +' - _ - creates tied notes (sums multiple durations) +' - O - followed by a number 0 to 8 sets current octave range +' - V - followed by a number 0 to 15 sets volume of notes +' - T - followed by a number 60 to 240 sets tempo of music +' - N - separates two numbers (actually, any unexpected character does this, including space) +' - () - specifies that the enclosed phrase must be repeated +' - H - specifies that the Play command must stop +' - W - followed by a number 0 to 7 sets volume effect +' - X - followed by a number 0 to 65535 sets duration of volume effect +' - U - turns on volume effect in the channel +' - M - followed by a number from 0 to 63 specifies the channels mixer mode +' +' The following commands are not implemented yet: +' - !! - comments +' - Y, Z - MIDI control (also you can't now pass more than 3 parameters to Play). +' +' Notes: +' +' - Unlike Sinclair Basic Play routine, this one doesn't insert tiny pauses between adjacent notes. +' I consider this to be a feature, rather than a bug. +' +' - There is no proper error handling implemented. In particular: +' - Unknown commands are silently ignored. +' - Out-of-range numbers cause undefined behavior. +' - An opening bracket is silently ignored if nesting level limit was reached (brackets can be maximum 4 levels deep). +' +' - This routine is more flexible in the way it parses commands than Sinclair Basic Play routine. +' Some combinations that give errors in Sinclair Basic, will play fine in this implementation. +' +' - This sub tends to provide more accurate timings than the original Sinclair Basic Play routine. +' However, perfect timing is not guaranteed, it may fluctuate depending on the complexity of the melody. +' +' - There can be subtle difference in behaviour between this sub and Sinclair Play, +' especially in undocumented edge cases (such as using ties together with triplets). +' +' - This sub disables interrupts at the start, and enables them in the end, +' regardless of whether they were enabled or not before. +' +' - This sub assumes the standard ZX Spectrum CPU speed of approximately 3.5 MHz. +' +' - The strings are passed by value and thus are copied on the routine invocation. +' The memory-effective version of this routine is yet to be implemented. +' +' - The compiler gives warning `[W150] Parameter 'microticks' is never used`. +' This is false positive and, unfortunately, cannot be suppressed on library level. +' +' - For some obscure reason, this sub doesn't work if compiler optimization level is set to 1 or lower. +' The default optimization level (2) is fine. +' +' - The `--enable-break` compiler option causes play slowdown. The sound chip is not silenced when Break is pressed, +' so it may feel as if the music 'freezed'. +' --------------------------------------------------------------------------------------------------------------------- +declare sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") + + +' Implementation ------------------------------------------------------------------------------------------------------ + +' How many ticks there are in a bar (a whole note). +' A tick is a single iteration of the main processing loop. +const _Play_TicksPerBar as ubyte = 96 + +const _Play_NotesPerOctave as ubyte = 12 +const _Play_TotalOctaves as ubyte = 9 + +' Maps note length to the corresponding number of ticks. +dim _Play_NoteLengthsInTicks(1 to 12) as ubyte => { _ + _Play_TicksPerBar / 16, _ '1 - semi-quaver + _Play_TicksPerBar / 16 * 1.5, _ '2 - dotted semi-quaver + _Play_TicksPerBar / 8, _ '3 - quaver + _Play_TicksPerBar / 8 * 1.5, _ '4 - dotted quaver + _Play_TicksPerBar / 4, _ '5 - crotchet + _Play_TicksPerBar / 4 * 1.5, _ '6 - dotted crotchet + _Play_TicksPerBar / 2, _ '7 - minim + _Play_TicksPerBar / 2 * 1.5, _ '8 - dotted minim + _Play_TicksPerBar, _ '9 - semi-breve + _Play_TicksPerBar / 24, _ '10 - triplet semi-quaver + _Play_TicksPerBar / 12, _ '11 - triplet quaver + _Play_TicksPerBar / 6 _ '12 - triplet crotchet +} + +' Divider values that need to be sent to the audio chip registers to play the notes. +' Note that the lowest notes in octave 0 are unplayable because of 12-bit overflow and probably wrong notes will be +' played instead of them. +' TODO: replace them with maximum possible values or zeros? See how it's done in Sinclair Play. +' TODO: in Sinclair Play it is possible to play notes in higher octaves (using several sharps in a row). +' Need to add more values to the table. +dim _Play_NoteDividers(0 to _Play_NotesPerOctave * _Play_TotalOctaves - 1) as uinteger = { _ +_ ' C C# D D# E F F# G G# A A# B + 6779, 6398, 6039, 5700, 5380, 5078, 4793, 4524, 4270, 4031, 3804, 3591, _ 'octave 0 + 3389, 3199, 3020, 2850, 2690, 2539, 2397, 2262, 2135, 2015, 1902, 1795, _ 'octave 1 + 1695, 1600, 1510, 1425, 1345, 1270, 1198, 1131, 1068, 1008, 951, 898, _ 'octave 2 + 847, 800, 755, 713, 673, 635, 599, 566, 534, 504, 476, 449, _ 'octave 3 + 424, 400, 377, 356, 336, 317, 300, 283, 267, 252, 238, 224, _ 'octave 4 + 212, 200, 189, 178, 168, 159, 150, 141, 133, 126, 119, 112, _ 'octave 5 + 106, 100, 94, 89, 84, 79, 75, 71, 67, 63, 59, 56, _ 'octave 6 + 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28, _ 'octave 7 + 26, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 14 _ 'octave 8 +} + +' Maps ascii code of a letter to the corresponding index of `_Play_NoteDividers` (octave 0). +dim _Play_NoteIndexes(code("A") to code("G")) as ubyte = { _ + /'A'/ 9, _ + /'B'/ 11, _ + /'C'/ 0, _ + /'D'/ 2, _ + /'E'/ 4, _ + /'F'/ 5, _ + /'G'/ 7 _ +} + +' Maps envelope shape number to its hardware equivalent. +dim _Play_EnvelopeShapes(0 to 7) as ubyte = { _ + /'0'/ %0000, _ + /'1'/ %0100, _ + /'2'/ %1011, _ + /'3'/ %1101, _ + /'4'/ %1000, _ + /'5'/ %1100, _ + /'6'/ %1110, _ + /'7'/ %1010 _ +} + +' Pointer to the current channel context. +' Made global for better performance, and also because it would be problematic to access it from nested subs if it were +' local (see 'Implementation note' on `Play`). +dim _Play_ContextPtr as uinteger + +' Switches to the first channel context. +#define _PLAY_CTX_FIRST_CHANNEL() let _Play_ContextPtr = ChannelContextBufferPtr + +' Swithes to the next channel context. +#define _PLAY_CTX_NEXT_CHANNEL() let _Play_ContextPtr = _Play_ContextPtr + ChannelContextSize + +' Gets the value of the given `type` at the given `offset` of the current channel context. +#define _PLAY_CTX_GET(type, offset) (peek(type, _Play_ContextPtr + (offset))) + +' Sets the given `value` of the given `type` to the given `offset` of the current channel context. +#define _PLAY_CTX_SET(type, offset, value) poke type _Play_ContextPtr + (offset), (value) + +' Arithmetically adds the given `value` to the value of the given `type` stored at the given `offset` +' of the current channel context. +#define _PLAY_CTX_ADD(type, offset, value) _PLAY_CTX_SET(type, offset, _PLAY_CTX_GET(type, offset) + (value)) + +' Write the value to the given register of the sound chip. +#define _PLAY_WRITE_TO_REGISTER(register, value) out $fffd, (register) : out $bffd, (value) + + +' Main sub. +' +' Implementation notes: +' +' If you want to extend the inner subs or functions, or add new ones, +' please beware that the current compiler version (v1.19.0-beta7 at the time of writing) +' doesn't support accessing outer local vars from the inner sub/function, +' if the inner sub/function has its own vars or params. +' +' The readability of the code was in many ways sacrificed in favor of performance, +' which is necessary to achieve accurate timings. +' +' TODO: add sub variants that accept strings byref, and that accept an array. +' TODO: check valid ranges of command parameters, handle integer overflow/underflow +' +sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") + + const CpuCyclesPerSecond as ulong = 3546900 + const CpuCyclesPerMicrotick as ubyte = 27 ' see the `Wait` sub + const BeatsPerBar as ubyte = 4 + const SecondsPerMinute as ubyte = 60 + const ChannelCount as ubyte = 3 + + const DefaultTempo as ubyte = 120 + const DefaultOctave as ubyte = 5 + const DefaultNoteLength as ubyte = 5 + const DefaultVolume as ubyte = 15 + const DefaultMixer as ubyte = %11111000 + const DefaultEnvelopeShape as ubyte = 0 + + ' Special volume value that enables hardware envelope. + const VolumeEnvelopeOn as ubyte = 16 + + ' General processing overhead compensation. Applied to every `Wait` invocation. + ' Determined experimentally. + ' TODO: adjust if needed after everything is implemented. + const TickGeneralOverheadInMicroticks as uinteger = 90 + + ' Overhead compensation for commands processing of a single channel. + ' Applied only on those ticks when there are commands processed for the channel. + ' Determined experimentally. + ' TODO: adjust if needed after everything is implemented. + const TickChannelCommandsOverheadInMicroticks as uinteger = 140 + + ' Channel numbers are zero-based, because it's better in terms of performance (less arithmetics in runtime needed). + const MaxChannel as ubyte = ChannelCount - 1 + + ' Maximum nesting level for brackets. + ' Initial level is 0. An opening bracket increases the level, a closing bracket decreases the level. + const MaxNestingLevel as ubyte = 4 + + ' Size of a single channel context in bytes. Don't forget to increase this if you add more context fields. + ' Note: this is used in macro `_PLAY_CTX_NEXT_CHANNEL`. + const ChannelContextSize as ubyte = 26 + + ' Channel context data is stored here. + dim ChannelContextBuffer(0 to ChannelContextSize * ChannelCount - 1) as ubyte + + ' Pointer to context data. + ' Note: this is used in macro `_PLAY_CTX_FIRST_CHANNEL`. + dim ChannelContextBufferPtr as uinteger + ChannelContextBufferPtr = @ChannelContextBuffer(0) + + ' Offsets of fields in a channel context. + ' If you add more fields, don't forget to increase `ChannelContextSize`. + const _CharPtr as ubyte = 0 ' (uinteger) Pointer to the current character in the channel string. + const _StringEndPtr as ubyte = 2 ' (uinteger) Pointer to the first byte after the last char of the channel + ' string. + const _TickBackCounter as ubyte = 4 ' (uinteger) How many ticks to wait before proceeding to the next command + ' in the channel string. Zero means we need to proceed now. + const _PrimaryNoteLengthInTicks as ubyte = 6 ' (uinteger) Current note length in ticks. + const _ActualNoteLengthInTicks as ubyte = 8 ' (uinteger) Actual note length in ticks. + ' Mostly the same as `_PrimaryNoteLengthInTicks`, + ' but may differ for triplets and ties. + const _ResetNoteLengthBackCount as ubyte = 10 ' (ubyte) How many notes left to play before actual note length must + ' be reset to primary note length. + const _BaseDividerIndex as ubyte = 11 ' (ubyte) Divider index (see `_Play_NoteDividers`) that corresponds to + ' note C of the current octave. + const _SemitoneAdjustment as ubyte = 12 ' (byte) How many semitones to add or subtract from the next note. + const _FinishedFlag as ubyte = 13 ' (ubyte) If nonzero, then the channel has finished playing. + const _Volume as ubyte = 14 ' (ubyte) Current volume. + ' If equals to `VolumeEnvelopeOn`, the envelope generator is used. + const _NestingLevel as ubyte = 15 ' (ubyte) Current brackets nesting level. + const _ReturnPtrs as ubyte = 16 ' (uinteger * (MaxNestingLevel+1)) + ' Stack of pointers to return to on closing brackets. + ' The zeroth element always points to the string start, + ' for infinite repeat when there is an unpaired closing bracket. + + ' Current tempo as beats per minute. A 'beat' is a 1/4-length note. + dim Tempo as ubyte + + ' Current tempo as microticks per tick. + ' For 'microtick' definition, see the `CpuCyclesPerMicrotick` const. + ' For 'tick' definition, see the `_Play_TicksPerBar` const. + dim MicroticksPerTick as uinteger + + ' Current hardware envelope shape. + ' See `_Play_EnvelopeShapes` for possible values. + dim EnvelopeShape as ubyte + + dim LastChar as ubyte ' Last char read by `ReadChar` sub. + dim LastNumber as uinteger ' Last number read by `ReadNumber` sub. + + ' Reads a char from the current channel string and puts it to `LastChar` variable. + ' Puts 0 if there's nothing left to read. + ' This is a sub, not a function, for performance reasons. + sub ReadChar + if _PLAY_CTX_GET(uinteger, _CharPtr) = _PLAY_CTX_GET(uinteger, _StringEndPtr) then + LastChar = 0 + return + end if + + LastChar = peek(_PLAY_CTX_GET(uinteger, _CharPtr)) + _PLAY_CTX_ADD(uinteger, _CharPtr, 1) + end sub + + ' Reads the number from the current channel string and puts it in `LastNumber` variable. + ' Puts 0 if the number is unreadable. + sub ReadNumber + LastNumber = 0 + + do + ReadChar + + if LastChar >= code("0") and LastChar <= code("9") then + LastNumber = LastNumber * 10 + LastChar - code("0") + else + ' The number has ended. + if LastChar <> 0 then + ' Step back if the string has not ended. + _PLAY_CTX_ADD(uinteger, _CharPtr, -1) + end if + exit do + end if + loop + end sub + + ' Sets octave for the current channel. + sub SetOctave(octave as ubyte) + _PLAY_CTX_SET(ubyte, _BaseDividerIndex, octave * _Play_NotesPerOctave) + end sub + + ' Sets `MicroticksPerTick` according to the current `Tempo` value. + sub UpdateMicroticksPerTick + MicroticksPerTick = _ + CpuCyclesPerSecond * SecondsPerMinute * BeatsPerBar _ + / CpuCyclesPerMicrotick / _Play_TicksPerBar / Tempo + end sub + + ' Waits for the given amount of microticks. + ' One microtick is 27 CPU cycles (if you change it, also change `CpuCyclesPerMicrotick` const). + ' TODO: is it possible to suppress the 'unused parameter' warning? + sub fastcall Wait(microticks as uinteger) + asm + proc + local loop + + ld bc, 1 ; bc = 1 + or a ; reset carry flag + loop: + sbc hl, bc ; microticks = microticks - bc ; 15 cycles + jr nz, loop ; if microticks <> 0 then goto loop ; 12 cycles + + endp + end asm + end sub + + ' Set tone pitch on the sound chip for a channel. + sub SetChipTonePitchDivider(channel as ubyte, divider as uinteger) + _PLAY_WRITE_TO_REGISTER(channel * 2, divider band $ff) + _PLAY_WRITE_TO_REGISTER(channel * 2 + 1, divider >> 8) + end sub + + ' Set pitch for noise generator of the sound chip. + sub SetChipNoisePitchDivider(divider as ubyte) + _PLAY_WRITE_TO_REGISTER(6, divider) + end sub + + ' Set volume on the sound chip for a channel. A special value `VolumeEnvelopeOn` means use envelope generator. + sub SetChipVolume(channel as ubyte, volume as ubyte) + _PLAY_WRITE_TO_REGISTER(channel + 8, volume) + end sub + + ' Sets envelope shape on the sound chip. + ' See `_Play_EnvelopeShapes` for possible values. + sub SetChipEnvelopeShape(shape as ubyte) + _PLAY_WRITE_TO_REGISTER(13, shape) + end sub + + ' Sets envelope period on the sound chip. + sub SetChipEnvelopePeriod(period as uinteger) + _PLAY_WRITE_TO_REGISTER(11, period band $ff) + _PLAY_WRITE_TO_REGISTER(12, period >> 8) + end sub + + ' Set mixer mode on the sound chip. + ' Bits 0, 1, 2 - if zero, enable tone on channels A, B, C correspondingly + ' Bits 3, 4, 5 - if zero, enable noise on channels A, B, C correspondingly + ' Bits 6, 7 - i/o ports, better set 1 there for now. (MIDI control? I don't know) + sub SetChipMixer(value as ubyte) + _PLAY_WRITE_TO_REGISTER(7, value) + end sub + + ' If macro `_PLAY_BENCHMARK_MODE` is defined, then interrupts are not disabled, and the system timer is used to + ' measure the duration of play. The duration in frames is printed on the screen after playing. + ' Note that interrupts add overhead and inaccuracies to timings, so this mode is not intended for fine-tuning + ' timings. This should only be used for differential analysis of code optimization efficiency. + #ifndef _PLAY_BENCHMARK_MODE + asm + di + end asm + #endif + + _PLAY_CTX_FIRST_CHANNEL() + _PLAY_CTX_SET(uinteger, _CharPtr, @channel0) + + _PLAY_CTX_NEXT_CHANNEL() + _PLAY_CTX_SET(uinteger, _CharPtr, @channel1) + + _PLAY_CTX_NEXT_CHANNEL() + _PLAY_CTX_SET(uinteger, _CharPtr, @channel2) + + dim channel as ubyte + dim strLen as uinteger + dim ptr as uinteger + + _PLAY_CTX_FIRST_CHANNEL() + + for channel = 0 to MaxChannel + ' We need low-level access to the strings to achieve good performance. + + ' dereference the pointer to the heap + ptr = peek(uinteger, _PLAY_CTX_GET(uinteger, _CharPtr)) + + ' read the string length + strLen = peek(uinteger, ptr) + + ' adjust the pointer so it points to the first char + ptr = ptr + 2 + + ' store pointers to the string start and end + _PLAY_CTX_SET(uinteger, _CharPtr, ptr) + _PLAY_CTX_SET(uinteger, _ReturnPtrs + 0, ptr) + _PLAY_CTX_SET(uinteger, _StringEndPtr, ptr + strLen) + + SetOctave DefaultOctave + _PLAY_CTX_SET(ubyte, _Volume, DefaultVolume) + _PLAY_CTX_SET(uinteger, _PrimaryNoteLengthInTicks, _Play_NoteLengthsInTicks(DefaultNoteLength)) + _PLAY_CTX_SET(uinteger, _ActualNoteLengthInTicks, _Play_NoteLengthsInTicks(DefaultNoteLength)) + _PLAY_CTX_SET(uinteger, _TickBackCounter, 0) + _PLAY_CTX_SET(byte, _SemitoneAdjustment, 0) + _PLAY_CTX_SET(ubyte, _ResetNoteLengthBackCount, 0) + _PLAY_CTX_SET(ubyte, _FinishedFlag, 0) + _PLAY_CTX_SET(ubyte, _NestingLevel, 0) + + _PLAY_CTX_NEXT_CHANNEL() + next channel + + EnvelopeShape = DefaultEnvelopeShape + Tempo = DefaultTempo + UpdateMicroticksPerTick + SetChipMixer DefaultMixer + + dim processedChannels as ubyte + dim finishedChannels as ubyte + dim lengthInTicks as uinteger + dim dividerIndex as ubyte + dim nestingLevel as ubyte + dim returnPtr as uinteger + dim returnPtrOffset as uinteger + dim halt as ubyte = 0 + dim volume as ubyte + dim mustInitEnvelope as ubyte + + #ifdef _PLAY_BENCHMARK_MODE + dim SysFrames as uinteger at $5c78 + dim startTime as uinteger = SysFrames + #endif + + do + finishedChannels = 0 + processedChannels = 0 + mustInitEnvelope = 0 + + _PLAY_CTX_FIRST_CHANNEL() + + for channel = 0 to MaxChannel + if _PLAY_CTX_GET(uinteger, _TickBackCounter) = 0 then + do + ReadChar + + if LastChar = 0 then + ' This channel has finished playing. + _PLAY_CTX_SET(ubyte, _FinishedFlag, 1) + ' While other channels are still playing, this one will do rests. + SetChipVolume channel, 0 + exit do + + else if LastChar = code("&") then + SetChipVolume channel, 0 + exit do + + else if (LastChar >= code("a") and LastChar <= code("g")) _ + or (LastChar >= code("A") and LastChar <= code("G")) then + + dividerIndex = _PLAY_CTX_GET(ubyte, _BaseDividerIndex) + + if LastChar >= code("a") then + ' if lowercase, then transpose down 1 octave and make uppercase + dividerIndex = dividerIndex - _Play_NotesPerOctave + LastChar = LastChar - 32 + end if + + dividerIndex = dividerIndex + _ + _Play_NoteIndexes(LastChar) + _PLAY_CTX_GET(byte, _SemitoneAdjustment) + + _PLAY_CTX_SET(byte, _SemitoneAdjustment, 0) + + SetChipTonePitchDivider channel, _Play_NoteDividers(dividerIndex) + + if channel = 0 then + ' Channel A pitch also defines noise generator pitch. + ' The formula taken from ROM disassembly. + ' Note: this affects all channels which have noise enabled. + SetChipNoisePitchDivider ((bnot dividerIndex) band $7f) >> 2 + end if + + volume = _PLAY_CTX_GET(ubyte, _Volume) + SetChipVolume channel, volume + + if volume = VolumeEnvelopeOn then + ' If volume envelope is enabled on any channel, we need to re-initialize volume shape + ' on sound chip on every note start, for the envelope to start when the note starts. + ' Note that this affects all channels at once, but this is the limitation of hardware. + mustInitEnvelope = 1 + end if + + exit do + + else if LastChar = code("$") then + _PLAY_CTX_ADD(byte, _SemitoneAdjustment, -1) + + else if LastChar = code("#") then + _PLAY_CTX_ADD(byte, _SemitoneAdjustment, 1) + + else if LastChar >= code("0") and LastChar <= code("9") + _PLAY_CTX_ADD(uinteger, _CharPtr, -1) ' step back + ReadNumber + lengthInTicks = _Play_NoteLengthsInTicks(LastNumber) + + _PLAY_CTX_SET(uinteger, _ActualNoteLengthInTicks, lengthInTicks) + + if LastNumber >= 10 then + ' triplet mode (temporary length) + _PLAY_CTX_SET(ubyte, _ResetNoteLengthBackCount, 3) + else + _PLAY_CTX_SET(uinteger, _PrimaryNoteLengthInTicks, lengthInTicks) + end if + + else if LastChar = code("_") then + ' TODO: ties with triplets work differently in Sinclair ROM. + ReadNumber + lengthInTicks = _Play_NoteLengthsInTicks(LastNumber) + + _PLAY_CTX_SET(ubyte, _ResetNoteLengthBackCount, 1) + _PLAY_CTX_ADD(uinteger, _ActualNoteLengthInTicks, lengthInTicks) + _PLAY_CTX_SET(uinteger, _PrimaryNoteLengthInTicks, lengthInTicks) + + else if LastChar = code("(") then + nestingLevel = _PLAY_CTX_GET(ubyte, _NestingLevel) + + if nestingLevel < MaxNestingLevel then + nestingLevel = nestingLevel + 1 + returnPtrOffset = _ReturnPtrs + nestingLevel * 2 + _PLAY_CTX_SET(ubyte, _NestingLevel, nestingLevel) + _PLAY_CTX_SET(uinteger, returnPtrOffset, _PLAY_CTX_GET(uinteger, _CharPtr)) + end if + + else if LastChar = code(")") then + nestingLevel = _PLAY_CTX_GET(ubyte, _NestingLevel) + returnPtrOffset = _ReturnPtrs + nestingLevel * 2 + returnPtr = _PLAY_CTX_GET(uinteger, returnPtrOffset) + + if returnPtr = 0 then + ' already repeated - decrease nesting level + nestingLevel = nestingLevel - 1 + _PLAY_CTX_SET(ubyte, _NestingLevel, nestingLevel) + else + ' return to the repeat point + _PLAY_CTX_SET(uinteger, _CharPtr, returnPtr) + + ' clear return pointer unless it is zero level (in which case we loop infinitely) + if nestingLevel > 0 then + _PLAY_CTX_SET(uinteger, returnPtrOffset, 0) + end if + end if + + else if LastChar = code("O") then + ReadNumber + SetOctave LastNumber + + else if LastChar = code("V") then + ReadNumber + _PLAY_CTX_SET(ubyte, _Volume, LastNumber) + + else if LastChar = code("U") then + _PLAY_CTX_SET(ubyte, _Volume, VolumeEnvelopeOn) + + else if LastChar = code("X") then + ReadNumber + SetChipEnvelopePeriod LastNumber + + else if LastChar = code("W") then + ReadNumber + EnvelopeShape = _Play_EnvelopeShapes(LastNumber) + + else if LastChar = code("M") then + ReadNumber + ' invert the bits and push directly to the chip + SetChipMixer bnot LastNumber + + else if LastChar = code("T") then + ReadNumber + + if channel = 0 then + Tempo = LastNumber + UpdateMicroticksPerTick + end if + + else if LastChar = code("H") then + halt = 1 + exit for + + ' TODO: process other commands here + end if + loop + + if _PLAY_CTX_GET(ubyte, _ResetNoteLengthBackCount) > 0 then + _PLAY_CTX_ADD(ubyte, _ResetNoteLengthBackCount, -1) + else + _PLAY_CTX_SET(uinteger, _ActualNoteLengthInTicks, _PLAY_CTX_GET(uinteger, _PrimaryNoteLengthInTicks)) + end if + + _PLAY_CTX_SET(uinteger, _TickBackCounter, _PLAY_CTX_GET(uinteger, _ActualNoteLengthInTicks)) + + processedChannels = processedChannels + 1 + end if + + _PLAY_CTX_ADD(uinteger, _TickBackCounter, -1) + + finishedChannels = finishedChannels + _PLAY_CTX_GET(ubyte, _FinishedFlag) + + _PLAY_CTX_NEXT_CHANNEL() + next channel + + if mustInitEnvelope then + SetChipEnvelopeShape EnvelopeShape + end if + + if halt then + for channel = 0 to MaxChannel + SetChipVolume channel, 0 + next channel + exit do + end if + + Wait MicroticksPerTick _ + - TickGeneralOverheadInMicroticks _ + - TickChannelCommandsOverheadInMicroticks * processedChannels + loop until finishedChannels = ChannelCount + + #ifdef _PLAY_BENCHMARK_MODE + print "Play duration: "; SysFrames - startTime; " frames." + #endif + + asm + ei + end asm +end sub + +#pragma pop(explicit) +#pragma pop(strict) diff --git a/src/lib/arch/zx48k/stdlib/print42.bas b/src/lib/arch/zx48k/stdlib/print42.bas index bbe8ba44c..6c1d84d3d 100644 --- a/src/lib/arch/zx48k/stdlib/print42.bas +++ b/src/lib/arch/zx48k/stdlib/print42.bas @@ -39,9 +39,15 @@ asm LOCAL examineChar examineChar: LD A,(HL) ; Grab the character at our pointer position - CP 128 ; Too high to print? + CP 165 ; Too high to print? JR NC, nextChar ; Then we go to the next + CP 144 + JR NC, prn ; char is a UDG + + CP 128 + JR NC, nextChar ; char is a block graphic + CP 22 ; Is this an AT? JR NZ, isNewline ; If not jump over the AT routine to isNewline @@ -130,6 +136,9 @@ printachar: PUSH HL ; Store H'L' where we can get it. EXX + CP 144 + jr nc,printudg + ld c, a ; Put a copy of the character in C ld h, 0 ld l, a ; Put the Character in HL @@ -148,6 +157,19 @@ printachar: ld c, l ; Copy our character data address into BC jr printdata ; We have our data source, so we print it. +LOCAL printudg +printudg + sub 144 ; get the udg offset + ld hl,$5C7B ; examine system variables for udg address + ld e,(hl) ; lsb for udg address + inc hl + ld d,(hl) ; mbs for udg address + ld l,a ; character offset for udg + call mult8 ; multiplies L by 8 and adds in DE [so HL points at our table entry] + ld b, h + ld c, l ; Copy our character data address into BC + jr printdata ; We have our data source, so we print it. + LOCAL calcChar calcChar: ; this is the calculate from the ROM data option ; a holds the column kill data diff --git a/src/lib/arch/zx48k/stdlib/scroll.bas b/src/lib/arch/zx48k/stdlib/scroll.bas index 33cfa9919..06661eb6f 100644 --- a/src/lib/arch/zx48k/stdlib/scroll.bas +++ b/src/lib/arch/zx48k/stdlib/scroll.bas @@ -1,8 +1,9 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License ' -' Copyleft (k) 2008 +' Copyleft (k) 2008, 2025 ' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' and Conrado Badenas ' ' Use this file as a template to develop your own library file ' ---------------------------------------------------------------- @@ -25,6 +26,709 @@ sub fastcall ScrollRight(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) asm push namespace core + PROC + LOCAL LOOP1, LOOP2, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + +; Read parameters, return if they are bad + ; a = x1 + pop hl ; RET address + pop bc ; b = y1 + pop de ; d = x2 + ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now + + ld c, a ; BC = y1x1 + ld a, d + sub c + ret c ; x1 > x2 + + ld a, h + sub b + ret c ; y1 > y2 + +; Compute height and masks + inc a + ld e, a ; e = y2 - y1 + 1 + + ld a,c ;e.g. x1 = 3, mask1 = %11100000, CPL = %00011111 + and 7 + inc a + ld b,a ;B = 1 + (x1 MOD 8) = 1,2,...,8 for x1 = 0,1,...,7 + 8*n + xor a +MASK1: + rra + scf ;inject B-1 1s from the left + djnz MASK1 + ld (AND1+1),a ;e.g. %11100000 + ld (AND5+1),a + cpl + ld (AND2+1),a ;e.g. %00011111 + ld (AND7+1),a + + ld a,d ;e.g. x2 = 5, mask2 = %11111100, CPL = %00000011 + and 7 + inc a + ld b,a ;B = 1 + (x2 MOD 8) = 1,2,...,8 for x2 = 0,1,...,7 + 8*n + xor a +MASK2: + scf ;inject B 1s from the left + rra + djnz MASK2 + ld (AND4+1),a ;e.g. %11111100 + ld (AND8+1),a + cpl + ld (AND3+1),a ;e.g. %00000011 + ld (AND6+1),a + +; Compute Ncols and Display File address, and choose branch + ld a,c ;x1 + and %11111000 + rrca + rrca + rrca + ld b,a ;col1 + ld a,d ;x2 + and %11111000 + rrca + rrca + rrca ;col2 + sub b ;A = Ncols - 1 = col2 - col1 + ex af,af' ;save Ncols-1 and ZeroFlag + + ld b, h ; BC = y2x1 + ld a, 191 + call 22ACh ; 2 bytes after the 'PIXEL ADDRESS' subroutine, because +; https://skoolkid.github.io/rom/asm/22AA.html starts with LD A,175 + res 6, h ; Starts from 0 + ld bc, (SCREEN_ADDR) + add hl, bc ; Now current offset + + ex af,af' ;load Ncols-1 and ZeroFlag + jr z,NEQ1 + +; N > 1 (there are 2 or more columns) + dec a ;A = Ncols - 2 + ld d,a ;D = N-2 +LOOP1: + push hl +; Scroll column 1 + ld a,(hl) +AND1: + and %11100000 ;e.g. x1 = 3 + ld c,a ;get out-window part of byte + ld a,(hl) +AND2: + and %00011111 ;e.g. x1 = 3 + rra ;scroll in-window part of byte + rl b ;CF is stored as bit0 of B + or c ;put out-window part of byte + ld (hl),a + inc hl +; Scroll columns 2,3,...,N-1 + ld a,d ;N-2 + and a ;A=0 iff N=2 + jr z,COLUMNN + rr b ;CF is on stage + ld b,a +LOOP2: + rr (hl) + inc hl + djnz LOOP2 + rl b ;CF is stored as bit0 of B +; Scroll column N +COLUMNN: + ld a,(hl) +AND3: + and %00000011 ;e.g. x2 = 5 + ld c,a ;get out-window part of byte + ld a,(hl) + rr b ;CF is on stage + rra ;scroll in-window part of byte +AND4: + and %11111100 ;e.g. x2 = 5 + or c ;put out-window part of byte + ld (hl),a +; Scroll another line + pop hl + dec e + ret z + call SP.PixelDown + jp LOOP1 + +; N = 1 (there is only one column) +NEQ1: + ld b,(hl) + ld a,b +AND5: + and %11100000 ;e.g. x1 = 3 + ld c,a + ld a,b +AND6: + and %00000011 ;e.g. x2 = 5 + or c + ld c,a ;get out-window part of byte + ld a,b +AND7: + and %00011111 ;e.g. x1 = 3 + rra ;scroll in-window part of byte +AND8: + and %11111100 ;e.g. x2 = 5 + or c ;put out-window part of byte + ld (hl),a +; Scroll another line + dec e + ret z + call SP.PixelDown + jp NEQ1 + ENDP + + pop namespace + end asm +end sub + +' ---------------------------------------------------------------- +' sub ScrollLeft +' pixel by pixel left scroll +' scrolls 1 pixel left the window defined by (x1, y1, x2, y2) +' ---------------------------------------------------------------- +sub fastcall ScrollLeft(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) + asm + push namespace core + + PROC + LOCAL LOOP1, LOOP2, MASK1, MASK2, COLUMN1, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + +; Read parameters, return if they are bad + ; a = x1 + pop hl ; RET address + pop bc ; b = y1 + pop de ; d = x2 + ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now + + ld c, a ; BC = y1x1 + ld a, d + sub c + ret c ; x1 > x2 + + ld a, h + sub b + ret c ; y1 > y2 + +; Compute height and masks + inc a + ld e, a ; e = y2 - y1 + 1 + + ld a,c ;e.g. x1 = 3, mask1 = %11100000, CPL = %00011111 + and 7 + inc a + ld b,a ;B = 1 + (x1 MOD 8) = 1,2,...,8 for x1 = 0,1,...,7 + 8*n + xor a +MASK1: + rra + scf ;inject B-1 1s from the left + djnz MASK1 + ld (AND3+1),a ;e.g. %00000011 + ld (AND6+1),a + cpl + ld (AND4+1),a ;e.g. %11111100 + ld (AND8+1),a + + ld a,d ;e.g. x2 = 5, mask2 = %11111100, CPL = %00000011 + and 7 + inc a + ld b,a ;B = 1 + (x2 MOD 8) = 1,2,...,8 for x2 = 0,1,...,7 + 8*n + xor a +MASK2: + scf ;inject B 1s from the left + rra + djnz MASK2 + ld (AND2+1),a ;e.g. %00011111 + ld (AND7+1),a + cpl + ld (AND1+1),a ;e.g. %11100000 + ld (AND5+1),a + +; Compute Ncols and Display File address, and choose branch + ld a,c ;x1 + and %11111000 + rrca + rrca + rrca + ld b,a ;col1 + ld a,d ;x2 + and %11111000 + rrca + rrca + rrca ;col2 + sub b ;A = Ncols - 1 = col2 - col1 + ex af,af' ;save Ncols-1 and ZeroFlag + + ld c, d + ld b, h ; BC = y2x2 + ld a, 191 + call 22ACh ; 2 bytes after the 'PIXEL ADDRESS' subroutine, because +; https://skoolkid.github.io/rom/asm/22AA.html starts with LD A,175 + res 6, h ; Starts from 0 + ld bc, (SCREEN_ADDR) + add hl, bc ; Now current offset + + ex af,af' ;load Ncols-1 and ZeroFlag + jr z,NEQ1 + +; N > 1 (there are 2 or more columns) + dec a ;A = Ncols - 2 + ld d,a ;D = N-2 +LOOP1: + push hl +; Scroll column N + ld a,(hl) +AND1: + and %00000011 ;e.g. x2 = 5 + ld c,a ;get out-window part of byte + ld a,(hl) +AND2: + and %11111100 ;e.g. x2 = 5 + rla ;scroll in-window part of byte + rl b ;CF is stored as bit0 of B + or c ;put out-window part of byte + ld (hl),a + dec hl +; Scroll columns N-1,...,3,2 + ld a,d ;N-2 + and a ;A=0 iff N=2 + jr z,COLUMN1 + rr b ;CF is on stage + ld b,a +LOOP2: + rl (hl) + dec hl + djnz LOOP2 + rl b ;CF is stored as bit0 of B +; Scroll column 1 +COLUMN1: + ld a,(hl) +AND3: + and %11100000 ;e.g. x1 = 3 + ld c,a ;get out-window part of byte + ld a,(hl) + rr b ;CF is on stage + rla ;scroll in-window part of byte +AND4: + and %00011111 ;e.g. x1 = 3 + or c ;put out-window part of byte + ld (hl),a +; Scroll another line + pop hl + dec e + ret z + call SP.PixelDown + jp LOOP1 + +; N = 1 (there is only one column) +NEQ1: + ld b,(hl) + ld a,b +AND5: + and %00000011 ;e.g. x2 = 5 + ld c,a + ld a,b +AND6: + and %11100000 ;e.g. x1 = 3 + or c + ld c,a ;get out-window part of byte + ld a,b +AND7: + and %11111100 ;e.g. x2 = 5 + rla ;scroll in-window part of byte +AND8: + and %00011111 ;e.g. x1 = 3 + or c ;put out-window part of byte + ld (hl),a +; Scroll another line + dec e + ret z + call SP.PixelDown + jp NEQ1 + ENDP + + pop namespace + end asm +end sub + + +' ---------------------------------------------------------------- +' sub ScrollUp +' pixel by pixel up scroll +' scrolls 1 pixel up the window defined by (x1, y1, x2, y2) +' ---------------------------------------------------------------- +sub fastcall ScrollUp(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) + asm + push namespace core + + PROC + LOCAL LOOP1, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + LOCAL EMPTYLINE + +; Read parameters, return if they are bad + ; a = x1 + pop hl ; RET address + pop bc ; b = y1 + pop de ; d = x2 + ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now + + ld c, a ; BC = y1x1 + ld a, d + sub c + ret c ; x1 > x2 + + ld a, h + sub b + ret c ; y1 > y2 + +; Compute height and masks + push ix + inc a + ld ixL,a;ixL = y2 - y1 + 1 + + ld a,c ;e.g. x1 = 3, mask1 = %11100000, CPL = %00011111 + and 7 + inc a + ld b,a ;B = 1 + (x1 MOD 8) = 1,2,...,8 for x1 = 0,1,...,7 + 8*n + xor a +MASK1: + rra + scf ;inject B-1 1s from the left + djnz MASK1 + ld (AND1+1),a ;e.g. %11100000 + ld (AND5+1),a + cpl + ld (AND2+1),a ;e.g. %00011111 + ld (AND7+1),a + + ld a,d ;e.g. x2 = 5, mask2 = %11111100, CPL = %00000011 + and 7 + inc a + ld b,a ;B = 1 + (x2 MOD 8) = 1,2,...,8 for x2 = 0,1,...,7 + 8*n + xor a +MASK2: + scf ;inject B 1s from the left + rra + djnz MASK2 + ld (AND4+1),a ;e.g. %11111100 + ld (AND8+1),a + cpl + ld (AND3+1),a ;e.g. %00000011 + ld (AND6+1),a + +; Compute Ncols and Display File address, and choose branch + ld a,c ;x1 + and %11111000 + rrca + rrca + rrca + ld b,a ;col1 + ld a,d ;x2 + and %11111000 + rrca + rrca + rrca ;col2 + sub b ;A = Ncols - 1 = col2 - col1 + ex af,af' ;save Ncols-1 and ZeroFlag + + ld b, h ; BC = y2x1 + ld a, 191 + call 22ACh ; 2 bytes after the 'PIXEL ADDRESS' subroutine, because +; https://skoolkid.github.io/rom/asm/22AA.html starts with LD A,175 + res 6, h ; Starts from 0 + ld bc, (SCREEN_ADDR) + add hl, bc ; Now current offset + + ex af,af' ;load Ncols-1 and ZeroFlag + jr z,NEQ1 + +; N > 1 (there are 2 or more columns) + dec a ;A = Ncols - 2 + ld ixH,a ;save Ncols-2 + ld b,0 +LOOP1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE ;HL at empty line + call nz,SP.PixelDown ;HL at line below + inc ixL ;restore iterative variable for a second check + push hl +; Scroll column 1 + ld a,(hl) +AND2: + and %00011111 ;e.g. x1 = 3 + ld c,a ;get in-window part of byte + ld a,(de) +AND1: + and %11100000 ;e.g. x1 = 3 + or c ;put in-window part of byte + ld (de),a + inc hl + inc de +; Scroll columns 2,3,...,N-1 + ld a,ixH ;load Ncols-2 + and a + jr z,COLUMNN + ld c,a + ldir +; Scroll column N +COLUMNN: + ld a,(hl) +AND4: + and %11111100 ;e.g. x2 = 5 + ld c,a ;get in-window part of byte + ld a,(de) +AND3: + and %00000011 ;e.g. x2 = 5 + or c ;put in-window part of byte + ld (de),a +; Scroll another line + pop hl + dec ixL + jp nz,LOOP1 + pop ix + ret + +; N = 1 (there is only one column) +NEQ1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE ;HL at empty line + call nz,SP.PixelDown ;HL at line below + inc ixL ;restore iterative variable for a second check + + ld a,(hl) +AND7: + and %00011111 ;e.g. x1 = 3 +AND8: + and %11111100 ;e.g. x2 = 5 + ld c,a ;get in-window part of byte + ld a,(de) +AND5: + and %11100000 ;e.g. x1 = 3 + ld b,a + ld a,(de) +AND6: + and %00000011 ;e.g. x2 = 5 + or b + or c ;put in-window part of byte + ld (de),a +; Scroll another line + dec ixL + jp nz,NEQ1 + pop ix + ret + + defs 32,0 ;empty line with 32 zero-bytes +EMPTYLINE: + ld hl,EMPTYLINE-32 + ENDP + + pop namespace + end asm +end sub + + +' ---------------------------------------------------------------- +' sub ScrollDown +' pixel by pixel down scroll +' scrolls 1 pixel down the window defined by (x1, y1, x2, y2) +' ---------------------------------------------------------------- +sub fastcall ScrollDown(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) + asm + push namespace core + + PROC + LOCAL LOOP1, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + LOCAL EMPTYLINE + +; Read parameters, return if they are bad + ; a = x1 + pop hl ; RET address + pop bc ; b = y1 + pop de ; d = x2 + ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now + + ld c, a ; BC = y1x1 + ld a, d + sub c + ret c ; x1 > x2 + + ld a, h + sub b + ret c ; y1 > y2 + +; Compute height and masks + push ix + inc a + ld ixL,a;ixL = y2 - y1 + 1 + ld h,b ;y1 + + ld a,c ;e.g. x1 = 3, mask1 = %11100000, CPL = %00011111 + and 7 + inc a + ld b,a ;B = 1 + (x1 MOD 8) = 1,2,...,8 for x1 = 0,1,...,7 + 8*n + xor a +MASK1: + rra + scf ;inject B-1 1s from the left + djnz MASK1 + ld (AND1+1),a ;e.g. %11100000 + ld (AND5+1),a + cpl + ld (AND2+1),a ;e.g. %00011111 + ld (AND7+1),a + + ld a,d ;e.g. x2 = 5, mask2 = %11111100, CPL = %00000011 + and 7 + inc a + ld b,a ;B = 1 + (x2 MOD 8) = 1,2,...,8 for x2 = 0,1,...,7 + 8*n + xor a +MASK2: + scf ;inject B 1s from the left + rra + djnz MASK2 + ld (AND4+1),a ;e.g. %11111100 + ld (AND8+1),a + cpl + ld (AND3+1),a ;e.g. %00000011 + ld (AND6+1),a + +; Compute Ncols and Display File address, and choose branch + ld a,c ;x1 + and %11111000 + rrca + rrca + rrca + ld b,a ;col1 + ld a,d ;x2 + and %11111000 + rrca + rrca + rrca ;col2 + sub b ;A = Ncols - 1 = col2 - col1 + ex af,af' ;save Ncols-1 and ZeroFlag + + ld b, h ; BC = y1x1 + ld a, 191 + call 22ACh ; 2 bytes after the 'PIXEL ADDRESS' subroutine, because +; https://skoolkid.github.io/rom/asm/22AA.html starts with LD A,175 + res 6, h ; Starts from 0 + ld bc, (SCREEN_ADDR) + add hl, bc ; Now current offset + + ex af,af' ;load Ncols-1 and ZeroFlag + jr z,NEQ1 + +; N > 1 (there are 2 or more columns) + dec a ;A = Ncols - 2 + ld ixH,a ;save Ncols-2 + ld b,0 +LOOP1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE ;HL at empty line + call nz,SP.PixelUp ;HL at line above + inc ixL ;restore iterative variable for a second check + push hl +; Scroll column 1 + ld a,(hl) +AND2: + and %00011111 ;e.g. x1 = 3 + ld c,a ;get in-window part of byte + ld a,(de) +AND1: + and %11100000 ;e.g. x1 = 3 + or c ;put in-window part of byte + ld (de),a + inc hl + inc de +; Scroll columns 2,3,...,N-1 + ld a,ixH ;load Ncols-2 + and a + jr z,COLUMNN + ld c,a + ldir +; Scroll column N +COLUMNN: + ld a,(hl) +AND4: + and %11111100 ;e.g. x2 = 5 + ld c,a ;get in-window part of byte + ld a,(de) +AND3: + and %00000011 ;e.g. x2 = 5 + or c ;put in-window part of byte + ld (de),a +; Scroll another line + pop hl + dec ixL + jp nz,LOOP1 + pop ix + ret + +; N = 1 (there is only one column) +NEQ1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE ;HL at empty line + call nz,SP.PixelUp ;HL at line below + inc ixL ;restore iterative variable for a second check + + ld a,(hl) +AND7: + and %00011111 ;e.g. x1 = 3 +AND8: + and %11111100 ;e.g. x2 = 5 + ld c,a ;get in-window part of byte + ld a,(de) +AND5: + and %11100000 ;e.g. x1 = 3 + ld b,a + ld a,(de) +AND6: + and %00000011 ;e.g. x2 = 5 + or b + or c ;put in-window part of byte + ld (de),a +; Scroll another line + dec ixL + jp nz,NEQ1 + pop ix + ret + + defs 32,0 ;empty line with 32 zero-bytes +EMPTYLINE: + ld hl,EMPTYLINE-32 + ENDP + + pop namespace + end asm +end sub + + +' ---------------------------------------------------------------- +' sub ScrollRightAligned +' pixel by pixel right scroll. +' scrolls 1 pixel right the window defined by (x1, y1, x2, y2) +' This scroll is aligned to columns. +' x1 and x2 are divided by 8 and rounded down (floor) to get the column number (0..31) +' ---------------------------------------------------------------- +sub fastcall ScrollRightAligned(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) + asm + push namespace core + PROC LOCAL LOOP1 LOCAL LOOP2 @@ -84,11 +788,13 @@ end sub ' ---------------------------------------------------------------- -' sub ScrolLeft +' sub ScrolLeftAligned ' pixel by pixel left scroll ' scrolls 1 pixel left the window defined by (x1, y1, x2, y2) +' This scroll is aligned to columns. +' x1 and x2 are divided by 8 and rounded down (floor) to get the column number (0..31) ' ---------------------------------------------------------------- -sub fastcall ScrollLeft(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) +sub fastcall ScrollLeftAligned(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) asm push namespace core @@ -152,11 +858,13 @@ end sub ' ---------------------------------------------------------------- -' sub ScrolUp +' sub ScrolUpAligned ' pixel by pixel up scroll ' scrolls 1 pixel up the window defined by (x1, y1, x2, y2) +' This scroll is aligned to columns. +' x1 and x2 are divided by 8 and rounded down (floor) to get the column number (0..31) ' ---------------------------------------------------------------- -sub fastcall ScrollUp(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) +sub fastcall ScrollUpAligned(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) asm push namespace core @@ -238,11 +946,13 @@ end sub ' ---------------------------------------------------------------- -' sub ScrolDown +' sub ScrolDownAligned ' pixel by pixel down scroll ' scrolls 1 pixel down the window defined by (x1, y1, x2, y2) +' This scroll is aligned to columns. +' x1 and x2 are divided by 8 and rounded down (floor) to get the column number (0..31) ' ---------------------------------------------------------------- -sub fastcall ScrollDown(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) +sub fastcall ScrollDownAligned(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) asm push namespace core @@ -322,10 +1032,11 @@ LOOP_START: end asm end sub + #pragma pop(case_insensitive) -REM the following is required, because it defines screen start addr -#require "cls.asm" +REM the following is required, because it defines SCREEN_ADDR and SCREEN_ATTR_ADDR +#require "sysvars.asm" #require "SP/PixelDown.asm" #require "SP/PixelUp.asm" diff --git a/src/lib/arch/zx48k/stdlib/winscroll.bas b/src/lib/arch/zx48k/stdlib/winscroll.bas index 45e5a9a4e..381f02250 100644 --- a/src/lib/arch/zx48k/stdlib/winscroll.bas +++ b/src/lib/arch/zx48k/stdlib/winscroll.bas @@ -1,8 +1,9 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License ' -' Copyleft (k) 2008 +' Copyleft (k) 2008, 2025 ' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' and Conrado Badenas ' ' Use this file as a template to develop your own library file ' ---------------------------------------------------------------- @@ -19,75 +20,39 @@ REM Avoid recursive / multiple inclusion ' --------------------------------------------------------------------- ' sub WinScrollRight ' scrolls the window defined by (row, col, width, height) 1 cell right +' it works with width=1 or height=1 but it is not impressive +' nothing is done if width=0 or height=0 ' --------------------------------------------------------------------- sub fastcall WinScrollRight(row as uByte, col as uByte, width as Ubyte, height as Ubyte) asm push namespace core - LOCAL BucleChars - LOCAL BucleScans - LOCAL BucleAttrs - PROC - ld b, a - pop hl - pop de - ld c, d + LOCAL BucleRows, BucleScans, AfterLDDR, AfterLDDR2, AfterLDDR3 + +; Read parameters, return if they are bad + ld b, a ;row + pop hl ;return address pop de + ld a, d ;colLeft + pop de ;width + add a, d + dec a + ld c, a ;colRight = colLeft + width - 1 ex (sp), hl - ld e, h + ld e, h ;height ld a, e or a - ret z - or d - ret z - push bc - ld a,b - and 18h - ld h,a - ld a,b - and 07h - add a,a - add a,a - add a,a - add a,a - add a,a - add a,c - add a,d - dec a - ld l,a ;HL=top-left window address in bitmap coord - ld bc, (SCREEN_ADDR) - add hl, bc - ld b,e - -BucleChars: - push bc - ld b,8 - -BucleScans: - push bc - push de - push hl - ld c,d - dec c - ld b,0 - ld d,h - ld e,l - dec l - lddr - ; clean up latest - xor a - ld (de),a - pop hl - pop de - inc h - pop bc - djnz BucleScans - dec h - call SP.PixelDown - pop bc - djnz BucleChars - - pop bc + ret z ;height=0 + ld a, d + or a + ret z ;width=0 + +; Compute Attributes address + sub 2 ;CF=1 if width=1, CF=0 if width>1 + inc a + ex af,af' ;A' = width-1, CF'=1 if width=1, CF'=0 if width>1 + push ix + ld ixL,e ;IXl = height, iterative variable ld l,b ld h,0 add hl,hl @@ -97,33 +62,77 @@ BucleScans: add hl,hl ld a,l add a,c - add a,d - dec a - ld l,a - ld a,h - ld h,a ;HL=top-left window address in attr coord - ld bc, (SCREEN_ATTR_ADDR) - add hl, bc - ld b,e - -BucleAttrs: - push bc - push de - push hl - ld c,d - dec c - ld b,0 - ld d,h - ld e,l - dec l - lddr - pop hl - ld de,32 + ld l,a ;HL = row*32 + colRight + ld de,(SCREEN_ATTR_ADDR) add hl,de - pop de - pop bc - djnz BucleAttrs +; For each row, compute Display File address +BucleRows: + push bc ;coords = row,col + push hl ;attr address + ld a,b ;row + and %00011000 ;select third (0,1,2) + ld h,a + ld a,b + and %00000111 ;select Row In Third (0,...,7) + rrca + rrca + rrca ;A = %RIT00000 + add a,c + ld l,a ;HL = %000th000RITcolmn + ld de,(SCREEN_ADDR) + add hl,de + ld b,0 + ex af,af' ;A = width-1, CF=1 if width=1, CF=0 if width>1 + ld ixH,7 + +; For each row, transfer 8 scans +BucleScans: + push hl + jr c,AfterLDDR + ld d,h + ld e,l ;DE = to address + dec hl ;HL = from address + ld c,a + lddr ;1st-7th scan in row + ex de,hl +AfterLDDR: + ld (hl),b ;clean up leftmost + pop hl + inc h ;scan below + dec ixH + jp nz,BucleScans + + jr c,AfterLDDR2 + ld d,h + ld e,l ;DE = to address + dec hl ;HL = from address + ld c,a + lddr ;8th scan in row + ex de,hl +AfterLDDR2: + ld (hl),b ;clean up leftmost + +; For each row, transfer a line of attributes + pop hl ;attr address + jr c,AfterLDDR3 + ld d,h + ld e,l ;DE = to address + dec hl ;HL = from address + ld c,a + push de + lddr ;attrs in row + pop hl +AfterLDDR3: + ex af,af' ;A' = width-1, CF'=1 if width=1, CF'=0 if width>1 + ld de,32 + add hl,de ;row below + pop bc + inc b ;row below + dec ixL + jp nz,BucleRows + + pop ix ENDP pop namespace end asm @@ -133,74 +142,36 @@ end sub ' --------------------------------------------------------------------- ' sub WinScrollLeft ' scrolls the window defined by (row, col, width, height) 1 cell left +' it works with width=1 or height=1 but it is not impressive +' nothing is done if width=0 or height=0 ' --------------------------------------------------------------------- sub fastcall WinScrollLeft(row as uByte, col as uByte, width as Ubyte, height as Ubyte) asm push namespace core PROC - LOCAL BucleChars - LOCAL BucleScans - LOCAL BucleAttrs + LOCAL BucleRows, BucleScans, AfterLDIR, AfterLDIR2, AfterLDIR3 - ld b, a - pop hl - pop de - ld c, d +; Read parameters, return if they are bad + ld b, a ;row + pop hl ;return address pop de + ld c, d ;col + pop de ;width ex (sp), hl - ld e, h + ld e, h ;height ld a, e or a - ret z - or d - ret z - push bc - ld a,b - and 18h - ld h,a - ld a,b - and 07h - add a,a - add a,a - add a,a - add a,a - add a,a - add a,c - ld l,a ;HL=top-left window address in bitmap coord - ld bc, (SCREEN_ADDR) - add hl, bc - ld b,e - -BucleChars: - push bc - ld b,8 - -BucleScans: - push bc - push de - push hl - ld c,d - dec c - ld b,0 - ld d,h - ld e,l - inc e - ex de,hl - ldir - ; clean up latest - xor a - ld (de),a - pop hl - pop de - inc h - pop bc - djnz BucleScans - dec h - call SP.PixelDown - pop bc - djnz BucleChars - - pop bc + ret z ;height=0 + ld a, d + or a + ret z ;width=0 + +; Compute Attributes address + sub 2 ;CF=1 if width=1, CF=0 if width>1 + inc a + ex af,af' ;A' = width-1, CF'=1 if width=1, CF'=0 if width>1 + push ix + ld ixL,e ;IXl = height, iterative variable ld l,b ld h,0 add hl,hl @@ -210,31 +181,77 @@ BucleScans: add hl,hl ld a,l add a,c - ld l,a - ld a,h - ld h,a ;HL=top-left address in attr coords - ld bc, (SCREEN_ATTR_ADDR) - add hl, bc - ld b,e - -BucleAttrs: - push bc - push de - push hl - ld c,d - dec c - ld b,0 - ld d,h - ld e,l - inc e - ex de,hl - ldir - pop hl - ld de,32 + ld l,a ;HL = row*32 + col + ld de,(SCREEN_ATTR_ADDR) add hl,de - pop de - pop bc - djnz BucleAttrs + +; For each row, compute Display File address +BucleRows: + push bc ;coords = row,col + push hl ;attr address + ld a,b ;row + and %00011000 ;select third (0,1,2) + ld h,a + ld a,b + and %00000111 ;select Row In Third (0,...,7) + rrca + rrca + rrca ;A = %RIT00000 + add a,c + ld l,a ;HL = %000th000RITcolmn + ld de,(SCREEN_ADDR) + add hl,de + ld b,0 + ex af,af' ;A = width-1, CF=1 if width=1, CF=0 if width>1 + ld ixH,7 + +; For each row, transfer 8 scans +BucleScans: + push hl + jr c,AfterLDIR + ld d,h + ld e,l ;DE = to address + inc hl ;HL = from address + ld c,a + ldir ;1st-7th scan in row + ex de,hl +AfterLDIR: + ld (hl),b ;clean up rightmost + pop hl + inc h ;scan below + dec ixH + jp nz,BucleScans + + jr c,AfterLDIR2 + ld d,h + ld e,l ;DE = to address + inc hl ;HL = from address + ld c,a + ldir ;8th scan in row + ex de,hl +AfterLDIR2: + ld (hl),b ;clean up rightmost + +; For each row, transfer a line of attributes + pop hl ;attr address + jr c,AfterLDIR3 + ld d,h + ld e,l ;DE = to address + inc hl ;HL = from address + ld c,a + push de + ldir ;attrs in row + pop hl +AfterLDIR3: + ex af,af' ;A' = width-1, CF'=1 if width=1, CF'=0 if width>1 + ld de,32 + add hl,de ;row below + pop bc + inc b ;row below + dec ixL + jp nz,BucleRows + + pop ix ENDP pop namespace end asm @@ -244,107 +261,35 @@ end sub ' --------------------------------------------------------------------- ' sub WinScrollUp ' scrolls the window defined by (row, col, width, height) 1 cell up +' it works with width=1 or height=1 but it is not impressive +' nothing is done if width=0 or height=0 ' --------------------------------------------------------------------- sub fastcall WinScrollUp(row as uByte, col as uByte, width as Ubyte, height as Ubyte) asm push namespace core PROC - LOCAL BucleScans, BucleAttrs, ScrollAttrs - LOCAL CleanLast, CleanLastLoop, EndCleanScan + LOCAL BucleRows, BucleScans, AttrAddress + LOCAL CleanBottomRow, CleanBottomScans, AfterLDIR - ld b, a - pop hl - pop de - ld c, d +; Read parameters, return if they are bad + ld b, a ;row + pop hl ;return address pop de + ld c, d ;col + pop de ;width ex (sp), hl - ld e, h + ld e, h ;height ld a, e or a - ret z - or d - ret z - - push bc - push de - - ld a,b - and 18h - ld h,a - ld a,b - and 07h - add a,a - add a,a - add a,a - add a,a - add a,a - add a,c - ld l,a ;HL=top-left window address in bitmap coord - ld bc, (SCREEN_ADDR) - add hl, bc - ld a,e - ld c, d ; c = width - ld d, h - ld e, l - dec a - jr z, CleanLast - add a,a - add a,a - add a,a - ld b, a ; b = 8 * (height - 1) - - inc h - inc h - inc h - inc h - inc h - inc h - inc h - call SP.PixelDown - -BucleScans: - push bc - push de - push hl - ld b, 0 - ldir - pop hl - pop de - pop bc - call SP.PixelDown - ex de, hl - call SP.PixelDown - ex de, hl - djnz BucleScans - -CleanLast: - ex de,hl - pop de - ld b, 8 - ld c, d - push de - -CleanLastLoop: - push bc - push hl - ld (hl), 0 - dec c - jr z, EndCleanScan - ld d, h - ld e, l - inc de - ld b, 0 - ldir - -EndCleanScan: - pop hl - pop bc - inc h - djnz CleanLastLoop + ret z ;height=0 + ld a, d + or a + ret z ;width=0 -ScrollAttrs: - pop de - pop bc +; Compute TopRow Attributes address + ex af,af' ;A' = width + push ix + ld ixL,e ;IXl = height, iterative variable ld l,b ld h,0 add hl,hl @@ -354,147 +299,145 @@ ScrollAttrs: add hl,hl ld a,l add a,c - ld l,a - ld a,h - ld h,a ;HL=top-left address in attr coords - ld bc, (SCREEN_ATTR_ADDR) - add hl, bc - ld b,e - dec b - ret z - -BucleAttrs: - push bc - push de - push hl - ld b,0 - ld c,d - ex de,hl - ld hl,32 + ld l,a ;HL = row*32 + col + ld de,(SCREEN_ATTR_ADDR) add hl,de - ldir - pop hl - ld de,32 + ld (AttrAddress+1),hl + +; Compute TopRow Display File address + ld a,b ;row + and %00011000 ;select third (0,1,2) + ld h,a + ld a,b + and %00000111 ;select Row In Third (0,...,7) + rrca + rrca + rrca ;A = %RIT00000 + add a,c + ld l,a ;HL = %000th000RITcolmn + ld de,(SCREEN_ADDR) add hl,de - pop de - pop bc - djnz BucleAttrs + push hl ;HL = from address + +BucleRows: + dec ixL + jr z,CleanBottomRow +; For each row, compute Display File address + inc b ;row below + ld a,b ;row + and %00011000 ;select third (0,1,2) + ld h,a + ld a,b + and %00000111 ;select Row In Third (0,...,7) + rrca + rrca + rrca ;A = %RIT00000 + add a,c + ld l,a ;HL = %000th000RITcolmn + ld de,(SCREEN_ADDR) + add hl,de + +; For each row, transfer 8 scans + pop de ;DE = to address, obtained from last "push hl; from address" + push hl ;HL = from address, will be the next "to address" + push bc ;coords = row,col + ld b,0 + ex af,af' ;A = width + ld ixH,7 +BucleScans: + ld c,a + push de + push hl + ldir ;1st-7th scan in row + pop hl + pop de + inc h ;scan below + inc d ;scan below + dec ixH + jp nz,BucleScans + + ld c,a + ldir ;8th scan in row + +; For each row, transfer a line of attributes +AttrAddress: + ld hl,AttrAddress + ld d,h + ld e,l ;DE = to address + ld c,32 + add hl,bc ;HL = from address + ld (AttrAddress+1),hl + ld c,a + ldir ;attrs in row + ex af,af' ;A' = width + pop bc + jp BucleRows + +; Clean bottom row (Display File, not Attributes) +CleanBottomRow: + ld b,0 + ex af,af' ;A = width + ld ixH,8 ;no need to speed up code by processing scans 1-7 apart of scan 8 + pop hl +CleanBottomScans: + ld (hl),b + ld c,a + dec c + jr z,AfterLDIR + push hl + ld d,h + ld e,l + inc de + ldir + pop hl +AfterLDIR: + inc h + dec ixH + jp nz,CleanBottomScans + + pop ix ENDP pop namespace end asm - end sub ' --------------------------------------------------------------------- ' sub WinScrollDown ' scrolls the window defined by (row, col, width, height) 1 cell down +' it works with width=1 or height=1 but it is not impressive +' nothing is done if width=0 or height=0 ' --------------------------------------------------------------------- sub fastcall WinScrollDown(row as uByte, col as uByte, width as Ubyte, height as Ubyte) asm push namespace core PROC - LOCAL BucleScans, BucleAttrs, ScrollAttrs - LOCAL CleanLast, CleanLastLoop, EndCleanScan + LOCAL BucleRows, BucleScans, AttrAddress + LOCAL CleanTopRow, CleanTopScans, AfterLDIR - ld b, a - pop hl - pop de - ld c, d +; Read parameters, return if they are bad + ld b, a ;rowTop + pop hl ;return address pop de + ld c, d ;col + pop de ;width ex (sp), hl - ld e, h - ld a, e - or a - ret z - or d - ret z - + ld e, h ;height ld a, b add a, e dec a - ld b, a ; b = row + height - 1 = top most row - - push bc - push de - - ld a,b - and 18h - ld h,a - ld a,b - and 07h - add a,a - add a,a - add a,a - add a,a - add a,a - add a,c - ld l,a ;HL=bottom-left window address in bitmap coord - ld bc, (SCREEN_ADDR) - add hl, bc - ld a,e - ld c, d ; c = width - ld d, h - ld e, l - dec a - jr z, CleanLast - add a,a - add a,a - add a,a - ld b, a ; b = 8 * (height - 1) - - inc d - inc d - inc d - inc d - inc d - inc d - inc d - call SP.PixelUp - -BucleScans: - push bc - push de - push hl - ld b, 0 - ldir - pop hl - pop de - pop bc - call SP.PixelUp - ex de, hl - call SP.PixelUp - ex de, hl - djnz BucleScans - -CleanLast: - ex de,hl - pop de - ld b, 8 - ld c, d - push de - -CleanLastLoop: - push bc - push hl - ld (hl), 0 - dec c - jr z, EndCleanScan - ld d, h - ld e, l - inc de - ld b, 0 - ldir - -EndCleanScan: - pop hl - pop bc - dec h - djnz CleanLastLoop + ld b, a ;rowBottom = rowTop + height - 1 + ld a, e + or a + ret z ;height=0 + ld a, d + or a + ret z ;width=0 -ScrollAttrs: - pop de - pop bc +; Compute BottomRow Attributes address + ex af,af' ;A' = width + push ix + ld ixL,e ;IXl = height, iterative variable ld l,b ld h,0 add hl,hl @@ -504,43 +447,111 @@ ScrollAttrs: add hl,hl ld a,l add a,c - ld l,a - ld a,h - ld h,a ;HL=top-left address in attr coords - ld bc, (SCREEN_ATTR_ADDR) - add hl, bc - ld b,e - dec b - ret z - -BucleAttrs: - push bc - push de - push hl - ld b,0 - ld c,d - ex de,hl - ld hl,-32 + ld l,a ;HL = row*32 + col + ld de,(SCREEN_ATTR_ADDR) add hl,de - ldir - pop hl - ld de,-32 + ld (AttrAddress+1),hl + +; Compute BottomRow Display File address + ld a,b ;row + and %00011000 ;select third (0,1,2) + ld h,a + ld a,b + and %00000111 ;select Row In Third (0,...,7) + rrca + rrca + rrca ;A = %RIT00000 + add a,c + ld l,a ;HL = %000th000RITcolmn + ld de,(SCREEN_ADDR) add hl,de - pop de - pop bc - djnz BucleAttrs + push hl ;HL = from address + +BucleRows: + dec ixL + jr z,CleanTopRow +; For each row, compute Display File address + dec b ;row above + ld a,b ;row + and %00011000 ;select third (0,1,2) + ld h,a + ld a,b + and %00000111 ;select Row In Third (0,...,7) + rrca + rrca + rrca ;A = %RIT00000 + add a,c + ld l,a ;HL = %000th000RITcolmn + ld de,(SCREEN_ADDR) + add hl,de + +; For each row, transfer 8 scans + pop de ;DE = to address, obtained from last "push hl; from address" + push hl ;HL = from address, will be the next "to address" + push bc ;coords = row,col + ld b,0 + ex af,af' ;A = width + ld ixH,7 +BucleScans: + ld c,a + push de + push hl + ldir ;1st-7th scan in row + pop hl + pop de + inc h ;scan below + inc d ;scan below + dec ixH + jp nz,BucleScans + + ld c,a + ldir ;8th scan in row + +; For each row, transfer a line of attributes +AttrAddress: + ld hl,AttrAddress + ld d,h + ld e,l ;DE = to address + ld c,32 ;it is a "mORAcle" that CarryFlag is always 0 here + sbc hl,bc ;HL = from address + ld (AttrAddress+1),hl + ld c,a + ldir ;attrs in row + ex af,af' ;A' = width + pop bc + jp BucleRows + +; Clean top row (Display File, not Attributes) +CleanTopRow: + ld b,0 + ex af,af' ;A = width + ld ixH,8 ;no need to speed up code by processing scans 1-7 apart of scan 8 + pop hl +CleanTopScans: + ld (hl),b + ld c,a + dec c + jr z,AfterLDIR + push hl + ld d,h + ld e,l + inc de + ldir + pop hl +AfterLDIR: + inc h + dec ixH + jp nz,CleanTopScans + + pop ix ENDP pop namespace end asm - end sub #pragma pop(case_insensitive) -REM the following is required, because it defines screen start addr -#require "cls.asm" -#require "SP/PixelDown.asm" -#require "SP/PixelUp.asm" +REM the following is required, because it defines SCREEN_ADDR and SCREEN_ATTR_ADDR #require "sysvars.asm" diff --git a/src/lib/arch/zx81sd/runtime/arch_config.asm b/src/lib/arch/zx81sd/runtime/arch_config.asm new file mode 100644 index 000000000..eac1425cf --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/arch_config.asm @@ -0,0 +1,29 @@ +; arch_config.asm — Constantes de arquitectura ZX81 + SD81 Booster +; +; Sobreescribe zx48k/runtime/arch_config.asm para redirigir las +; llamadas ROM del runtime hacia implementaciones propias en RAM. + +; --------------------------------------------------------------------------- +; Usar scroll por buffer propio (sin ROM Spectrum) +; --------------------------------------------------------------------------- +#define __ZXB_ENABLE_BUFFER_SCROLL + +; --------------------------------------------------------------------------- +; Sin soporte de coma flotante en esta versión +; (el FP calculator de la ROM Spectrum no está disponible) +; --------------------------------------------------------------------------- +#define __ZXB_NO_FLOAT + +; --------------------------------------------------------------------------- +; Gráficos — implementaciones propias +; --------------------------------------------------------------------------- +; Incluir aquí los ficheros con las rutinas que sustituyen a las ROM calls. +; El resto del runtime (plot.asm, draw.asm...) incluye arch_config.asm, +; por lo que estas definiciones quedan disponibles automáticamente. + +#include once +#include once + +; Nota: SCROLL_SCR, KEY_SCAN, KEY_TEST, KEY_CODE, LD_BYTES, ROM_SAVE, etc. +; deberán implementarse cuando se necesiten. Los dejamos sin definir +; para que el enlazador falle con un error claro si se usan accidentalmente. diff --git a/src/lib/arch/zx81sd/runtime/arith/divf.asm b/src/lib/arch/zx81sd/runtime/arith/divf.asm new file mode 100644 index 000000000..42e092006 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/arith/divf.asm @@ -0,0 +1,74 @@ +#include once +#include once +#include once + +; ------------------------------------------------------------- +; Floating point library using the FP ROM Calculator (ZX 48K) + +; All of them uses C EDHL registers as 1st paramter. +; For binary operators, the 2n operator must be pushed into the +; stack, in the order BC DE HL (B not used). +; +; Uses CALLEE convention +; ------------------------------------------------------------- +; +; zx81sd override: the only change from zx48k's version is where +; TMP/ERR_SP live. The original uses the Spectrum ROM's DEST (23629) +; and ERR_SP (23613) system variables to save/restore a stack recovery +; point around the division (a "longjmp" trick for divide-by-zero) -- +; on zx81sd that mechanism never actually gets used for real recovery +; (__ERROR does DI+HALT directly, it doesn't restore SP from ERR_SP), +; but this code writes there on every division regardless of whether +; an error happens, and those addresses fall inside the program's own +; compiled code (block 2) instead of real sysvars. DIVF_SCRATCH +; (sysvars.asm) is dedicated scratch space instead -- same mechanism, +; just relocated. + + push namespace core + +__DIVF: ; Division + PROC + LOCAL __DIVBYZERO + LOCAL TMP, ERR_SP + +TMP EQU DIVF_SCRATCH ; zx81sd: dedicated scratch, not DEST +ERR_SP EQU DIVF_SCRATCH + 2 ; zx81sd: dedicated scratch, not ERR_SP + + call __FPSTACK_PUSH2 + + ld hl, (ERR_SP) + ld (TMP), hl + ld hl, __DIVBYZERO + push hl + ld (ERR_SP), sp + + ; ------------- ROM DIV + rst 28h + defb 01h ; EXCHANGE + defb 05h ; DIV + defb 38h; ; END CALC + + pop hl + ld hl, (TMP) + ld (ERR_SP), hl + + jp __FPSTACK_POP + +__DIVBYZERO: + ld hl, (TMP) + ld (ERR_SP), hl + + ld a, ERROR_NumberTooBig + ld (ERR_NR), a + + ; Returns 0 on DIV BY ZERO error + xor a + ld b, a + ld c, a + ld d, a + ld e, a + ret + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/array/array.asm b/src/lib/arch/zx81sd/runtime/array/array.asm new file mode 100644 index 000000000..ec71e353e --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/array/array.asm @@ -0,0 +1,178 @@ +; vim: ts=4:et:sw=4: +; Copyleft (K) by Jose M. Rodriguez de la Rosa +; (a.k.a. Boriel) +; http://www.boriel.com +; ------------------------------------------------------------------- +; Simple array Index routine +; Number of total indexes dimensions - 1 at beginning of memory +; HL = Start of array memory (First two bytes contains N-1 dimensions) +; Dimension values on the stack, (top of the stack, highest dimension) +; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + +; For any array of N dimension A(aN-1, ..., a1, a0) +; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as +; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: +; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +; +; zx81sd override: the only change from zx48k's version is +; LBOUND_PTR's storage address. The original uses the Spectrum ROM's +; MEMBOT system variable (23698, safe scratch RAM on real hardware) -- +; on zx81sd that address falls inside the program's own compiled code +; (block 2, $4000-$5FFF) instead of a real sysvar, silently corrupting +; it on every multi-dimensional array access. ARRAY_SCRATCH (sysvars.asm) +; is a dedicated 8-byte scratch area in the zx81sd sysvar block instead. + +#include once +#include once + +#ifdef __CHECK_ARRAY_BOUNDARY__ +#include once +#endif + + push namespace core + +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ;; HL <-- [HL] + +__ARRAY: + PROC + + LOCAL LOOP + LOCAL ARRAY_END + LOCAL TMP_ARR_PTR ; Ptr to Array DATA region. Stored temporarily + LOCAL LBOUND_PTR, UBOUND_PTR ; LBound and UBound PTR indexes + LOCAL RET_ADDR ; Contains the return address popped from the stack + +LBOUND_PTR EQU ARRAY_SCRATCH ; zx81sd: dedicated scratch, not MEMBOT +UBOUND_PTR EQU LBOUND_PTR + 2 ; Next 2 bytes for UBOUND PTR +RET_ADDR EQU UBOUND_PTR + 2 ; Next 2 bytes for RET_ADDR +TMP_ARR_PTR EQU RET_ADDR + 2 ; Next 2 bytes for TMP_ARR_PTR + + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE <-- PTR to Dim sizes table + ld (TMP_ARR_PTR), hl ; HL = Array __DATA__.__PTR__ + inc hl + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __LBOUND__ PTR + ld (LBOUND_PTR), bc ; Store it for later + +#ifdef __CHECK_ARRAY_BOUNDARY__ + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __UBOUND__ PTR + ld (UBOUND_PTR), bc +#endif + + ex de, hl ; HL <-- PTR to Dim sizes table, DE <-- dummy + ex (sp), hl ; Return address in HL, PTR Dim sizes table onto Stack + ld (RET_ADDR), hl ; Stores it for later + + exx + pop hl ; Will use H'L' as the pointer to Dim sizes table + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + + ld hl, 0 ; HL = Element Offset "accumulator" + +LOOP: + ex de, hl ; DE = Element Offset + ld hl, (LBOUND_PTR) + ld a, h + or l + ld b, h + ld c, l + jr z, 1f + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (LBOUND_PTR), hl +1: + pop hl ; Get next index (Ai) from the stack + sbc hl, bc ; Subtract LBOUND + +#ifdef __CHECK_ARRAY_BOUNDARY__ + ld a, ERROR_SubscriptWrong + jp c, __ERROR + push hl ; Saves (Ai) - Lbound(i) + add hl, bc ; Recover original (Ai) value + push hl + ld hl, (UBOUND_PTR) + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (UBOUND_PTR), hl + pop hl ; original (Ai) value + scf + sbc hl, bc ; HL <- HL - BC - 1 = Ai - UBound(i) - 1 => No Carry if Ai > UBound(i) + jp nc, __ERROR + pop hl ; Recovers (Ai) - Lbound(Ai) +#endif + + add hl, de ; Adds current index + + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + dec bc ; Decrements loop counter + + ld e, (hl) ; Loads next dimension size into D'E' + inc hl + ld d, (hl) + inc hl + push de + exx + + pop de ; DE = Max bound Number (i-th dimension) + + call __FMUL16 ; HL <= HL * DE mod 65536 + jp LOOP + +ARRAY_END: + ld a, (hl) + exx + +#ifdef __BIG_ARRAY__ + ld d, 0 + ld e, a + call __FMUL16 +#else + LOCAL ARRAY_SIZE_LOOP + + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP + +#endif + + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start + ld de, (RET_ADDR) + push de + ret + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/beep.asm b/src/lib/arch/zx81sd/runtime/beep.asm new file mode 100644 index 000000000..ac6c61f3a --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/beep.asm @@ -0,0 +1,41 @@ +; BEEP — Pulso corto de beeper (clic de tecla) +; +; SD81 Booster en modo Superfast HiRes Spectrum emula el puerto ULA del +; Spectrum en $FBh: bits 2-0 = color de borde, bits 4-3 = beeper (ver +; border.asm y Apendice "Modo Spectrum" del manual del SD81 Booster). +; Ambas funciones comparten el mismo puerto de solo escritura, asi que +; se mantiene una copia sombra del ultimo byte escrito para poder +; pulsar el beeper sin alterar el color de borde actual (y viceversa). + + push namespace core + +SD81_ULA_PORT EQU $FB ; Puerto ULA emulado en modo HiRes Spectrum +SD81_ULA_BEEP_BITS EQU $18 ; bits 4-3 + +__ZX81SD_ULA_SHADOW: + DEFB 0 + +; --------------------------------------------------------------------------- +; __ZX81SD_KEYCLICK — Pulso breve de beeper (clic de tecla) +; No recibe ni devuelve nada. Registros modificados: AF, BC. +; --------------------------------------------------------------------------- +__ZX81SD_KEYCLICK: + PROC + LOCAL CLICK_DELAY + + ld hl, __ZX81SD_ULA_SHADOW + ld a, (hl) + or SD81_ULA_BEEP_BITS + out (SD81_ULA_PORT), a + + ld b, 30 +CLICK_DELAY: + djnz CLICK_DELAY + + ld a, (hl) ; restaura el byte previo (beeper apagado, borde intacto) + out (SD81_ULA_PORT), a + ret + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/bootstrap.asm b/src/lib/arch/zx81sd/runtime/bootstrap.asm new file mode 100644 index 000000000..b98741573 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/bootstrap.asm @@ -0,0 +1,127 @@ +; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) +; +; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente +; por el prólogo del backend Python (emit_prologue), que emite esas +; instrucciones como constantes de arquitectura conocidas en tiempo de +; compilación. +; +; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. +; Se registra con #init para que el compilador inserte automáticamente +; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. + +#include once +#include once + +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): +; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta +; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en +; todo binario, aunque el programa concreto no acabe generando código que +; la invoque. +#include once + +#init .core.SD81_INIT_SYSVARS + + push namespace core + +; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + + ret + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/border.asm b/src/lib/arch/zx81sd/runtime/border.asm new file mode 100644 index 000000000..cb7f3ce6e --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/border.asm @@ -0,0 +1,26 @@ +; BORDER — Cambia el color del borde +; SD81 Booster en modo Superfast HiRes Spectrum emula el puerto ULA del +; Spectrum en $FBh: bits 2-0 = color de borde, bits 4-3 = beeper. +; El beeper (ver beep.asm) usa el mismo puerto, asi que se preservan +; los bits 4-3 actuales (guardados en __ZX81SD_ULA_SHADOW) en vez de +; sobrescribir el byte completo. +; +; Entrada: A = color de borde (bits 2-0, igual que en Spectrum) +; Sustituye a: BORDER EQU $229Bh (ROM Spectrum) + +#include once + + push namespace core + +BORDER: + and $07 + ld b, a + ld hl, __ZX81SD_ULA_SHADOW + ld a, (hl) + and $18 ; conserva los bits de beeper + or b + ld (hl), a + out (SD81_ULA_PORT), a + ret + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/break.asm b/src/lib/arch/zx81sd/runtime/break.asm new file mode 100644 index 000000000..73f0addc9 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/break.asm @@ -0,0 +1,62 @@ +; CHECK_BREAK — Detección de la tecla BREAK (SHIFT + SPACE) +; ZX81 + SD81 Booster +; +; Los puertos del teclado del ZX81 son idénticos a los del Spectrum +; (puerto $FEh con líneas de dirección en el bus A8-A15). +; TS_BRK ($8020h de la ROM Spectrum) lee las half-rows de CAPS SHIFT y SPACE; +; aquí se reimplementa directamente sin depender de la ROM. +; +; Puerto $FEh: +; A8 (línea 0, $FEFE) = SHIFT/Z/X/C/V +; A12 (línea 7, $7FFE) = SPACE/./M/N/B ← SPACE está aquí, bit 0 +; +; BREAK = CAPS SHIFT (bit 0 de $FEFE) + SPACE (bit 0 de $7FFE) + +#include once +#include once + + push namespace core + +CHECK_BREAK: + PROC + LOCAL TS_BRK, NO_BREAK + + push af + call TS_BRK + jr c, NO_BREAK + + ld a, ERROR_BreakIntoProgram + jp __ERROR + +NO_BREAK: + pop af + pop hl ; ret address + ex (sp), hl ; restaura HL original + ret + +; TS_BRK — Comprueba si BREAK está pulsado +; Salida: carry set = no pulsado; carry clear = BREAK pulsado +; Destruye: A +TS_BRK: + ld a, $FE + in a, ($FE) ; half-row SPACE/./M/N/B (A12 high = $7FFE... ) + ; Para leer la half-row de SPACE necesitamos A12=0, resto altos: + ; Dirección: $7FFE = 0111 1111 1111 1110b → A12=0, A8=1 + ld a, $7F + in a, ($FE) ; lee half-row 7 (SPACE en bit 0) + rra ; bit 0 → carry (0 = pulsado) + jr c, NO_SPACE + + ; SPACE pulsado — comprobar CAPS SHIFT + ld a, $FE ; A8=0 → half-row 0 (CAPS SHIFT en bit 0) + in a, ($FE) + rra ; bit 0 → carry (0 = pulsado) + ret ; carry clear = ambas pulsadas = BREAK + +NO_SPACE: + scf ; SPACE no pulsada → no es BREAK + ret + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/charset.asm b/src/lib/arch/zx81sd/runtime/charset.asm new file mode 100644 index 000000000..9cc665859 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/charset.asm @@ -0,0 +1,20 @@ +; CHARSET — Fuente de caracteres 8x8 compatible Spectrum +; +; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + + push namespace core + +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + +; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, +; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, +; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ +; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa +; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/chr.asm b/src/lib/arch/zx81sd/runtime/chr.asm new file mode 100644 index 000000000..a6942465e --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/chr.asm @@ -0,0 +1,86 @@ +; CHR$(x, y, x) returns the string CHR$(x) + CHR$(y) + CHR$(z) +; +; zx81sd override: the only change from zx48k's version is TMP's storage +; address. The original uses the Spectrum ROM's DEST system variable +; (23629, safe scratch RAM on real hardware) to stash the return address +; while it calls __MEM_ALLOC -- on zx81sd that address falls inside the +; program's own compiled code (block 2, landed inside __DIVU32 in one +; observed build) instead of a real sysvar. CHR_SCRATCH (sysvars.asm) is +; a dedicated 2-byte scratch slot instead. + +#include once +#include once + + push namespace core + +CHR: ; Returns HL = Pointer to STRING (NULL if no memory) + ; Requires alloc.asm for dynamic memory heap. + ; Parameters: HL = Number of bytes to insert (already push onto the stack) + ; STACK => parameters (16 bit, only the High byte is considered) + ; Used registers A, A', BC, DE, HL, H'L' + + PROC + + LOCAL __POPOUT + LOCAL TMP + +TMP EQU CHR_SCRATCH ; zx81sd: dedicated scratch, not DEST + + ld a, h + or l + ret z ; If Number of parameters is ZERO, return NULL STRING + + ld b, h + ld c, l + + pop hl ; Return address + ld (TMP), hl + + push bc + inc bc + inc bc ; BC = BC + 2 => (2 bytes for the length number) + call __MEM_ALLOC + pop bc + + ld d, h + ld e, l ; Saves HL in DE + + ld a, h + or l + jr z, __POPOUT ; No Memory, return + + ld (hl), c + inc hl + ld (hl), b + inc hl + +__POPOUT: ; Removes out of the stack every byte and return + ; If Zero Flag is set, don't store bytes in memory + ex af, af' ; Save Zero Flag + + ld a, b + or c + jr z, __CHR_END + + dec bc + pop af ; Next byte + + ex af, af' ; Recovers Zero flag + jr z, __POPOUT + + ex af, af' ; Saves Zero flag + ld (hl), a + inc hl + ex af, af' ; Recovers Zero Flag + + jp __POPOUT + +__CHR_END: + ld hl, (TMP) + push hl ; Restores return addr + ex de, hl ; Recovers original HL ptr + ret + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/draw.asm b/src/lib/arch/zx81sd/runtime/draw.asm new file mode 100644 index 000000000..77537ddbc --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/draw.asm @@ -0,0 +1,335 @@ +; DRAW using bresenhams algorithm and screen positioning — ZX81 + SD81 Booster +; Copyleft (k) 2010 by J. Rodriguez (a.k.a. Boriel) http://www.boriel.com +; Adaptado de zx48k/runtime/draw.asm para sustituir las llamadas ROM +; por implementaciones propias (sin ROM Spectrum disponible). +; +; Cambios respecto al original zx48k: +; - PIXEL_ADDR: llama a nuestra implementación en pixel_addr.asm +; - COORDS: usa el valor de sysvars.asm ($8000+), no $5C7D + +; Y parameter in A +; X parameter in high byte on top of the stack + +#include once +#include once + +#include once +#include once +#include once + +#include once +#include once +#include once +#include once + +;; DRAW PROCEDURE + push namespace core + + PROC + + LOCAL __DRAW1 + LOCAL __DRAW2 + LOCAL __DRAW3 + LOCAL __DRAW4, __DRAW4_LOOP + LOCAL __DRAW5 + LOCAL __DRAW6, __DRAW6_LOOP + LOCAL __DRAW_ERROR + LOCAL DX1, DX2, DY1, DY2 + LOCAL __INCX, __INCY, __DECX, __DECY + +__DRAW_ERROR: + jp __OUT_OF_SCREEN_ERR + +DRAW: + ;; ENTRY POINT + + LOCAL __DRAW_SETUP1, __DRAW_START, __PLOTOVER, __PLOTINVERSE + + ex de, hl ; DE = Y OFFSET + pop hl ; return addr + ex (sp), hl ; CALLEE => HL = X OFFSET + ld bc, (COORDS) + + ld a, c + add a, l + ld l, a + ld a, h + adc a, 0 ; HL = HL + C + ld h, a + jr nz, __DRAW_ERROR ; if a <> 0 => Out of Screen + + ld a, b + add a, e + ld e, a + ld a, d + adc a, 0 ; DE = DE + B + ld d, a + jr nz, __DRAW_ERROR ; if a <> 0 => Out of Screen + + ld a, 191 + sub e + jr c, __DRAW_ERROR ; Out of screen + + ld h, e ; now H,L = y2, x2 + +__DRAW: + ; __FASTCALL__ Entry. Plots from (COORDS) to coord H, L + push hl + ex de, hl ; D,E = y2, x2; + + ld a, (P_FLAG) + ld c, a + bit 2, a ; Test for INVERSE1 + jr z, __DRAW_SETUP1 + ld a, 2Fh ; CPL + ld (__PLOTINVERSE), a + ld a, 0A6h ; and (hl) + jp __DRAW_START + +__DRAW_SETUP1: + xor a ; nop + ld (__PLOTINVERSE), a + ld a, 0B6h ; or (hl) + bit 0, c ; Test for OVER + jr z, __DRAW_START + ld a, 0AEh ; xor (hl) + +__DRAW_START: + ld (__PLOTOVER), a ; "Pokes" last operation + exx + ld bc, (COORDS) ; B'C' = y1, x1 + ld d, b ; Saves B' in D' + ld a, 191 + call PIXEL_ADDR + res 6, h ; no-op en SD81 (H siempre $00-$17), mantiene compatibilidad + + ;; Now gets pixel mask in A register + ld b, a + inc b + xor a + scf + LOCAL __PIXEL_MASK +__PIXEL_MASK: + rra + djnz __PIXEL_MASK + + ld b, d ; Restores B' from D' + ld de, (SCREEN_ADDR) + add hl, de + pop de ; D'E' = y2, x2 + exx ; At this point: D'E' = y2,x2 coords + ; B'C' = y1, y1 coords + ; H'L' = Screen Address of pixel + + ex af, af' ; Saves A reg for later + ; A' = Pixel mask + + ld bc, (COORDS) ; B,C = y1, x1 + + ld a, e + sub c ; dx = X2 - X1 + ld c, a ; Saves dx in c + + ld a, 0Ch ; INC C opcode + ld hl, __INCX ; xi = 1 + jr nc, __DRAW1 + + ld a, c + neg ; dx = X1 - X2 + ld c, a + ld a, 0Dh ; DEC C opcode + ld hl, __DECX ; xi = -1 + +__DRAW1: + ld (DX1), a + ld (DX1 + 2), hl ; Updates DX1 call address + ld (DX2), a + ld (DX2 + 2), hl ; Updates DX2 call address + + ld a, d + sub b ; dy = Y2 - Y1 + ld b, a ; Saves dy in b + + ld a, 4 ; INC B opcode + ld hl, __INCY ; y1 = 1 + jr nc, __DRAW2 + + ld a, b + neg + ld b, a ; dy = Y2 - Y1 + ld a, 5 ; DEC B opcode + ld hl, __DECY ; y1 = -1 + +__DRAW2: + ld (DY1), a + ld (DY1 + 2), hl ; Updates DX1 call address + ld (DY2), a + ld (DY2 + 2), hl ; Updates DX2 call address + + ld a, b + sub c ; dy - dx + jr c, __DRAW_DX_GT_DY ; DX > DY + + ; At this point DY >= DX + ; -------------------------- + ; HL = error = dY / 2 + ld h, 0 + ld l, b + srl l + + ; DE = -dX + xor a + sub c + ld e, a + sbc a, a + ld d, a + + ; BC = DY + ld c, b + ld b, h + + exx + scf ; Sets Carry to signal update ATTR + ex af, af' ; Brings back pixel mask + ld e, a ; Saves it in free E register + jp __DRAW4_LOOP + +__DRAW3: ; While c != e => while y != y2 + exx + add hl, de ; error -= dX + bit 7, h ; + exx ; recover coordinates + jr z, __DRAW4 ; if error < 0 + + exx + add hl, bc ; error += dY + exx + + ld a, e +DX1: ; x += xi + inc c + call __INCX ; This address will be dynamically updated + ld e, a + +__DRAW4: + +DY1: ; y += yi + inc b + call __INCY ; This address will be dynamically updated + ld a, e ; Restores A reg. + call __FASTPLOT + +__DRAW4_LOOP: + ld a, b + cp d + jp nz, __DRAW3 + ld (COORDS), bc + ret + +__DRAW_DX_GT_DY: ; DX > DY + ; -------------------------- + ; HL = error = dX / 2 + ld h, 0 + ld l, c + srl l ; HL = error = DX / 2 + + ; DE = -dY + xor a + sub b + ld e, a + sbc a, a + ld d, a + + ; BC = dX + ld b, h + + exx + ld d, e + scf ; Sets Carry to signal update ATTR + ex af, af' ; Brings back pixel mask + ld e, a ; Saves it in free E register + jp __DRAW6_LOOP + +__DRAW5: ; While loop + exx + add hl, de ; error -= dY + bit 7, h ; if (error < 0) + exx ; Restore coords + jr z, __DRAW6 ; + exx + add hl, bc ; error += dX + exx + +DY2: ; y += yi + inc b + call __INCY ; This address will be dynamically updated + +__DRAW6: + ld a, e +DX2: ; x += xi + inc c + call __INCX ; This address will be dynamically updated + ld e, a + call __FASTPLOT + +__DRAW6_LOOP: + ld a, c ; Current X coord + cp d + jp nz, __DRAW5 + ld (COORDS), bc + ret + +__DRAW_END: + exx + ret + + ;; Given a A mask and an HL screen position + ;; return the next left position + ;; Also updates BC coords +__DECX EQU SP.PixelLeft + + ;; Like the above, but to the RIGHT + ;; Also updates BC coords +__INCX EQU SP.PixelRight + + ;; Given an HL screen position, calculates + ;; the above position + ;; Also updates BC coords +__INCY EQU SP.PixelUp + + ;; Given an HL screen position, calculates + ;; the above position + ;; Also updates BC coords +__DECY EQU SP.PixelDown + + ;; Puts the A register MASK in (HL) +__FASTPLOT: +__PLOTINVERSE: + nop ; Replace with CPL if INVERSE 1 +__PLOTOVER: + or (hl) ; Replace with XOR (hl) if OVER 1 AND INVERSE 0 + ; Replace with AND (hl) if INVERSE 1 + + ld (hl), a + ex af, af' ; Recovers flag. If Carry set => update ATTR + ld a, e ; Recovers A reg + ret nc + + push hl + push de + push bc + call SET_PIXEL_ADDR_ATTR + pop bc + pop de + pop hl + + LOCAL __FASTPLOTEND +__FASTPLOTEND: + or a ; Resets carry flag + ex af, af' ; Recovers A reg + ld a, e + ret + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/draw3.asm b/src/lib/arch/zx81sd/runtime/draw3.asm new file mode 100644 index 000000000..e70bf27ef --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/draw3.asm @@ -0,0 +1,465 @@ +; draw3.asm (zx81sd) — DRAW en modo arco (DRAW x, y, angulo) +; +; Sustituye a zx48k/runtime/draw3.asm, que llama directamente a direcciones +; fijas de la ROM del Spectrum (STACK-A/STACK-BC en $2D28/$2D2Bh, STK-TO-A en +; $2314h, STK-TO-BC en $2307h, CD-PRMS1 en $247Dh) para nada de eso hay ROM +; mapeada en zx81sd. Aqui se usan las mismas rutinas, pero ya portadas como +; codigo propio en fp_calc.asm (Fases 1, 4 y 5). El resto del algoritmo (que +; es codigo bytecode del calculador: rst 28h + literales) es identico al +; original, ya que rst 28h ya apunta a nuestro CALCULATE propio. + +#include once +#include once +#include once +#include once +#include once +#include once + + push namespace core + +DRAW3: + PROC + LOCAL L2477 + LOCAL L2420 + LOCAL L2439 + LOCAL L245F + LOCAL L23C1 + LOCAL SUM_C, SUM_B + LOCAL DR_SIN_NZ + + exx + ex af, af' ;; Preserves ARC + pop hl + pop de + ex (sp), hl ;; CALLEE + push de + call __FPSTACK_I16 ;; X Offset + pop hl + call __FPSTACK_I16 ;; Y Offset + exx + ex af, af' + call __FPSTACK_PUSH ;; R Arc + +; Now enter the calculator and store the complete rotation angle in mem-5 + + RST 28H ;; FP-CALC x, y, A. + DEFB $C5 ;;st-mem-5 x, y, A. + +; Test the angle for the special case of 360 degrees. + + DEFB $A2 ;;stk-half x, y, A, 1/2. + DEFB $04 ;;multiply x, y, A/2. + DEFB $1F ;;sin x, y, sin(A/2). + DEFB $31 ;;duplicate x, y, sin(A/2),sin(A/2) + DEFB $30 ;;not x, y, sin(A/2), (0/1). + DEFB $30 ;;not x, y, sin(A/2), (1/0). + DEFB $00 ;;jump-true x, y, sin(A/2). + + DEFB (DR_SIN_NZ - $) & 0FFh + ;;if sin(r/2) is not zero. + +; The third parameter is 2*PI (or a multiple of 2*PI) so a 360 degrees turn +; would just be a straight line. Eliminating this case here prevents +; division by zero at later stage. + + DEFB $02 ;;delete x, y. + DEFB $38 ;;end-calc x, y. + JP L2477 + +; --- + +; An arc can be drawn. + +DR_SIN_NZ: + DEFB $C0 ;;st-mem-0 x, y, sin(A/2). store mem-0 + DEFB $02 ;;delete x, y. + +; The next step calculates (roughly) the diameter of the circle of which the +; arc will form part. + + DEFB $C1 ;;st-mem-1 x, y. store mem-1 + DEFB $02 ;;delete x. + + DEFB $31 ;;duplicate x, x. + DEFB $2A ;;abs x, x (+ve). + DEFB $E1 ;;get-mem-1 x, X, y. + DEFB $01 ;;exchange x, y, X. + DEFB $E1 ;;get-mem-1 x, y, X, y. + DEFB $2A ;;abs x, y, X, Y (+ve). + DEFB $0F ;;addition x, y, X+Y. + DEFB $E0 ;;get-mem-0 x, y, X+Y, sin(A/2). + DEFB $05 ;;division x, y, X+Y/sin(A/2). + DEFB $2A ;;abs x, y, X+Y/sin(A/2) = D. + + DEFB $E0 ;;get-mem-0 x, y, D, sin(A/2). + DEFB $01 ;;exchange x, y, sin(A/2), D. + + DEFB $38 ;;end-calc x, y, sin(A/2), D. + +; The next test avoids drawing 4 straight lines when the start and end pixels +; are adjacent (or the same). + + LD A,(HL) ; fetch exponent byte of D. + CP $81 ; compare to 1 + JR NC,L23C1 ; forward, if > 1, to DR-PRMS + +; else delete the top two stack values and draw a simple straight line. + + RST 28H ;; FP-CALC + DEFB $02 ;;delete + DEFB $02 ;;delete + DEFB $38 ;;end-calc x, y. + + JP L2477 ; to LINE-DRAW + +; --- + +; The ARC will consist of multiple straight lines so call CD-PRMS1 to +; pre-calculate sine values from the angle (in mem-5) and determine also +; the number of straight lines from that value and the 'diameter' which +; is at the top of the calculator stack. + +#ifdef DRAW3_DEBUG +DRAW3_BP_PRMS: ; breakpoint: justo antes de llamar a CD-PRMS1 +#endif +L23C1: CALL L247D ; routine CD-PRMS1 + + ; mem-0 ; (A)/No. of lines (=a) (step angle) + ; mem-1 ; sin(a/2) + ; mem-2 ; - + ; mem-3 ; cos(a) const + ; mem-4 ; sin(a) const + ; mem-5 ; Angle of rotation (A) in + ; B ; Count of straight lines - max 252. + + PUSH BC ; Save the line count on the machine stack. + +; Remove the now redundant diameter value D. + + RST 28H ;; FP-CALC x, y, sin(A/2), D. + DEFB $02 ;;delete x, y, sin(A/2). + +; Dividing the sine of the step angle by the sine of the total angle gives +; the length of the initial chord on a unary circle. + + DEFB $E1 ;;get-mem-1 x, y, sin(A/2), sin(a/2) + DEFB $01 ;;exchange x, y, sin(a/2), sin(A/2) + DEFB $05 ;;division x, y, sin(a/2)/sin(A/2) + DEFB $C1 ;;st-mem-1 x, y. f. + DEFB $02 ;;delete x, y. + +; With the factor stored, scale the x coordinate first. + + DEFB $01 ;;exchange y, x. + DEFB $31 ;;duplicate y, x, x. + DEFB $E1 ;;get-mem-1 y, x, x, f. + DEFB $04 ;;multiply y, x, x*f (=xx) + DEFB $C2 ;;st-mem-2 y, x, xx. + DEFB $02 ;;delete y. x. + +; Now scale the y coordinate. + + DEFB $01 ;;exchange x, y. + DEFB $31 ;;duplicate x, y, y. + DEFB $E1 ;;get-mem-1 x, y, y, f + DEFB $04 ;;multiply x, y, y*f (=yy) + +; 'sin' and 'cos' trash locations mem-0 to mem-2 so fetch mem-2 to the +; calculator stack for safe keeping. + + DEFB $E2 ;;get-mem-2 x, y, yy, xx. + +; Rotate the first arc through (A-a)/2 radians. +; +; xRotated = y * sin(angle) + x * cos(angle) +; yRotated = y * cos(angle) - x * sin(angle) +; + + DEFB $E5 ;;get-mem-5 x, y, yy, xx, A. + DEFB $E0 ;;get-mem-0 x, y, yy, xx, A, a. + DEFB $03 ;;subtract x, y, yy, xx, A-a. + DEFB $A2 ;;stk-half x, y, yy, xx, A-a, 1/2. + DEFB $04 ;;multiply x, y, yy, xx, (A-a)/2. (=angle) + DEFB $31 ;;duplicate x, y, yy, xx, angle, angle. + DEFB $1F ;;sin x, y, yy, xx, angle, sin(angle) + DEFB $C5 ;;st-mem-5 x, y, yy, xx, angle, sin(angle) + DEFB $02 ;;delete x, y, yy, xx, angle + + DEFB $20 ;;cos x, y, yy, xx, cos(angle). + +; mem-0, mem-1 and mem-2 can be used again now... + + DEFB $C0 ;;st-mem-0 x, y, yy, xx, cos(angle). + DEFB $02 ;;delete x, y, yy, xx. + + DEFB $C2 ;;st-mem-2 x, y, yy, xx. + DEFB $02 ;;delete x, y, yy. + + DEFB $C1 ;;st-mem-1 x, y, yy. + DEFB $E5 ;;get-mem-5 x, y, yy, sin(angle) + DEFB $04 ;;multiply x, y, yy*sin(angle). + DEFB $E0 ;;get-mem-0 x, y, yy*sin(angle), cos(angle) + DEFB $E2 ;;get-mem-2 x, y, yy*sin(angle), cos(angle), xx. + DEFB $04 ;;multiply x, y, yy*sin(angle), xx*cos(angle). + DEFB $0F ;;addition x, y, xRotated. + DEFB $E1 ;;get-mem-1 x, y, xRotated, yy. + DEFB $01 ;;exchange x, y, yy, xRotated. + DEFB $C1 ;;st-mem-1 x, y, yy, xRotated. + DEFB $02 ;;delete x, y, yy. + + DEFB $E0 ;;get-mem-0 x, y, yy, cos(angle). + DEFB $04 ;;multiply x, y, yy*cos(angle). + DEFB $E2 ;;get-mem-2 x, y, yy*cos(angle), xx. + DEFB $E5 ;;get-mem-5 x, y, yy*cos(angle), xx, sin(angle). + DEFB $04 ;;multiply x, y, yy*cos(angle), xx*sin(angle). + DEFB $03 ;;subtract x, y, yRotated. + DEFB $C2 ;;st-mem-2 x, y, yRotated. + +; Now the initial x and y coordinates are made positive and summed to see +; if they measure up to anything significant. + + DEFB $2A ;;abs x, y, yRotated'. + DEFB $E1 ;;get-mem-1 x, y, yRotated', xRotated. + DEFB $2A ;;abs x, y, yRotated', xRotated'. + DEFB $0F ;;addition x, y, yRotated+xRotated. + DEFB $02 ;;delete x, y. + + DEFB $38 ;;end-calc x, y. + +; Although the test value has been deleted it is still above the calculator +; stack in memory and conveniently DE which points to the first free byte +; addresses the exponent of the test value. + + LD A,(DE) ; Fetch exponent of the length indicator. + CP $81 ; Compare to that for 1 + + POP BC ; Balance the machine stack + + JP C,L2477 ; forward, if the coordinates of first line + ; don't add up to more than 1, to LINE-DRAW + +; Continue when the arc will have a discernable shape. + + PUSH BC ; Restore line counter to the machine stack. + +; The parameters of the DRAW command were relative and they are now converted +; to absolute coordinates by adding to the coordinates of the last point +; plotted. + + RST 28H ;; FP-CALC x, y. + DEFB $01 ;;exchange y, x. + DEFB $38 ;;end-calc y, x. + + LD A,(COORDS) ;; Fetch System Variable COORDS-x + CALL L2D28 ;; routine STACK-A + + RST 28H ;; FP-CALC y, x, last-x. + +; Store the last point plotted to initialize the moving ax value. + + DEFB $C0 ;;st-mem-0 y, x, last-x. + DEFB $0F ;;addition y, absolute x. + DEFB $01 ;;exchange tx, y. + DEFB $38 ;;end-calc tx, y. + + LD A,(COORDS + 1) ; Fetch System Variable COORDS-y + CALL L2D28 ; routine STACK-A + + RST 28H ;; FP-CALC tx, y, last-y. + +; Store the last point plotted to initialize the moving ay value. + + DEFB $C5 ;;st-mem-5 tx, y, last-y. + DEFB $0F ;;addition tx, ty. + +; Fetch the moving ax and ay to the calculator stack. + + DEFB $E0 ;;get-mem-0 tx, ty, ax. + DEFB $E5 ;;get-mem-5 tx, ty, ax, ay. + DEFB $38 ;;end-calc tx, ty, ax, ay. + + POP BC ; Restore the straight line count. + +; ----------------------------------- +; THE 'CIRCLE/DRAW CONVERGENCE POINT' +; ----------------------------------- + +L2420: + DEC B ; decrement the arc count (4,8,12,16...). + + JP L2439 ; forward to ARC-START + +; -------------- +; THE 'ARC LOOP' +; -------------- + +L2425: +#ifdef DRAW3_DEBUG +DRAW3_BP_ARCLOOP: ; breakpoint: inicio de cada vuelta del bucle (rotacion) +#endif + RST 28H ;; FP-CALC + DEFB $E1 ;;get-mem-1 rx. + DEFB $31 ;;duplicate rx, rx. + DEFB $E3 ;;get-mem-3 cos(a) + DEFB $04 ;;multiply rx, rx*cos(a). + DEFB $E2 ;;get-mem-2 rx, rx*cos(a), ry. + DEFB $E4 ;;get-mem-4 rx, rx*cos(a), ry, sin(a). + DEFB $04 ;;multiply rx, rx*cos(a), ry*sin(a). + DEFB $03 ;;subtract rx, rx*cos(a) - ry*sin(a) + DEFB $C1 ;;st-mem-1 rx, new relative x rotated. + DEFB $02 ;;delete rx. + + DEFB $E4 ;;get-mem-4 rx, sin(a). + DEFB $04 ;;multiply rx*sin(a) + DEFB $E2 ;;get-mem-2 rx*sin(a), ry. + DEFB $E3 ;;get-mem-3 rx*sin(a), ry, cos(a). + DEFB $04 ;;multiply rx*sin(a), ry*cos(a). + DEFB $0F ;;addition rx*sin(a) + ry*cos(a). + DEFB $C2 ;;st-mem-2 new relative y rotated. + DEFB $02 ;;delete . + DEFB $38 ;;end-calc . + +;; ARC-START +L2439: +#ifdef DRAW3_DEBUG +DRAW3_BP_ARCSTART: ; breakpoint: calculo de Dx/Dy de cada segmento +#endif + PUSH BC ; Preserve the arc counter on the machine stack. + + RST 28H ;; FP-CALC ax, ay. + DEFB $C0 ;;st-mem-0 ax, ay. + DEFB $02 ;;delete ax. + + DEFB $E1 ;;get-mem-1 ax, xr. + DEFB $0F ;;addition ax+xr (= new ax). + DEFB $31 ;;duplicate ax, ax. + DEFB $38 ;;end-calc ax, ax. + + LD A,(COORDS) ; COORDS-x last x (integer ix 0-255) + CALL L2D28 ; routine STACK-A + + RST 28H ;; FP-CALC ax, ax, ix. + DEFB $03 ;;subtract ax, ax-ix = relative DRAW Dx. + + DEFB $E0 ;;get-mem-0 ax, Dx, ay. + DEFB $E2 ;;get-mem-2 ax, Dx, ay, ry. + DEFB $0F ;;addition ax, Dx, ay+ry (= new ay). + DEFB $C0 ;;st-mem-0 ax, Dx, ay. + DEFB $01 ;;exchange ax, ay, Dx, + DEFB $E0 ;;get-mem-0 ax, ay, Dx, ay. + DEFB $38 ;;end-calc ax, ay, Dx, ay. + + LD A,(COORDS + 1) ; COORDS-y last y (integer iy 0-175) + CALL L2D28 ; routine STACK-A + + RST 28H ;; FP-CALC ax, ay, Dx, ay, iy. + DEFB $03 ;;subtract ax, ay, Dx, ay-iy ( = Dy). + DEFB $38 ;;end-calc ax, ay, Dx, Dy. + + CALL L2477 ; Routine DRAW-LINE draws (Dx,Dy) relative to + ; the last pixel plotted leaving absolute x + ; and y on the calculator stack. + ; ax, ay. + + POP BC ; Restore the arc counter from the machine stack. + + DJNZ L2425 ; Decrement and loop while > 0 to ARC-LOOP + +; ------------- +; THE 'ARC END' +; ------------- + +L245F: +#ifdef DRAW3_DEBUG +DRAW3_BP_ARCEND: ; breakpoint: ultimo segmento hasta el punto final +#endif + RST 28H ;; FP-CALC tx, ty, ax, ay. + DEFB $02 ;;delete tx, ty, ax. + DEFB $02 ;;delete tx, ty. + DEFB $01 ;;exchange ty, tx. + DEFB $38 ;;end-calc ty, tx. + +; First calculate the relative x coordinate to the end-point. + + LD A,(COORDS) ; COORDS-x + CALL L2D28 ; routine STACK-A + + RST 28H ;; FP-CALC ty, tx, coords_x. + DEFB $03 ;;subtract ty, rx. + +; Next calculate the relative y coordinate to the end-point. + + DEFB $01 ;;exchange rx, ty. + DEFB $38 ;;end-calc rx, ty. + + LD A,(COORDS + 1) ; COORDS-y + CALL L2D28 ; routine STACK-A + + RST 28H ;; FP-CALC rx, ty, coords_y + DEFB $03 ;;subtract rx, ry. + DEFB $38 ;;end-calc rx, ry. +; Finally draw the last straight line. +L2477: +#ifdef DRAW3_DEBUG +DRAW3_BP_LINE: ; breakpoint: aqui B,C,D,E = linea que se va a dibujar +#endif + call L2307 ;;Pops x, and y, and stores it in B, C + +#ifdef DRAW3_DEBUG + push af + push hl + ld a, (DRAW3_DEBUG_N) + cp 16 + jr nc, DRAW3_DEBUG_SKIP2 + ld hl, (DRAW3_DEBUG_PTR) + ld (hl), b ; Y magnitude + inc hl + ld (hl), c ; X magnitude + inc hl + ld (hl), d ; Y sign (bit7 via RL later) + inc hl + ld (hl), e ; X sign (bit7 via RL later) + inc hl + ld a, (COORDS) + ld (hl), a ; COORDS-x before this line + inc hl + ld a, (COORDS + 1) + ld (hl), a ; COORDS-y before this line + inc hl + ld (DRAW3_DEBUG_PTR), hl + ld a, (DRAW3_DEBUG_N) + inc a + ld (DRAW3_DEBUG_N), a +DRAW3_DEBUG_SKIP2: + pop hl + pop af +#endif + + ld hl, (COORDS) ;;Calculates x2 and y2 in L, H + + rl e ;; Rotate left to carry + ld a, c + jr nc, SUM_C + neg +SUM_C: + add a, l + ld l, a ;; X2 + + rl d ;; Low sign to carry + ld a, b + jr nc, SUM_B + neg +SUM_B: + add a, h + ld h, a + jp __DRAW ;;forward to LINE-DRAW (Fastcalled) + + ENDP + +#ifdef DRAW3_DEBUG +DRAW3_DEBUG_N: defb 0 +DRAW3_DEBUG_PTR: defw DRAW3_DEBUG_BUF +DRAW3_DEBUG_BUF: defs 16 * 6 +#endif + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/error.asm b/src/lib/arch/zx81sd/runtime/error.asm new file mode 100644 index 000000000..d0dd52c0a --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/error.asm @@ -0,0 +1,40 @@ +; Simple error control routines — ZX81 + SD81 Booster +; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. +; Las interrupciones ya están desactivadas (DI desde el bootstrap). + +#include once + + push namespace core + +; Códigos de error (compatibles con el manual del ZX Spectrum) +ERROR_Ok EQU -1 +ERROR_SubscriptWrong EQU 2 +ERROR_OutOfMemory EQU 3 +ERROR_OutOfScreen EQU 4 +ERROR_NumberTooBig EQU 5 +ERROR_InvalidArg EQU 9 +ERROR_IntOutOfRange EQU 10 +ERROR_NonsenseInBasic EQU 11 +ERROR_InvalidFileName EQU 14 +ERROR_InvalidColour EQU 19 +ERROR_BreakIntoProgram EQU 20 +ERROR_TapeLoadingErr EQU 26 + +; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + +; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/fp_calc.asm b/src/lib/arch/zx81sd/runtime/fp_calc.asm new file mode 100644 index 000000000..adc53f1c7 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/fp_calc.asm @@ -0,0 +1,1996 @@ +; =========================================================================== +; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster +; +; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las +; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay +; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero +; nuestro binario compilado). +; +; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, +; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, +; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de +; operacion calculadora, y ahora $0028 contiene codigo real (ver +; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. +; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica +; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas +; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el +; "series generator" ya portado aqui, mas la tabla de coeficientes de cada +; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no +; evaluacion de expresiones completas como hace la ROM real — ver conversacion +; de diseño). +; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits +; con el bit 7 del byte2 usado como signo (bit de mantisa +; implicito, siempre a 1 salvo cuando se usa para el signo) +; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT +; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son +; reubicables) para facilitar la referencia cruzada con el disassembly. +; =========================================================================== + +#include once +#include once +#include once +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): +; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e +; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), +; en vez de depender de que el fichero que lo incluya lo haga tambien. + + push namespace core + +; --------------------------------------------------------------------------- +; Punto de entrada — sustituye a RST $28h +; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + +; --------------------------------------------------------------------------- +; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio +; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un +; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que +; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. +; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + +; --------------------------------------------------------------------------- +; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) +; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + +; --------------------------------------------------------------------------- +; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL +; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + +; --------------------------------------------------------------------------- +; MOVE-FP / duplicate (literal $31, $33C0) +; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + +; --------------------------------------------------------------------------- +; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) +; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e + +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + +; --------------------------------------------------------------------------- +; SKIP-CONS ($33F7 / $33F8) +; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + +; --------------------------------------------------------------------------- +; LOC-MEM ($3406) +; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + +; --------------------------------------------------------------------------- +; get-mem-xx (literales $E0-$FF, $340F) +; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + +; --------------------------------------------------------------------------- +; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes +; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + +; --------------------------------------------------------------------------- +; st-mem-xx (literales $C0-$DF, $342D) +; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + +; --------------------------------------------------------------------------- +; exchange (literal $01, $343C) +; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + +; --------------------------------------------------------------------------- +; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN +; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) +; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + +; --------------------------------------------------------------------------- +; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, +; $3492) +; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 + +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret + +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + +; --------------------------------------------------------------------------- +; INT-FETCH ($2D7F) / INT-STORE ($2D8E) +; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret + +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + +; --------------------------------------------------------------------------- +; PREP-ADD ($2F9B) +; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + +; --------------------------------------------------------------------------- +; FETCH-TWO ($2FBA) +; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + +; --------------------------------------------------------------------------- +; SHIFT-FP ($2FDD) / ADD-BACK ($3004) +; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret + +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + +; --------------------------------------------------------------------------- +; subtract (literal $03, $300F) / addition (literal $0F, $3014) +; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl + +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + +; --------------------------------------------------------------------------- +; HL-HL*DE ($30A9) / PREP-M/D ($30C0) +; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret + +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + +; --------------------------------------------------------------------------- +; multiply (literal $04, $30CA) +; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret + +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + +; --------------------------------------------------------------------------- +; division (literal $05, $31AF) +; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + +; --------------------------------------------------------------------------- +; Integer truncation towards zero (literal $3A, $3214) +; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + +; --------------------------------------------------------------------------- +; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) +; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + +; --------------------------------------------------------------------------- +; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) +; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + +; --------------------------------------------------------------------------- +; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs +; +; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, +; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion +; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro +; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). +; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF + +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + +; --------------------------------------------------------------------------- +; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal +; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + +; --------------------------------------------------------------------------- +; delete (literal $02) — un simple RET; tambien destino de salto indirecto +; --------------------------------------------------------------------------- +L33A1: + ret + +; --------------------------------------------------------------------------- +; fp-calc-2 (literal $3B) — reentrada de un solo literal +; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + +; --------------------------------------------------------------------------- +; STK-PNTRS ($35BF) +; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + +; --------------------------------------------------------------------------- +; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) +; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret + +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret + +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + +; --------------------------------------------------------------------------- +; end-calc (literal $38) +; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + +; --------------------------------------------------------------------------- +; n-mod-m (literal $32) — implementado como programa del propio calculador +; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + +; --------------------------------------------------------------------------- +; int (literal $27) — implementado como programa del propio calculador +; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + +; --------------------------------------------------------------------------- +; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / +; less-0 (literal $36) / SIGN-TO-C / FP-0/1 +; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret + +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 + +L3501: + call L34E9 + jr L350B + +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + +; --------------------------------------------------------------------------- +; or (literal $07) / no-&-no (literal $08) +; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B + +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + +; --------------------------------------------------------------------------- +; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; +; no se soportan comparaciones de cadenas via calculador en este runtime. +; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + +; --------------------------------------------------------------------------- +; END-TESTS ($358C) +; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + +; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR +; +; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, +; programas del propio calculador (rst 28h + bytes de literal), identicos a +; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. +; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se +; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el +; mecanismo de error propio (ERROR_xxx + jp __ERROR). +; =========================================================================== + +; --------------------------------------------------------------------------- +; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño +; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + +; --------------------------------------------------------------------------- +; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) +; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + +; --------------------------------------------------------------------------- +; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry +; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + +; --------------------------------------------------------------------------- +; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 +; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + +; --------------------------------------------------------------------------- +; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) +; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + +; --------------------------------------------------------------------------- +; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) +; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + +; --------------------------------------------------------------------------- +; tan (literal $21, $37DA) — sin(x) / cos(x) +; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + +; --------------------------------------------------------------------------- +; atn (literal $24, $37E2) +; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + +; --------------------------------------------------------------------------- +; asn (literal $22, $3833) +; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + +; --------------------------------------------------------------------------- +; acs (literal $23, $3843) +; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + +; --------------------------------------------------------------------------- +; ln (literal $25, $3713) +; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + +; --------------------------------------------------------------------------- +; exp (literal $26, $36C4) +; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + +; --------------------------------------------------------------------------- +; sqr (literal $28, $384A) — cae en to-power (codigo compartido) +; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + +; --------------------------------------------------------------------------- +; to-power (literal $06, $3851) +; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + +; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 +; +; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. +; No son literales del calculador (no se invocan via rst 28h + defb), sino +; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, +; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). +; =========================================================================== + +; --------------------------------------------------------------------------- +; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. +; C = $01 si es positivo o cero, $FF si es negativo. +; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. +; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret + +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + +; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas +; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). +; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + +; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope +; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas +; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y +; sin(a) del angulo de paso "a" = ANGULO/lineas. +; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE + +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) + +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + + pop bc ; restaura el contador de lineas + ret + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/fp_tostr.asm b/src/lib/arch/zx81sd/runtime/fp_tostr.asm new file mode 100644 index 000000000..50d379bc5 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/fp_tostr.asm @@ -0,0 +1,298 @@ +; fp_tostr.asm (zx81sd) — Conversión de un FLOAT a texto ASCII decimal +; +; Usado por printf.asm (PRINT de un FLOAT) y str.asm (STR$) en zx81sd. +; +; La ROM del Spectrum resuelve esto con PRINT-FP, una rutina enorme que +; además soporta notación científica (E-format) y se apoya en mecanismos +; ajenos a zx81sd (canales CHAN-OPEN, área de "workspace" del editor BASIC +; que crece con BC-SPACES). Aquí, por acuerdo explícito, se implementa una +; versión simplificada: signo + parte entera + hasta 5 decimales, sin +; notación científica, recortando ceros finales (y el punto si todos los +; decimales resultan cero). Cubre el uso habitual de PRINT/STR$ con FLOAT. +; +; Se apoya en el calculador ya portado (fp_calc.asm: duplicate/int/subtract/ +; negate) y en rutinas Z80 puras ya existentes y verificadas en zx81sd: +; __FTOU32REG/__FTOU8 (conversión FLOAT -> entero, sin ROM) y __DIVU32. + +#include once +#include once +#include once +#include once + + push namespace core + +; --- Variables de trabajo (no reentrante, uso transitorio) ------------------ +FP_STR_ORIG: defb 0, 0, 0, 0, 0 ; A E D C B del valor (se vuelve positivo) +FP_STR_INT: defb 0, 0, 0, 0, 0 ; parte entera +FP_STR_FRAC: defb 0, 0, 0, 0, 0 ; parte fraccionaria (se actualiza cada iteración) +FP_STR_COUNT: defb 0 ; contador de decimales restantes +FP_STR_WR: defw 0 ; cursor de escritura en FP_STR_BUF +FP_STR_BUF: defs 24 ; buffer de salida (signo + entero + '.' + decimales) + +; --------------------------------------------------------------------------- +; STORE5 — Guarda A,E,D,C,B en (HL),(HL+1)..(HL+4) +; LOAD5 — Carga A,E,D,C,B desde (HL),(HL+1)..(HL+4) +; (Z80 no tiene LD E,(nn)/LD D,(nn)/etc, solo LD A,(nn) y LD rr,(nn) con pares; +; por eso se accede siempre indirectamente vía HL.) +; --------------------------------------------------------------------------- +STORE5: + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + ret + +LOAD5: + ld a, (hl) + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ret + +; --------------------------------------------------------------------------- +; EMIT_CHAR — Escribe A en el buffer de salida y avanza el cursor +; --------------------------------------------------------------------------- +EMIT_CHAR: + push hl + ld hl, (FP_STR_WR) + ld (hl), a + inc hl + ld (FP_STR_WR), hl + pop hl + ret + +; --------------------------------------------------------------------------- +; EMIT_U32 — Escribe DEHL (entero sin signo de 32 bits) como digitos decimales +; en el buffer de salida (sin ceros a la izquierda; "0" si el valor es cero). +; Mismo algoritmo que __PRINTU32 (printi32.asm/printnum.asm), pero escribiendo +; en el buffer en vez de en pantalla. +; --------------------------------------------------------------------------- +EMIT_U32: + PROC + LOCAL EMIT_U32_LOOP + LOCAL EMIT_U32_START + LOCAL EMIT_U32_CONT + + ld b, 0 + +EMIT_U32_LOOP: + ld a, h + or l + or d + or e + jp z, EMIT_U32_START + + push bc + ld bc, 0 + push bc + ld bc, 10 + push bc + call __DIVU32 + pop bc + + exx + ld a, l + or '0' + push af + exx + inc b + jp EMIT_U32_LOOP + +EMIT_U32_START: + ld a, b + or a + jp nz, EMIT_U32_CONT + ld a, '0' + call EMIT_CHAR + ret + +EMIT_U32_CONT: + pop af + push bc + call EMIT_CHAR + pop bc + djnz EMIT_U32_CONT + ret + + ENDP + +; --------------------------------------------------------------------------- +; FP_TO_STR — Convierte un FLOAT a texto ASCII decimal +; Entrada: A,E,D,C,B = valor FLOAT (convención habitual del runtime) +; Salida: HL = puntero al texto (sin prefijo de longitud), BC = longitud +; --------------------------------------------------------------------------- +FP_TO_STR: + PROC + LOCAL FP_TO_STR_POS + LOCAL FP_TO_STR_POS2 + LOCAL FP_TO_STR_FRACLOOP + LOCAL FP_TO_STR_FRACDONE + LOCAL FP_TO_STR_TRIMLOOP + LOCAL FP_TO_STR_TRIMDOT + LOCAL FP_TO_STR_TRIMKEEP + LOCAL FP_TO_STR_DONE + + ld hl, FP_STR_ORIG + call STORE5 + + ld hl, FP_STR_BUF + ld (FP_STR_WR), hl + + ; ¿Es cero? (forma canonica: A=0 y mantisa completa a 0) + or e + or d + or c + or b + jr nz, FP_TO_STR_POS + ld a, '0' + call EMIT_CHAR + jp FP_TO_STR_DONE + +FP_TO_STR_POS: + ; El bit 7 de E es el signo tanto en formato entero-pequeno como en + ; coma flotante completa (ver fp_calc.asm / __FTOU32REG). + ld a, (FP_STR_ORIG + 1) + bit 7, a + jr z, FP_TO_STR_POS2 + + ld a, '-' + call EMIT_CHAR + + ; Vuelve positivo el valor original (negate) para trabajar siempre en abs + ld hl, FP_STR_ORIG + call LOAD5 + call __FPSTACK_PUSH + rst 28h + defb $1B ;;negate + defb $38 ;;end-calc + call __FPSTACK_POP + ld hl, FP_STR_ORIG + call STORE5 + +FP_TO_STR_POS2: + ; intx = INT(x) (x ya es >= 0 en este punto) + ld hl, FP_STR_ORIG + call LOAD5 + call __FPSTACK_PUSH + rst 28h + defb $31 ;;duplicate + defb $27 ;;int + defb $38 ;;end-calc + call __FPSTACK_POP + ld hl, FP_STR_INT + call STORE5 + + ; frac = x - intx + ld hl, FP_STR_INT + call LOAD5 + call __FPSTACK_PUSH + rst 28h + defb $03 ;;subtract + defb $38 ;;end-calc + call __FPSTACK_POP + ld hl, FP_STR_FRAC + call STORE5 + + ; imprime la parte entera (ya es >= 0, cabe en 32 bits sin signo) + ld hl, FP_STR_INT + call LOAD5 + call __FTOU32REG ; DEHL = parte entera + call EMIT_U32 + + ld a, '.' + call EMIT_CHAR + ld a, 5 + ld (FP_STR_COUNT), a + +FP_TO_STR_FRACLOOP: + ld hl, FP_STR_FRAC + call LOAD5 + call __FPSTACK_PUSH ; pila: [frac] + + xor a + ld d, 10 + ld e, a + ld c, a + ld b, a + call __FPSTACK_PUSH ; pila: [frac, 10] + rst 28h + defb $04 ;;multiply + defb $38 ;;end-calc + ; pila: [frac*10] + rst 28h + defb $31 ;;duplicate + defb $27 ;;int + defb $38 ;;end-calc + ; pila: [frac*10, digito] + call __FPSTACK_POP + call __FTOU8 ; A = digito (0-9) + push af ; guarda el digito (igual que val.asm con rst 28h) + + pop af + push af + ld d, a + xor a + ld e, a + ld c, a + ld b, a + call __FPSTACK_PUSH ; pila: [frac*10, digito] + rst 28h + defb $03 ;;subtract + defb $38 ;;end-calc + ; pila: [frac*10 - digito] = nuevo frac + call __FPSTACK_POP + ld hl, FP_STR_FRAC + call STORE5 + + pop af ; recupera el digito + or '0' + call EMIT_CHAR + + ld hl, FP_STR_COUNT + dec (hl) + jr nz, FP_TO_STR_FRACLOOP + +FP_TO_STR_FRACDONE: + ; recorta ceros finales (y el punto si todos los decimales eran cero) + ld hl, (FP_STR_WR) + +FP_TO_STR_TRIMLOOP: + dec hl + ld a, (hl) + cp '.' + jr z, FP_TO_STR_TRIMDOT + cp '0' + jr nz, FP_TO_STR_TRIMKEEP + jr FP_TO_STR_TRIMLOOP + +FP_TO_STR_TRIMDOT: + ld (FP_STR_WR), hl + jp FP_TO_STR_DONE + +FP_TO_STR_TRIMKEEP: + inc hl + ld (FP_STR_WR), hl + +FP_TO_STR_DONE: + ld hl, (FP_STR_WR) + ld de, FP_STR_BUF + or a + sbc hl, de ; HL = longitud del texto + ld b, h + ld c, l + ld hl, FP_STR_BUF + ret + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/io/keyboard/inkey.asm b/src/lib/arch/zx81sd/runtime/io/keyboard/inkey.asm new file mode 100644 index 000000000..992f0c7c7 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/io/keyboard/inkey.asm @@ -0,0 +1,53 @@ +; INKEY Function (zx81sd) +; Returns a string allocated in dynamic memory +; containing the string. +; An empty string otherwise. +; +; La version de zx48k llama a rutinas de la ROM del Spectrum +; (KEY_SCAN/KEY_TEST/KEY_CODE) que no existen en tiempo de ejecucion en +; zx81sd (no hay ROM mapeada). Se sustituye por un escaneo directo de la +; matriz de teclado del ZX81 y una decodificacion propia a ASCII. + +#include once +#include once + + push namespace core + +INKEY: + PROC + LOCAL __EMPTY_INKEY + + ld bc, 3 ; 1 char length string + call __MEM_ALLOC + + ld a, h + or l + ret z ; Return if NULL (No memory) + + push hl ; Saves memory pointer + + call __ZX81SD_KEYSCAN + pop hl + or a + jr z, __EMPTY_INKEY + + ld (hl), 1 + inc hl + ld (hl), 0 + inc hl + ld (hl), a + dec hl + dec hl ; HL Points to string result + ret + +__EMPTY_INKEY: + xor a + ld (hl), a + inc hl + ld (hl), a + dec hl + ret + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/io/keyboard/keyscan.asm b/src/lib/arch/zx81sd/runtime/io/keyboard/keyscan.asm new file mode 100644 index 000000000..8bf0a78cd --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/io/keyboard/keyscan.asm @@ -0,0 +1,397 @@ +; --------------------------------------------------------------------------- +; keyscan.asm — Escaneo directo de la matriz de teclado del ZX81 +; +; No hay ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa por +; completo nuestro binario compilado), asi que no se puede llamar a la +; rutina KEYBOARD/DECODE de la ROM original. El hardware del teclado, en +; cambio, es el mismo ZX81 de siempre (el SD81 Booster no lo toca), asi +; que se reimplementa aqui el escaneo fisico (puerto $FEFE, 8 filas +; seleccionadas rotando el registro alto del puerto) y una decodificacion +; propia a ASCII. +; +; ESQUEMA DE TECLAS (2026-07-04, revisado tras probar en el emulador) +; -------------------------------------------------------------------- +; El teclado fisico del ZX81 no tiene mayuscula/minuscula por tecla (SHIFT +; da simbolos, no la version en mayuscula de la letra) — a diferencia del +; Spectrum, donde SHIFT+letra si da la mayuscula de esa letra via la ROM +; (K-DECODE en modo L). Para poder imprimir el juego de caracteres +; completo sin depender de teclado externo: +; +; Sin modificador -> minuscula (a, s, d... como hasta ahora) +; SHIFT + letra -> MAYUSCULA de esa letra +; SHIFT + "2" -> conmuta CAPS LOCK (persistente; no imprime nada) +; CAPS LOCK activo -> minuscula pasa a mayuscula (SHIFT sigue dando +; mayuscula igual, no hay interaccion: es un OR) +; "." (con o sin SHIFT) -> "." o "," exactamente igual que en el ZX81 +; real (SHIFT+"." = ",") +; +; SHIFT es comodo de mantener pulsado con una mano mientras se pulsa la +; letra con la otra (aqui __ZX81SD_KEYSCAN SI detecta la simultaneidad +; real, con dos lecturas de puerto dedicadas para SHIFT y una tercera +; tecla). Pero un primer diseno que anadia un segundo modificador de +; simbolo con "." (pulsar "."+tecla a la vez para sacar el simbolo +; impreso en el teclado del ZX81) resulto impracticable de teclear: al +; usarse desde INPUT, la rutina de lectura ya compromete la tecla "." +; en cuanto se detecta pulsada sola, sin dar tiempo a que la segunda +; tecla llegue a la vez (no son dedos que se muevan en paralelo como +; SHIFT+letra, sino una pulsacion despues de otra). Por eso ese segundo +; modificador se ha movido a stdlib/input.bas, como una "tecla muerta" +; a nivel de composicion de caracteres (se pulsa "." y LUEGO la tecla +; del simbolo, secuencialmente, sin necesidad de mantenerlas a la vez): +; ver __ZX81SD_SYMBOL_FOR mas abajo, que da el mapeo tecla->simbolo que +; usa esa logica. +; +; IMPORTANTE: la ROM del ZX81 decodifica las teclas a SU PROPIO charset +; (con codigos de token para palabras clave de BASIC), no a ASCII. El +; runtime de zx81sd usa el charset Spectrum/ASCII para imprimir (ver +; charset.asm, specfont.bin), asi que las tablas de decodificacion de +; abajo traducen directamente cada tecla a su codigo ASCII, ignorando las +; teclas que en el ZX81 producen tokens de BASIC sin equivalente ASCII +; simple (STOP, AND, OR, THEN, TO, STEP, <=, >=, <>, **, EDIT, GRAPHICS, +; FUNCTION, cursores, LPRINT, LLIST, SLOW, FAST). La tecla RUBOUT +; (SHIFT+0) se traduce a ASCII 12, y ENTER (NEWLINE) a ASCII 13, para +; mantener compatibilidad con la convencion ya usada por stdlib/input.bas. +; --------------------------------------------------------------------------- + + push namespace core + +; --- Tablas de decodificacion, en ambito de fichero (no LOCAL a ningun +; PROC): las usa __ZX81SD_KEYSCAN y tambien __ZX81SD_SYMBOL_FOR. --- +; +; Orden de filas/columnas identico al de las tablas K-UNSHIFT/K-SHIFT de +; la ROM original del ZX81 (fila 0: SHIFT,Z,X,C,V ... fila 7: ENTER,L,K, +; J,H / SPACE,.,M,N,B), verificado contra el disassembly. +__ZX81SD_UNSHIFT_TABLE: + DEFB 'z', 'x', 'c', 'v' + DEFB 'a', 's', 'd', 'f', 'g' + DEFB 'q', 'w', 'e', 'r', 't' + DEFB '1', '2', '3', '4', '5' + DEFB '0', '9', '8', '7', '6' + DEFB 'p', 'o', 'i', 'u', 'y' + DEFB 13, 'l', 'k', 'j', 'h' ; NEWLINE -> ENTER (ASCII 13) + DEFB ' ', '.', 'm', 'n', 'b' + +; __ZX81SD_SYMBOL_TABLE — simbolos impresos en el teclado del ZX81 bajo +; SHIFT. Alineada posicion a posicion con __ZX81SD_UNSHIFT_TABLE (mismo +; indice = misma tecla fisica); la usa __ZX81SD_SYMBOL_FOR para el modo +; de composicion "." + tecla de stdlib/input.bas. +__ZX81SD_SYMBOL_TABLE: + DEFB ':', ';', '?', '/' + DEFB 0, 0, 0, 0, 0 ; STOP, LPRINT, SLOW, FAST, LLIST + DEFB '"', 0, 0, 0, 0 ; "" (par de comillas), OR, STEP, <=, <> + DEFB 0, 0, 0, 0, 0 ; EDIT, [CAPS LOCK], THEN, TO, cursor-izq + DEFB 12, 0, 0, 0, 0 ; RUBOUT (DEL=12), GRAPHICS, cursor der/arr/abj + DEFB '"', ')', '(', '$', 0 ; ", ), (, $, >= + DEFB 0, '=', '+', '-', 0 ; FUNCTION, =, +, -, ** + DEFB 0, ',', '>', '<', '*' ; £, ',', >, <, * + +; __ZX81SD_CAPS_TABLE — igual que __ZX81SD_SYMBOL_TABLE, pero con la +; MAYUSCULA en cada posicion que en UNSHIFT_TABLE es una letra (para +; SHIFT+letra, y para el modo CAPS LOCK persistente). Las posiciones que +; no son letra se dejan igual que en SYMBOL_TABLE (digitos, ENTER, +; SPACE, RUBOUT...). +__ZX81SD_CAPS_TABLE: + DEFB 'Z', 'X', 'C', 'V' + DEFB 'A', 'S', 'D', 'F', 'G' + DEFB 'Q', 'W', 'E', 'R', 'T' + DEFB 0, 0, 0, 0, 0 ; digitos 1-5 (el "2" se intercepta antes) + DEFB 12, 0, 0, 0, 0 ; digitos 0,9,8,7,6 (RUBOUT en el "0") + DEFB 'P', 'O', 'I', 'U', 'Y' + DEFB 0, 'L', 'K', 'J', 'H' ; ENTER sin cambio, L/K/J/H en mayuscula + DEFB 0, ',', 'M', 'N', 'B' ; SPACE sin cambio, "." no se alcanza aqui + +; --- Estado persistente del teclado (sobrevive entre llamadas) --- +__ZX81SD_KBD_CAPSLOCK: DEFB 0 ; 1 = CAPS LOCK activo +__ZX81SD_KBD_CAPS_EDGE: DEFB 0 ; deteccion de flanco para el combo SHIFT+"2" + +; --- Estado transitorio (se recalcula en cada llamada a __ZX81SD_KEYSCAN) --- +__ZX81SD_KBD_SHIFT: DEFB 0 +__ZX81SD_KBD_OTHER_VALID: DEFB 0 +__ZX81SD_KBD_OTHER_IDX: DEFB 0 + +; --------------------------------------------------------------------------- +; __ZX81SD_KEYSCAN +; +; Escanea el teclado y decodifica la tecla pulsada segun el esquema de +; arriba (SHIFT como modificador de mayuscula, CAPS LOCK persistente). +; +; Devuelve: +; A = codigo ASCII de la tecla pulsada, o 0 si no hay ninguna pulsada, +; si la combinacion no tiene equivalente ASCII, si solo esta +; pulsado SHIFT solo, o si se acaba de conmutar el CAPS LOCK +; (SHIFT+"2"). +; Flag Z activo si A = 0 +; +; Registros modificados: AF, BC, DE, HL +; --------------------------------------------------------------------------- +__ZX81SD_KEYSCAN: + PROC + LOCAL FIND_OTHER + LOCAL ROW_LOOP + LOCAL NEXT_ROW + LOCAL FIND_COL + LOCAL GOT_COL0 + LOCAL GOT_COL1 + LOCAL GOT_COL2 + LOCAL GOT_COL3 + LOCAL GOT_COL4 + LOCAL GOT_KEY + LOCAL OTHER_FOUND + LOCAL OTHER_NONE + LOCAL NOT_ROW0 + + LOCAL DECIDE + LOCAL HAVE_SHIFT + LOCAL SHIFT_ALONE + LOCAL CHECK_CAPS_KEY + LOCAL DO_CAPS_TOGGLE + LOCAL CAPS_EDGE_DONE + LOCAL NO_SHIFT + LOCAL NO_KEY + LOCAL USE_UNSHIFT + LOCAL USE_CAPS_FOR_PLAIN + LOCAL LOOKUP_OTHER_IN_HL + LOCAL RESET_CAPS_EDGE + + LOCAL SHIFT_BIT_SET + + ; --- 1. Leer SHIFT (fila 0, columna 0) con una lectura dedicada --- + ld bc, $FEFE + in a, (c) + and $01 + ld a, 1 + jr z, SHIFT_BIT_SET + xor a +SHIFT_BIT_SET: + ld (__ZX81SD_KBD_SHIFT), a + + ; --- 2. Buscar otra tecla pulsada (excluyendo SHIFT) --- + xor a + ld (__ZX81SD_KBD_OTHER_VALID), a + call FIND_OTHER ; carry activo = encontrada (indice en A) + jr nc, DECIDE + ld (__ZX81SD_KBD_OTHER_IDX), a + ld a, 1 + ld (__ZX81SD_KBD_OTHER_VALID), a + +DECIDE: + ld a, (__ZX81SD_KBD_SHIFT) + or a + jr nz, HAVE_SHIFT + jr NO_SHIFT + +HAVE_SHIFT: + ld a, (__ZX81SD_KBD_OTHER_VALID) + or a + jr nz, CHECK_CAPS_KEY + +SHIFT_ALONE: + ; Solo SHIFT: sin caracter, y se reinicia la deteccion de flanco del + ; combo SHIFT+"2" (no puede haber toggle sin la tecla "2" presente). + call RESET_CAPS_EDGE + xor a + ret + +CHECK_CAPS_KEY: + ld a, (__ZX81SD_KBD_OTHER_IDX) + cp 15 ; indice de la tecla "2" (fila 3, columna 1) + jr z, DO_CAPS_TOGGLE + + ; SHIFT + letra (o cualquier otra tecla, salvo "2"): __ZX81SD_CAPS_TABLE + ; (mayuscula en las posiciones de letra; en el resto, igual que + ; __ZX81SD_SYMBOL_TABLE — p.ej. SHIFT+"." sigue dando ","). + call RESET_CAPS_EDGE + ld hl, __ZX81SD_CAPS_TABLE + jr LOOKUP_OTHER_IN_HL + +DO_CAPS_TOGGLE: + ; Conmuta CAPS LOCK solo en el flanco de subida del combo (para no + ; conmutarlo en cada llamada mientras se mantiene pulsado). + ld a, (__ZX81SD_KBD_CAPS_EDGE) + or a + jr nz, CAPS_EDGE_DONE + ld a, 1 + ld (__ZX81SD_KBD_CAPS_EDGE), a + ld a, (__ZX81SD_KBD_CAPSLOCK) + xor 1 + ld (__ZX81SD_KBD_CAPSLOCK), a +CAPS_EDGE_DONE: + xor a ; SHIFT+"2" es mudo: nunca devuelve caracter + ret + +NO_SHIFT: + call RESET_CAPS_EDGE + + ld a, (__ZX81SD_KBD_OTHER_VALID) + or a + jr z, NO_KEY + + ; Sin SHIFT: __ZX81SD_UNSHIFT_TABLE (minuscula), salvo que CAPS LOCK + ; este activo, en cuyo caso se usa __ZX81SD_CAPS_TABLE (mayuscula en + ; las letras). "." cae aqui igual que cualquier otra tecla: siempre + ; devuelve "." (o "," si SHIFT, ya cubierto arriba). + ld a, (__ZX81SD_KBD_CAPSLOCK) + or a + jr z, USE_UNSHIFT + jr USE_CAPS_FOR_PLAIN + +USE_UNSHIFT: + ld hl, __ZX81SD_UNSHIFT_TABLE + jr LOOKUP_OTHER_IN_HL + +USE_CAPS_FOR_PLAIN: + ld hl, __ZX81SD_CAPS_TABLE + +LOOKUP_OTHER_IN_HL: + ld a, (__ZX81SD_KBD_OTHER_IDX) + ld e, a + ld d, 0 + add hl, de + ld a, (hl) + or a + ret + +NO_KEY: + xor a + ret + +RESET_CAPS_EDGE: + xor a + ld (__ZX81SD_KBD_CAPS_EDGE), a + ret + +; --------------------------------------------------------------------------- +; FIND_OTHER — busca una tecla pulsada distinta de SHIFT (fila 0, col 0). +; Devuelve acarreo INACTIVO si no encuentra ninguna; si encuentra una, +; acarreo ACTIVO y A = indice (0-38, formula fila*5+columna-1). Se usa el +; acarreo (no Z) porque el indice 0 (tecla Z) es un resultado valido con +; A=0, que haria Z activo por error si se usara ese flag para +; "encontrada/no encontrada". +; Registros modificados: AF, BC, DE, HL. +; --------------------------------------------------------------------------- +FIND_OTHER: + ld d, 0 ; D = indice de fila actual (0-7) + ld b, $FE ; B = mitad alta del puerto ($FEFE...$7FFE) + +ROW_LOOP: + ld c, $FE + in a, (c) ; lee la fila; bits 0-4 = columnas (0 = pulsada) + and $1F + + ld l, a ; L = mapa de bits de columnas pulsadas + + ld a, d + or a + jr nz, NOT_ROW0 + set 0, l ; fila 0: descartar SHIFT (columna 0) +NOT_ROW0: + ld a, l + cp $1F + jr z, NEXT_ROW ; nada mas pulsado en esta fila + +FIND_COL: + ; Bucle desenrollado a proposito: usar CP para comparar el contador + ; de columna sobreescribia A (el mapa de bits que se iba rotando) + ; antes de que la siguiente vuelta pudiera comprobarlo, con lo que + ; solo la columna 0 se detectaba bien. Sin bucle no hay ese riesgo. + ld a, l + rrca + jr nc, GOT_COL0 + rrca + jr nc, GOT_COL1 + rrca + jr nc, GOT_COL2 + rrca + jr nc, GOT_COL3 + rrca + jr nc, GOT_COL4 + jr NEXT_ROW ; no deberia ocurrir (ya se comprobo cp $1F antes) + +GOT_COL0: + ld c, 0 + jr GOT_KEY +GOT_COL1: + ld c, 1 + jr GOT_KEY +GOT_COL2: + ld c, 2 + jr GOT_KEY +GOT_COL3: + ld c, 3 + jr GOT_KEY +GOT_COL4: + ld c, 4 + +GOT_KEY: + ; indice de tabla = fila*5 + columna - 1 + ; (la fila 0 solo aporta 4 teclas: columnas 1-4, de ahi el -1) + ld a, d + add a, a + add a, a + add a, d ; A = fila*5 + add a, c ; A = fila*5 + columna + dec a ; A = indice (0-38) + jr OTHER_FOUND + +NEXT_ROW: + rlc b + inc d + ld a, d + cp 8 + jr nz, ROW_LOOP + +OTHER_NONE: + xor a + ret ; carry inactivo (xor siempre lo limpia) + +OTHER_FOUND: + scf ; carry activo; A ya tiene el indice (GOT_KEY) + ret + + ENDP + +; --------------------------------------------------------------------------- +; __ZX81SD_SYMBOL_FOR — dado un caracter ASCII (el que devolveria una +; pulsacion normal sin modificadores, es decir uno de los que aparecen en +; __ZX81SD_UNSHIFT_TABLE), devuelve el simbolo del ZX81 que le +; corresponde bajo el antiguo modificador SHIFT del ZX81 (o 0 si esa +; tecla no tiene simbolo). La usa stdlib/input.bas para componer simbolos +; como una "tecla muerta": el usuario pulsa "." y LUEGO esta tecla, sin +; necesitar mantenerlas pulsadas a la vez (ver cabecera del fichero). +; +; Entrada: A = caracter ASCII de la segunda tecla +; Salida: A = simbolo del ZX81, o 0 si esa tecla no tiene simbolo +; Registros modificados: AF, BC, DE, HL +; --------------------------------------------------------------------------- +__ZX81SD_SYMBOL_FOR: + PROC + LOCAL SEARCH_LOOP + LOCAL FOUND + LOCAL NOT_FOUND + + ld c, a ; C = caracter buscado + ld hl, __ZX81SD_UNSHIFT_TABLE + ld de, __ZX81SD_SYMBOL_TABLE + ld b, 39 ; numero de entradas de ambas tablas + +SEARCH_LOOP: + ld a, (hl) + cp c + jr z, FOUND + inc hl + inc de + djnz SEARCH_LOOP + jr NOT_FOUND + +FOUND: + ld a, (de) + or a + ret + +NOT_FOUND: + xor a + ret + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/io/sd81_mcu.asm b/src/lib/arch/zx81sd/runtime/io/sd81_mcu.asm new file mode 100644 index 000000000..1e473e009 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/io/sd81_mcu.asm @@ -0,0 +1,274 @@ +; sd81_mcu.asm (zx81sd) — Primitivas del protocolo del MCU del SD81 Booster +; para uso desde el RUNTIME en ensamblador (load.asm / save.asm). +; +; (La stdlib mcu.bas lleva sus propias copias de estas rutinas dentro de +; SUBs fastcall; aqui viven las de los statements LOAD/SAVE del compilador.) +; +; Protocolo: puerto $A7 = datos, puerto $AF = reloj (bit 7). El MCU +; invierte el bit de reloj cuando procesa cada lectura/escritura de $A7; +; tras cada operacion hay que esperar ese cambio. NUNCA escribir en $AF +; (provoca un reset del MCU). + + push namespace core + +SD81_MCU_DATA EQU $A7 +SD81_MCU_CLOCK EQU $AF + +; --------------------------------------------------------------------------- +; SD81_MCU_SEND — Envia el byte A al MCU y espera a que lo procese. +; Preserva BC, DE, HL. Modifica AF. +; --------------------------------------------------------------------------- +SD81_MCU_SEND: + PROC + LOCAL WAIT + push bc + ld b, a ; salva el dato + in a, (SD81_MCU_CLOCK) + ld c, a ; C = reloj previo + ld a, b + out (SD81_MCU_DATA), a +WAIT: + in a, (SD81_MCU_CLOCK) + xor c + jp p, WAIT + pop bc + ret + ENDP + +; --------------------------------------------------------------------------- +; SD81_MCU_RECV — Lee un byte del MCU en A y espera el toggle posterior. +; Preserva BC, DE, HL. Modifica AF. +; --------------------------------------------------------------------------- +SD81_MCU_RECV: + PROC + LOCAL WAIT + push bc + in a, (SD81_MCU_CLOCK) + ld c, a ; reloj previo + in a, (SD81_MCU_DATA) ; lee el dato (dispara el toggle del MCU) + ld b, a +WAIT: + in a, (SD81_MCU_CLOCK) + xor c + jp p, WAIT + ld a, b + pop bc + ret + ENDP + +; --------------------------------------------------------------------------- +; SD81_MCU_SEND_BLOCK — Envia BC bytes desde (HL) al MCU. +; Modifica AF, BC, E, HL. Devuelve HL apuntando tras el bloque. +; --------------------------------------------------------------------------- +SD81_MCU_SEND_BLOCK: + PROC + LOCAL LOOP, WAIT + ld a, b + or c + ret z +LOOP: + in a, (SD81_MCU_CLOCK) + ld e, a ; reloj previo + ld a, (hl) + out (SD81_MCU_DATA), a +WAIT: + in a, (SD81_MCU_CLOCK) + xor e + jp p, WAIT + inc hl + dec bc + ld a, b + or c + jr nz, LOOP + ret + ENDP + +; --------------------------------------------------------------------------- +; SD81_MCU_RECV_BLOCK — Recibe BC bytes del MCU en (HL). +; Modifica AF, BC, E, HL. +; --------------------------------------------------------------------------- +SD81_MCU_RECV_BLOCK: + PROC + LOCAL LOOP, WAIT + ld a, b + or c + ret z +LOOP: + in a, (SD81_MCU_CLOCK) + ld e, a + in a, (SD81_MCU_DATA) + ld (hl), a +WAIT: + in a, (SD81_MCU_CLOCK) + xor e + jp p, WAIT + inc hl + dec bc + ld a, b + or c + jr nz, LOOP + ret + ENDP + +; --------------------------------------------------------------------------- +; SD81_MCU_RECV_VERIFY — Recibe BC bytes y los compara con (HL). +; Devuelve A = 0 si todo coincide, A = 1 si hubo diferencias. +; (Consume siempre los BC bytes para no desincronizar el protocolo.) +; Modifica AF, BC, DE, HL. +; --------------------------------------------------------------------------- +SD81_MCU_RECV_VERIFY: + PROC + LOCAL LOOP, WAIT, SAME + ld d, 0 ; flag de diferencias + ld a, b + or c + jr z, SAME +LOOP: + in a, (SD81_MCU_CLOCK) + ld e, a + in a, (SD81_MCU_DATA) + cp (hl) + jr z, WAIT + ld d, 1 ; difiere +WAIT: + in a, (SD81_MCU_CLOCK) + xor e + jp p, WAIT + inc hl + dec bc + ld a, b + or c + jr nz, LOOP +SAME: + ld a, d + ret + ENDP + +; --------------------------------------------------------------------------- +; SD81_MCU_RECV_SINK — Recibe y descarta BC bytes (mantiene el protocolo +; sincronizado cuando el fichero es mayor que el buffer del llamador). +; Modifica AF, BC, E. +; --------------------------------------------------------------------------- +SD81_MCU_RECV_SINK: + PROC + LOCAL LOOP, WAIT + ld a, b + or c + ret z +LOOP: + in a, (SD81_MCU_CLOCK) + ld e, a + in a, (SD81_MCU_DATA) +WAIT: + in a, (SD81_MCU_CLOCK) + xor e + jp p, WAIT + dec bc + ld a, b + or c + jr nz, LOOP + ret + ENDP + +; --------------------------------------------------------------------------- +; SD81_ASC2ZX — Convierte el caracter ASCII en A a codigo de caracter +; ZX81 (los nombres de fichero de los comandos del MCU viajan en ZX81). +; Sin equivalente -> '?' ($0F). Preserva BC, DE, HL. +; --------------------------------------------------------------------------- +SD81_ASC2ZX: + PROC + LOCAL NOT_LOWER, NOT_UPPER, NOT_DIGIT, TABLE, TLOOP, TDONE + cp 'a' + jr c, NOT_LOWER + cp 'z' + 1 + jr nc, NOT_LOWER + sub 32 ; a mayuscula +NOT_LOWER: + cp 'A' + jr c, NOT_UPPER + cp 'Z' + 1 + jr nc, NOT_UPPER + sub 'A' - 38 ; letras: codigos 38-63 + ret +NOT_UPPER: + cp '0' + jr c, NOT_DIGIT + cp '9' + 1 + jr nc, NOT_DIGIT + sub '0' - 28 ; digitos: codigos 28-37 + ret +NOT_DIGIT: + push hl + push bc + ld hl, TABLE + ld b, (TDONE - TABLE) / 2 +TLOOP: + cp (hl) + inc hl + jr z, TDONE ; encontrado: (HL) = codigo ZX81 + inc hl + djnz TLOOP + ld a, $0F ; sin equivalente: '?' + pop bc + pop hl + ret +TDONE: + ld a, (hl) + pop bc + pop hl + ret + +TABLE: ; pares ASCII, codigo ZX81 + defb ' ', $00 + defb '.', $1B + defb '/', $18 + defb '-', $16 + defb '*', $17 + defb '<', $13 + defb '>', $12 + defb '(', $10 + defb ')', $11 + defb '$', $0D + defb ':', $0E + defb ',', $1A + defb ';', $19 + defb '+', $15 + defb '=', $14 + defb '"', $0B + ENDP + +; --------------------------------------------------------------------------- +; SD81_MCU_SEND_NAME — Envia un string de zxbasic (prefijo de longitud de +; 16 bits + datos) como cadena Pascal para el MCU: byte de longitud +; (recortada a 255) + caracteres convertidos a codigos ZX81. +; Entrada: HL = puntero al string (al prefijo de longitud). HL != 0. +; Modifica AF, BC, HL. +; --------------------------------------------------------------------------- +SD81_MCU_SEND_NAME: + PROC + LOCAL LOOP, LENOK + ld c, (hl) + inc hl + ld b, (hl) + inc hl ; BC = longitud, HL -> caracteres + ld a, b + or a + jr z, LENOK + ld c, 255 ; recorta a 255 (limite del protocolo) +LENOK: + ld a, c + call SD81_MCU_SEND ; longitud + ld a, c + or a + ret z + ld b, c +LOOP: + ld a, (hl) + call SD81_ASC2ZX + call SD81_MCU_SEND + inc hl + djnz LOOP + ret + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/io/sound/beep.asm b/src/lib/arch/zx81sd/runtime/io/sound/beep.asm new file mode 100644 index 000000000..f2562fcee --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/io/sound/beep.asm @@ -0,0 +1,208 @@ +; beep.asm (zx81sd) — Comando BEEP duracion, tono (version con expresiones) +; +; Sustituye a zx48k/runtime/io/sound/beep.asm, que llama a BEEP ($03F8) de +; la ROM Spectrum. Aqui se porta la rutina original usando el calculador FP +; propio (fp_calc.asm) y el bucle de beeper propio (io/sound/beeper.asm). +; +; BEEP dur, pitch +; dur = duracion en segundos (0 a 10) +; pitch = semitonos sobre/bajo el DO central (-60 a 127) +; +; Diferencias con la ROM: +; - MEM-0 esta en FP_MEM_AREA (sysvars propias), no en $5C92. +; - FIND-INT1/FIND-INT2 se sustituyen por __FPSTACK_POP + __FTOU32REG +; (ya portados y verificados en zx81sd). +; - La constante 437500 (= 3.5MHz/8 del Spectrum) se sustituye por +; 406250 (= 3.25MHz/8 del ZX81). El resto del bytecode es identico. +; +; Entrada (convencion del compilador, igual que en zx48k): +; Duracion en A,E,D,C,B (float); tono en la pila de maquina (float). + +#include once +#include once +#include once +#include once +#include once + + push namespace core + +BEEP: + PROC + LOCAL BEEP_I_OK, BEEP_OCTAVE, BEEP_ERROR, BEEP_SEMITONES + + call __FPSTACK_PUSH ; duracion -> pila FP + + pop hl ; direccion de retorno + pop af + pop de + pop bc ; tono (float) desde la pila de maquina + push hl ; CALLEE + + call __FPSTACK_PUSH ; tono -> pila FP + +; Igual que la ROM ($03F8): separa el tono en parte entera (mem-0) y +; fraccionaria, y deja en la pila 1 + 0.05776226 * frac(tono). + + rst 28h ;; FP-CALC dur, tono. + defb $31 ;;duplicate dur, tono, tono. + defb $27 ;;int dur, tono, int(tono). + defb $C0 ;;st-mem-0 (tono entero a mem-0) + defb $03 ;;subtract dur, frac(tono). + defb $34 ;;stk-data constante 0.05776226 + defb $EC ;;Exponent: $7C, Bytes: 4 + defb $6C, $98, $1F, $F5 + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $0F ;;addition dur, 1 + 0.0577*frac. + defb $38 ;;end-calc + +; mem-0 contiene el tono entero en formato entero-pequeno: +; 0, signo (0/FF), LSB, MSB, 0. Comprueba -128 <= tono <= 127. + + ld hl, FP_MEM_AREA + ld a, (hl) ; el primer byte debe ser 0 (forma entera) + and a + jp nz, BEEP_ERROR + + inc hl + ld c, (hl) ; C = byte de signo (0/FF) + inc hl + ld b, (hl) ; B = LSB (complemento a dos) + ld a, b + rla + sbc a, a ; A = 0/FF segun el bit 7 de B + cp c ; debe coincidir con el signo + jp nz, BEEP_ERROR + + inc hl + cp (hl) ; y el MSB debe ser 0/FF igualmente + jp nz, BEEP_ERROR + + ld a, b ; A = tono + 60 + add a, $3C + jp p, BEEP_I_OK ; si -60 <= tono, sigue + + jp po, BEEP_ERROR ; fuera de rango por abajo + +BEEP_I_OK: ; aqui -60 <= tono <= 127, A = tono+60 (0-187) + ld b, $FA ; B = -6 octavas bajo el DO central + +BEEP_OCTAVE: + inc b ; octava siguiente + sub $0C ; 12 semitonos = 1 octava + jr nc, BEEP_OCTAVE + + add a, $0C ; A = semitonos sobre DO (0-11) + push bc ; B = desplazamiento de octava (-5..10) + + ; HL = BEEP_SEMITONES + A*5 (LOC-MEM de la ROM) + ld c, a + add a, a + add a, a + add a, c ; A = A*5 (max 55, sin acarreo) + ld c, a + ld b, 0 + ld hl, BEEP_SEMITONES + add hl, bc + + ; STACK-NUM: apila el float de la tabla (frecuencia del semitono) + ld a, (hl) + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + call __FPSTACK_PUSH + + rst 28h ;; FP-CALC dur, factor, freq. + defb $04 ;;multiply dur, freq ajustada a frac(tono). + defb $38 ;;end-calc (HL -> exponente del resultado) + + pop af ; A = desplazamiento de octava + add a, (hl) ; freq *= 2^octava (suma al exponente) + ld (hl), a + + rst 28h ;; FP-CALC dur, freq. + defb $C0 ;;st-mem-0 (frecuencia a mem-0) + defb $02 ;;delete dur. + defb $31 ;;duplicate dur, dur. + defb $38 ;;end-calc + + ; comprueba 0 <= duracion <= 10 (como FIND-INT1 + CP 11 de la ROM) + call __FPSTACK_POP + bit 7, e ; negativa -> error + jp nz, BEEP_ERROR + call __FTOU32REG ; DEHL = int(duracion) + ld a, d + or e + or h + jp nz, BEEP_ERROR + ld a, l + cp $0B + jp nc, BEEP_ERROR + +; Calcula los parametros del bucle del beeper: +; ciclos = duracion * frecuencia +; periodo = 406250 / frecuencia - 30.125 (406250 = 3.25MHz / 8) + + rst 28h ;; FP-CALC dur. + defb $E0 ;;get-mem-0 dur, freq. + defb $04 ;;multiply ciclos. + defb $E0 ;;get-mem-0 ciclos, freq. + defb $34 ;;stk-data constante 406250 + defb $80 ;;Exponent: $93, Bytes: 3 + defb $43 + defb $46, $5D, $40 + defb $01 ;;exchange ciclos, 406250, freq. + defb $05 ;;division ciclos, 406250/freq. + defb $34 ;;stk-data constante 30.125 + defb $35 ;;Exponent: $85, Bytes: 1 + defb $71 + defb $03 ;;subtract ciclos, periodo. + defb $38 ;;end-calc + + call __FPSTACK_POP + call __FTOU32REG ; HL = periodo + push hl + call __FPSTACK_POP + call __FTOU32REG ; HL = ciclos + ex de, hl ; DE = ciclos + pop hl ; HL = periodo + + ld a, d + or e + ret z ; duracion 0: nada que hacer (evita 65536 ciclos) + dec de ; DE = ciclos - 1 + + push ix ; el beeper usa IX (frame pointer del compilador) + call __ZX81SD_BEEPER + pop ix + ret + +BEEP_ERROR: + ld a, ERROR_IntOutOfRange + jp __ERROR + +; Tabla de semitonos de la ROM ($046E): frecuencias de la octava central. +; Octavas arriba/abajo = multiplicar por 2^n (se suma n al exponente). + +BEEP_SEMITONES: + defb $89, $02, $D0, $12, $86 ; 261.625565290 DO + defb $89, $0A, $97, $60, $75 ; 277.182631135 DO# + defb $89, $12, $D5, $17, $1F ; 293.664768100 RE + defb $89, $1B, $90, $41, $02 ; 311.126983881 RE# + defb $89, $24, $D0, $53, $CA ; 329.627557039 MI + defb $89, $2E, $9D, $36, $B1 ; 349.228231549 FA + defb $89, $38, $FF, $49, $3E ; 369.994422674 FA# + defb $89, $43, $FF, $6A, $73 ; 391.995436072 SOL + defb $89, $4F, $A7, $00, $54 ; 415.304697513 SOL# + defb $89, $5C, $00, $00, $00 ; 440.000000000 LA + defb $89, $69, $14, $F6, $24 ; 466.163761616 LA# + defb $89, $76, $F1, $10, $05 ; 493.883301378 SI + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/io/sound/beeper.asm b/src/lib/arch/zx81sd/runtime/io/sound/beeper.asm new file mode 100644 index 000000000..1f99f7c98 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/io/sound/beeper.asm @@ -0,0 +1,142 @@ +; beeper.asm (zx81sd) — Generador de onda cuadrada por el beeper del SD81 +; +; Sustituye a zx48k/runtime/io/sound/beeper.asm, que llama a BEEPER ($03B5) +; de la ROM Spectrum. Aqui se porta el bucle original de la ROM adaptando: +; +; - Puerto: $FB (ULA emulada del SD81 en modo HiRes Spectrum) en vez de +; $FE. Mismo formato: bits 2-0 = borde, bit 3 = MIC, bit 4 = altavoz. +; El borde se toma de la copia sombra __ZX81SD_ULA_SHADOW (beep.asm) +; en vez de BORDCR ($5C48), y se restaura al terminar. +; - Reloj: el bucle es identico en T-states (no depende del reloj), pero +; el periodo HL que llega del COMPILADOR (BEEP const,const se precalcula +; en el frontend con la formula del Spectrum: HL = 437500/f - 30.125, +; 437500 = 3.5MHz/8) hay que corregirlo para los 3.25 MHz del ZX81: +; HL' = 406250/f - 30.125 = HL*13/14 - 2.15 (406250/437500 = 13/14) +; La entrada __BEEPER (la que usa el compilador) aplica esa correccion. +; La entrada __ZX81SD_BEEPER (interna) recibe el periodo ya en unidades +; de 3.25 MHz y no corrige nada (la usa nuestro BEEP de beep.asm, que +; calcula directamente con 406250). +; - Interrupciones: la ROM hace DI...EI. El runtime zx81sd se ejecuta con +; las interrupciones deshabilitadas (la FPGA genera el video), asi que +; se mantiene el DI (inocuo) y NO se hace EI al final. +; +; __ZX81SD_BEEPER — entrada directa (formato ROM $03B5): +; DE = numero de ciclos - 1 +; HL = periodo del tono: T-states = 236 + 8*HL (a 3.25 MHz) +; Modifica: AF, BC, DE, HL, IX (el llamador debe preservar IX si lo usa) + +#include once ; __ZX81SD_ULA_SHADOW (sombra borde/beeper) + + push namespace core + +__ZX81SD_BEEPER: + PROC + LOCAL BE_IX3, BE_IX2, BE_IX1, BE_IX0 + LOCAL BE_HL_LP, BE_AGAIN, BE_END + + di ; timing exacto (ya suelen estar deshabilitadas) + ld a, l + srl l + srl l ; L = parte media del periodo + cpl + and $03 ; A = 3 - parte fina del periodo + ld c, a + ld b, $00 + ld ix, BE_IX3 + add ix, bc ; IX = entrada al bucle con 0-3 NOPs (parte fina) + + ld a, (__ZX81SD_ULA_SHADOW) + and $07 ; bits 2-0 = borde actual + or $08 ; bit 3 (MIC) a 1, como la ROM + +BE_IX3: + nop ;(4) NOPs opcionales: ajuste fino del periodo +BE_IX2: + nop ;(4) +BE_IX1: + nop ;(4) +BE_IX0: + inc b ;(4) + inc c ;(4) + +BE_HL_LP: + dec c ;(4) bucle de duracion del semiciclo + jr nz, BE_HL_LP ;(12/7) + + ld c, $3F ;(7) + dec b ;(4) + jp nz, BE_HL_LP ;(10) + + xor $10 ;(7) conmuta el bit del altavoz + out (SD81_ULA_PORT), a ;(11) + ld b, h ;(4) B = parte gruesa del periodo + ld c, a ;(4) salva el byte del puerto + bit 4, a ;(8) si la salida quedo alta, + jr nz, BE_AGAIN ;(12/7) hace el semiciclo alto + + ld a, d ;(4) ciclo completo (bajo->bajo): + or e ;(4) ¿quedan ciclos? + jr z, BE_END ;(12/7) + + ld a, c ;(4) restaura el byte del puerto + ld c, l ;(4) C = parte media del periodo + dec de ;(6) + jp (ix) ;(8) siguiente ciclo + +BE_AGAIN: ; a mitad de ciclo + ld c, l ;(4) + inc c ;(4) +16 T para igualar semiciclo alto y bajo + jp (ix) ;(8) + +BE_END: + ld a, (__ZX81SD_ULA_SHADOW) + out (SD81_ULA_PORT), a ; deja borde/beeper como estaban (sin EI: ver cabecera) + ret + + ENDP + +; --------------------------------------------------------------------------- +; __BEEPER — Entrada del compilador para BEEP , +; HL (fastcall) = numero de ciclos - 1 +; (SP+2) en la pila = periodo calculado por el frontend PARA 3.5 MHz +; Corrige el periodo a 3.25 MHz (HL' = HL - HL/14 - 2) y llama al bucle. +; --------------------------------------------------------------------------- +__BEEPER: + PROC + LOCAL DIV14, DIV14_SKIP + + ex de, hl ; DE = ciclos - 1 + pop hl ; direccion de retorno + ex (sp), hl ; HL = periodo (unidades Spectrum) — CALLEE + + push de ; salva ciclos + push hl ; salva periodo original + ex de, hl ; DE = dividendo (periodo) + xor a ; A = resto + ld c, 14 + ld b, 16 +DIV14: + sla e ; desplaza el dividendo (bit 0 entra a 0; + rl d ; NO usar rl e: arrastraria el acarreo residual + rla ; del cp/sub de la vuelta anterior) + cp c + jr c, DIV14_SKIP + sub c + inc e +DIV14_SKIP: + djnz DIV14 ; DE = periodo / 14 + pop hl ; periodo original + or a + sbc hl, de ; HL = periodo - periodo/14 (= periodo*13/14) + dec hl + dec hl ; -2 (~ -30.125*(1-13/14), redondeado) + pop de ; ciclos - 1 + + push ix ; el bucle usa IX; el compilador usa IX como frame + call __ZX81SD_BEEPER + pop ix + ret + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/load.asm b/src/lib/arch/zx81sd/runtime/load.asm new file mode 100644 index 000000000..21f858551 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/load.asm @@ -0,0 +1,160 @@ +; load.asm (zx81sd) — LOAD/VERIFY ... CODE desde la tarjeta SD +; +; Sustituye a zx48k/runtime/load.asm (que usa LD-BYTES de cinta, puerto +; $FE y cabeceras de cassette del Spectrum). Aqui el statement LOAD de +; zxbasic carga el fichero desde la SD del SD81 Booster con el comando 9 +; del MCU (ver io/sd81_mcu.asm para el protocolo). +; +; Interfaz del compilador (identica a zx48k): +; pila: A = 1 LOAD / 0 VERIFY; BC = longitud; DE = direccion destino; +; HL = string de zxbasic con el nombre (hay que liberarlo). +; +; Semantica: +; - BC = 0: carga el fichero completo en DE. +; - BC > 0: carga como maximo BC bytes en DE; si el fichero es mayor +; se descarta el resto (el protocolo obliga a consumirlo); si es +; menor se marca error de carga. +; - VERIFY: compara en vez de escribir; diferencia = error de carga. +; - Error (fichero no existe, etc.): ERR_NR = ERROR_TapeLoadingErr y +; se retorna (mismo comportamiento "blando" que la version de cinta). +; - Ojo con extensiones especiales del MCU: un .ROM resetea la CPU +; (no retorna) y un .WAV solo se reproduce (carga 0 bytes). + +#include once +#include once +#include once + + push namespace core + +LOAD_CODE: + PROC + LOCAL LC_FLAG, LC_REQ, LC_DEST, LC_SIZE, LC_N + LOCAL LC_HAVE_SIZE, LC_USE_REQ, LC_DO_RECV, LC_DRAIN + LOCAL LC_ERROR, LC_END, LC_ERR_NOFREE + LOCAL LC_VERIFY + + pop hl ; direccion de retorno + pop af ; A = 1 LOAD / 0 VERIFY + pop bc ; longitud solicitada + pop de ; direccion destino + ex (sp), hl ; CALLEE: HL = string con el nombre + + ld (LC_FLAG), a + ld (LC_REQ), bc + ld (LC_DEST), de + + ld a, h + or l + jp z, LC_ERR_NOFREE ; string nulo + + push hl ; salva el puntero para MEM_FREE + + ; longitud del nombre 0 -> error (tras liberar) + ld e, (hl) + inc hl + ld d, (hl) + dec hl + ld a, d + or e + jr nz, LC_HAVE_SIZE + + pop hl + call MEM_FREE + jp LC_ERR_NOFREE + +LC_HAVE_SIZE: + ; comando 9 (LOAD) + nombre + ld a, 9 + call SD81_MCU_SEND + call SD81_MCU_SEND_NAME + pop hl + call MEM_FREE ; el nombre ya viajo: libera el string + + ; tamano real del fichero (little-endian) + call SD81_MCU_RECV + ld l, a + call SD81_MCU_RECV + ld h, a + ld (LC_SIZE), hl + + ; n = (LC_REQ = 0) ? size : min(LC_REQ, size) + ld bc, (LC_REQ) + ld a, b + or c + jr z, LC_DO_RECV ; BC=0: n = size (ya en HL) + or a + sbc hl, bc ; size - req + jr nc, LC_USE_REQ ; size >= req: n = req + ld hl, (LC_SIZE) ; size < req: n = size (y sera error corto) + jr LC_DO_RECV +LC_USE_REQ: + ld hl, (LC_REQ) + +LC_DO_RECV: + ld (LC_N), hl + ld b, h + ld c, l ; BC = n + ld hl, (LC_DEST) + ld a, (LC_FLAG) + or a + jr z, LC_VERIFY + + call SD81_MCU_RECV_BLOCK + xor a ; sin diferencias + jr LC_DRAIN + +LC_VERIFY: + call SD81_MCU_RECV_VERIFY ; A = 1 si hubo diferencias + +LC_DRAIN: + push af ; salva el resultado de la verificacion + ; descarta lo que sobre: size - n + ld hl, (LC_SIZE) + ld bc, (LC_N) + or a + sbc hl, bc + ld b, h + ld c, l + call SD81_MCU_RECV_SINK + + call SD81_MCU_RECV ; byte de estado del MCU + ld e, a + pop af + or e ; error si status != 0 o verificacion fallida + jp nz, LC_ERROR + + ; carga corta: se pidieron LC_REQ (>0) bytes y el fichero era menor + ld bc, (LC_REQ) + ld a, b + or c + jr z, LC_END + ld hl, (LC_N) + or a + sbc hl, bc + jr z, LC_END ; n = req: completo + +LC_ERROR: + ld a, ERROR_TapeLoadingErr + ld (ERR_NR), a +LC_END: + ret + +LC_ERR_NOFREE: + ld a, ERROR_InvalidFileName + ld (ERR_NR), a + ret + +LC_FLAG: + defb 0 +LC_REQ: + defw 0 +LC_DEST: + defw 0 +LC_SIZE: + defw 0 +LC_N: + defw 0 + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/paging.asm b/src/lib/arch/zx81sd/runtime/paging.asm new file mode 100644 index 000000000..5433ff188 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/paging.asm @@ -0,0 +1,54 @@ +; PAGING — Rutinas de paginación de memoria del SD81 Booster +; Puerto $E7h: mapeador de memoria en bloques de 8KB +; +; Modo simple (hasta 256KB, 32 páginas): +; OUT ($E7h), A con A = (página << 3) | bloque +; +; Modo completo (hasta 512KB, 64 páginas): +; OUT (C), A con A = bloque (0-7), B = página (0-63), C = $E7h +; +; Los bloques 0-3 ($0000-$7FFF) son ejecutables. +; Los bloques 4-7 ($8000-$FFFF) son solo datos (sin MC45). +; Los bloques 6-7 deben ser consistentes con el HFILE en modo normal del ZX81; +; en modo Superfast HiRes Spectrum la FPGA ignora esa restricción. + + push namespace core + +SD81_PAGE_PORT EQU $E7 ; Puerto del mapeador de memoria + +; PAGE_SET_SIMPLE — Asigna página a bloque (modo simple, hasta 32 páginas) +; Entrada: A = bloque (0-7), B = página (0-31) +; Destruye: A, C +PAGE_SET_SIMPLE: + ld c, a ; C = bloque + ld a, b + sla a + sla a + sla a ; A = página << 3 + or c ; A = (página << 3) | bloque + out (SD81_PAGE_PORT), a + ret + +; PAGE_SET_FULL — Asigna página a bloque (modo completo, hasta 64 páginas) +; Entrada: A = bloque (0-7), B = página (0-63) +; Destruye: C +PAGE_SET_FULL: + ld c, SD81_PAGE_PORT + out (c), a ; A = bloque, B = página + ret + +; PAGE_SET_SCREEN — Mapea la página de pantalla al bloque 6 ($C000) +; Entrada: B = número de página RAM dedicada a la pantalla +; Destruye: A, C +PAGE_SET_SCREEN: + ld a, 6 ; bloque 6 = $C000-$DFFF + jp PAGE_SET_FULL + +; PAGE_SET_DATA — Mapea una página de datos al bloque 7 ($E000) +; Entrada: B = número de página +; Destruye: A, C +PAGE_SET_DATA: + ld a, 7 ; bloque 7 = $E000-$FFFF + jp PAGE_SET_FULL + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/pause.asm b/src/lib/arch/zx81sd/runtime/pause.asm new file mode 100644 index 000000000..610268c32 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/pause.asm @@ -0,0 +1,29 @@ +; ----------------------------------------------------------------------- +; PAUSE — Espera N frames usando VSYNC del SD81 Booster +; Sustituye a CALL $1F3Dh (PAUSE_1 de la ROM Spectrum) que usa HALT. +; +; Entrada: HL = número de frames a esperar +; ----------------------------------------------------------------------- + +#include once + + push namespace core + +__PAUSE: + PROC + LOCAL PAUSE_LOOP + + ld b, h + ld c, l ; BC = contador de frames + +PAUSE_LOOP: + ld a, b + or c + ret z ; BC=0 → fin + call VSYNC_TICK ; espera un frame e incrementa FRAMES + dec bc + jr PAUSE_LOOP + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/pixel_addr.asm b/src/lib/arch/zx81sd/runtime/pixel_addr.asm new file mode 100644 index 000000000..93a0156f8 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/pixel_addr.asm @@ -0,0 +1,62 @@ +; PIXEL_ADDR — Calcula la dirección (offset) del byte de pantalla para (B=Y, C=X) +; Sustituye a PIXEL_ADDR EQU $22ACh (ROM Spectrum) +; +; Interfaz idéntica a la ROM Spectrum para compatibilidad con plot.asm / draw.asm: +; Entrada: A = 191 (límite superior Y), B = Y (0=abajo, 191=arriba), C = X (0-255) +; Salida: HL = offset dentro del bitmap desde $0000 (sin base de pantalla) +; A = X AND 7 (posición del bit, 0=izquierda/bit7, 7=derecha/bit0) +; Destruye: B (como la ROM). DEBE preservar D y E: draw.asm salva B' en D' +; alrededor de esta llamada (ld d,b / call PIXEL_ADDR / ld b,d), igual que +; hacía con PIXEL-ADD ($22AC) de la ROM Spectrum, que tampoco tocaba DE. +; +; El llamador (plot.asm, draw.asm) añade la base de pantalla: +; res 6, h ; no-op en nuestro caso (H siempre en $00-$17) +; add hl, (SCREEN_ADDR) +; +; Organización bitmap Spectrum interleaved: +; V = 191 - Y (Y desde abajo → V desde arriba) +; H = (V AND $C0)>>3 | (V AND $07) → tercio + línea en tercio +; L = (V AND $38)<<2 | (C>>3) → fila de char*32 + columna byte + + push namespace core + +PIXEL_ADDR: + PROC + + sub b ; A = 191 - Y (convierte coord Spectrum a offset desde arriba) + ld b, a ; B = V = 191-Y (B ya no se necesita: era la Y de entrada) + + ; -- H: tercio (bits 12-11) + línea en tercio (bits 10-8) -- + and $C0 ; A = (V AND $C0) = tercio * 64 + rrca + rrca + rrca ; A = tercio * 8 + ld h, a + ld a, b + and $07 ; A = línea en tercio (0-7) + or h + ld h, a ; H = (tercio<<3) | línea_en_tercio + + ; -- L: fila de carácter (bits 7-5) + columna de byte (bits 4-0) -- + ld a, b + and $38 ; A = fila_de_char * 8 + rlca + rlca ; A = fila_de_char * 32 (tras AND $38 no hay acarreo al rotar) + ld b, a + ld a, c + rrca + rrca + rrca + and $1F ; A = X / 8 (columna de byte, 0-31) + add a, b + ld l, a ; L = fila_de_char*32 + col_byte + + ; -- Retornar A = X AND 7 (posición del bit dentro del byte) -- + ld a, c + and $07 ; A = X AND 7 + + ret + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/plot.asm b/src/lib/arch/zx81sd/runtime/plot.asm new file mode 100644 index 000000000..f32e3581c --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/plot.asm @@ -0,0 +1,90 @@ +; MIXED __FASTCALL__ / __CALLEE__ PLOT Function — ZX81 + SD81 Booster +; Adaptado de zx48k/runtime/plot.asm para sustituir las llamadas ROM +; por implementaciones propias (sin ROM Spectrum disponible). +; +; Cambios respecto al original zx48k: +; - PIXEL_ADDR: llama a nuestra implementación en pixel_addr.asm +; - COORDS: usa el valor de sysvars.asm ($8000+), no $5C7D +; - PLOT_SUB: eliminado (el código propio no lo llama) + +; Y in A (accumulator) +; X in top of the stack + +#include once +#include once +#include once +#include once +#include once + + push namespace core + +PLOT: + PROC + + LOCAL __PLOT_ERR + LOCAL __PLOT_OVER1 + + pop hl + ex (sp), hl ; Callee + + ld b, a + ld c, h + +#ifdef SCREEN_Y_OFFSET + ld a, SCREEN_Y_OFFSET + add a, b + ld b, a +#endif + +#ifdef SCREEN_X_OFFSET + ld a, SCREEN_X_OFFSET + add a, c + ld c, a +#endif + + ld a, 191 + cp b + jr c, __PLOT_ERR ; jr is faster here (#1) + +__PLOT: ; __FASTCALL__ entry (b, c) = pixel coords (y, x) + ld (COORDS), bc ; Saves current point + ld a, 191 ; Max y coord + call PIXEL_ADDR + res 6, h ; no-op en SD81 (H siempre en $00-$17), mantiene compatibilidad + ld bc, (SCREEN_ADDR) + add hl, bc ; Now current offset + + ld b, a + inc b + ld a, 0FEh +LOCAL __PLOT_LOOP +__PLOT_LOOP: + rrca + djnz __PLOT_LOOP + + ld b, a + ld a, (P_FLAG) + ld c, a + ld a, (hl) + bit 0, c ; is it OVER 1 + jr nz, __PLOT_OVER1 + and b + +__PLOT_OVER1: + bit 2, c ; is it inverse 1 + jr nz, __PLOT_END + + xor b + cpl + +LOCAL __PLOT_END +__PLOT_END: + ld (hl), a + jp SET_PIXEL_ADDR_ATTR + +__PLOT_ERR: + jp __OUT_OF_SCREEN_ERR ; Spent 3 bytes, but saves 3 T-States at (#1) + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/po_gr_1.asm b/src/lib/arch/zx81sd/runtime/po_gr_1.asm new file mode 100644 index 000000000..c1435cdd5 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/po_gr_1.asm @@ -0,0 +1,57 @@ +; PO_GR_1 — Genera el patrón de bits para caracteres gráficos 128-143 +; Sustituye a PO-GR-1 ($0B38, ROM Spectrum). Mismo algoritmo que la ROM. +; +; Los caracteres gráficos Spectrum (CHR$ 128 a CHR$ 143) son bloques +; de 2×2 cuadrantes. El nibble bajo del código determina qué cuadrantes +; están encendidos, con el mapeo EXACTO de la ROM (PO-GR-2, $0B3E): +; +; bit 0 = cuadrante superior DERECHO → $0F en filas 0-3 +; bit 1 = cuadrante superior IZQUIERDO → $F0 en filas 0-3 +; bit 2 = cuadrante inferior DERECHO → $0F en filas 4-7 +; bit 3 = cuadrante inferior IZQUIERDO → $F0 en filas 4-7 +; +; (CHR$(129) = ▝, CHR$(130) = ▘, CHR$(143) = bloque macizo.) +; +; Entrada: B = código de carácter (128-143), solo se usan bits 3-0 +; Salida: MEM0 (8 bytes) = patrón del carácter; HL = MEM0 +; Destruye: A, B, C + +#include once + + push namespace core + +PO_GR_1: + PROC + LOCAL PO_GR_HALF, PO_GR_FILL + + ld a, b + and $0F + ld b, a ; B = bits de cuadrante + ld hl, MEM0 + call PO_GR_HALF ; filas 0-3 (bits 0-1) + call PO_GR_HALF ; filas 4-7 (bits 2-3) + ld hl, MEM0 ; devuelve el puntero al patrón + ret + +; Construye media celda (4 filas iguales) a partir de los dos bits +; bajos de B, desplazándolos fuera. Igual que PO-GR-2 de la ROM. +PO_GR_HALF: + rr b ; bit par → carry + sbc a, a ; $00 o $FF + and $0F ; mitad derecha + ld c, a + rr b ; bit impar → carry + sbc a, a + and $F0 ; mitad izquierda + or c ; byte de la fila completo + ld c, 4 +PO_GR_FILL: + ld (hl), a + inc hl + dec c + jr nz, PO_GR_FILL + ret + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/print.asm b/src/lib/arch/zx81sd/runtime/print.asm new file mode 100644 index 000000000..1bb7a0285 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/print.asm @@ -0,0 +1,588 @@ +; vim:ts=4:sw=4:et: +; PRINT command routine +; Does not print attribute. Use PRINT_STR or PRINT_NUM for that + +#include once +#include once +#include once +#include once +#include once +#include once +#include once +#include once +#include once +#include once +#include once +#include once +#include once + +; Putting a comment starting with @INIT
+; will make the compiler to add a CALL to
+; It is useful for initialization routines. +#init .core.__PRINT_INIT + + push namespace core + +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + + ;; Clears ATTR2 flags (OVER 2, etc) + xor a + ld (FLAGS2), a + ld hl, TV_FLAG + res 0, (hl) + + LOCAL SET_SCR_ADDR + call __LOAD_S_POSN + jp __SET_SCR_PTR + + ;; Receives HL = future value of S_POSN + ;; Stores it at (S_POSN) and refresh screen pointers (ATTR, SCR) +SET_SCR_ADDR: + ld (S_POSN), hl + ex de, hl + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + dec e + jp __SET_SCR_PTR + +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A + + LOCAL PO_GR_1 + + LOCAL __PRCHAR + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + +PRINT_JUMP_STATE EQU __PRINT_JUMP + 2 + +__PRINT_JUMP: + exx ; Switch to alternative registers + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively + +__PRINT_START: + +__PRINT_CHR: + cp ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones (> 127 bytes away) + ex af, af' ; Saves a value (char to print) for later + + ld hl, (S_POSN) + dec l + jr nz, 1f + ld l, SCR_COLS - 1 + dec h + jr nz, 2f + +#ifndef __ZXB_DISABLE_SCROLL + inc h + push hl + call __SCROLL_SCR + pop hl +#else + ld h, SCR_ROWS - 1 +#endif +2: + call SET_SCR_ADDR + jr 4f +1: + ld (S_POSN), hl +4: + ex af, af' + + cp 80h ; Is it a "normal" (printable) char + jr c, __SRCADDR + + cp 90h ; Is it an UDG? + jr nc, __PRINT_UDG + + ; Print an 8 bit pattern (80h to 8Fh) + + ld b, a + call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 + ld hl, MEM0 + jp __PRGRAPH + +; PO_GR_1 se incluye como rutina propia (po_gr_1.asm), no desde ROM Spectrum +#include once + +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jr __PRGRAPH0 + +__SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source +__SRCADDR: + ld bc, (CHARS) + +__PRGRAPH0: + add a, a ; A = a * 2 (since a < 80h) ; Thanks to Metalbrain at http://foro.speccy.org + ld l, a + ld h, 0 ; HL = a * 2 (accumulator) + add hl, hl + add hl, hl ; HL = a * 8 + add hl, bc ; HL = CHARS address + +__PRGRAPH: + ex de, hl ; HL = Write Address, DE = CHARS address + +#ifndef __ZXB_DISABLE_BOLD + ld a, (FLAGS2) + bit 2, a + call nz, __BOLD +#endif + +#ifndef __ZXB_DISABLE_ITALIC + ld a, (FLAGS2) + bit 4, a + call nz, __ITALIC +#endif + + ld hl, (DFCC) + push hl + + ld b, 8 ; 8 bytes per char + +__PRCHAR: + ld a, (de) ; DE *must* be source, and HL destiny + +PRINT_MODE: ; Which operation is used to write on the screen + ; Set it with: + ; LD A, + ; LD (PRINT_MODE), A + ; + ; Available operations: + ; NORMAL : 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; Set to one of the values above + +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + + ld (hl), a + + inc de + inc h ; Next line + djnz __PRCHAR + + pop hl + inc hl + ld (DFCC), hl + + ld hl, (DFCCL) ; current ATTR Pos + inc hl + ld (DFCCL), hl + dec hl + call __SET_ATTR + exx + ret + +; ------------- SPECIAL CHARS (< 32) ----------------- + +__PRINT_SPECIAL: ; Jumps here if it is a special char + ld hl, __PRINT_TABLE + jp JUMP_HL_PLUS_2A + +PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence + exx + +__PRINT_0Dh: ; Called WHEN printing CHR$(13) + ld hl, (S_POSN) + dec l + jr nz, 1f + dec h + jr nz, 1f +#ifndef __ZXB_DISABLE_SCROLL + inc h + push hl + call __SCROLL_SCR + pop hl +#else + ld h, SCR_ROWS - 1 +#endif +1: + ld l, 1 + +__PRINT_EOL_END: + call SET_SCR_ADDR + exx + ret + +__PRINT_COM: + exx + push hl + push de + push bc + call PRINT_COMMA + pop bc + pop de + pop hl + ret + +__PRINT_TAB: + ld hl, __PRINT_TAB1 + jr __PRINT_SET_STATE + +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + jr __PRINT_SET_STATE + +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + exx + push hl + push bc + push de + call PRINT_TAB + pop de + pop bc + pop hl + ret + +__PRINT_AT: + ld hl, __PRINT_AT1 + jr __PRINT_SET_STATE + +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START + +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret + +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + ld hl, (S_POSN) + ld h, a + ld a, SCR_ROWS + sub h + ld (S_POSN + 1), a + + ld hl, __PRINT_AT2 + jr __PRINT_SET_STATE + +__PRINT_AT2: + call __LOAD_S_POSN + ld e, a + call __SAVE_S_POSN + jp __PRINT_RESTART + +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jr nz, 3f + ld e, SCR_COLS - 2 + dec d + cp d + jr nz, 3f + ld d, SCR_ROWS - 1 +3: + call __SAVE_S_POSN + exx + ret + +__PRINT_INK: + ld hl, __PRINT_INK2 + jr __PRINT_SET_STATE + +__PRINT_INK2: + call INK_TMP + jp __PRINT_RESTART + +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE + +__PRINT_PAP2: + call PAPER_TMP + jp __PRINT_RESTART + +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE + +__PRINT_FLA2: + call FLASH_TMP + jp __PRINT_RESTART + +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE + +__PRINT_BRI2: + call BRIGHT_TMP + jp __PRINT_RESTART + +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE + +__PRINT_INV2: + call INVERSE_TMP + jp __PRINT_RESTART + +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE + +__PRINT_OVR2: + call OVER_TMP + jp __PRINT_RESTART + +#ifndef __ZXB_DISABLE_BOLD +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE + +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#endif + +#ifndef __ZXB_DISABLE_ITALIC +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE + +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#endif + +#ifndef __ZXB_DISABLE_BOLD + LOCAL __BOLD + +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +1: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz 1b + pop hl + ld de, MEM0 + ret +#endif + +#ifndef __ZXB_DISABLE_ITALIC + LOCAL __ITALIC + +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) + inc hl + srl (hl) + inc hl + srl (hl) + inc hl + inc hl + inc hl + sla (hl) + inc hl + sla (hl) + inc hl + sla (hl) + pop hl + ld de, MEM0 + ret +#endif + +#ifndef __ZXB_DISABLE_SCROLL + LOCAL __SCROLL_SCR + +; En zx81sd el scroll se hace SIEMPRE con la implementacion por buffer: +; la variante del zx48k ("__SCROLL_SCR EQU 0DFEh") delega en la rutina +; CL-SC-ALL de la ROM del Spectrum, que aqui no esta mapeada — ese CALL +; ejecutaria codigo arbitrario del programa (bug cazado con flights.bas: +; el primer PRINT que desbordaba la pantalla saltaba a la linea BASIC +; que casualmente ocupara $0DFE). +__SCROLL_SCR: ;; Scrolls screen and attrs 1 row up + ld de, (SCREEN_ADDR) + ld b, 3 +3: + push bc + ld a, 8 +1: + ld hl, 32 + add hl, de + ld bc, 32 * 7 + push de + ldir + pop de + inc d + dec a + jr nz, 1b + push hl + ld bc, -32 - 256 * 7 + add hl, bc + ex de, hl + ld a, 8 +2: + ld bc, 32 + push hl + push de + ldir + pop de + pop hl + inc d + inc h + dec a + jr nz, 2b + pop de + pop bc + djnz 3b + + dec de + ld h, d + ld l, e + ld a, 8 +3: + push hl + push de + ld (hl), b + dec de + ld bc, 31 + lddr + pop de + pop hl + dec d + dec h + dec a + jr nz, 3b + + ld de, (SCREEN_ATTR_ADDR) + ld hl, 32 + add hl, de + ld bc, 32 * 23 + ldir + + ld h, d + ld l, e + ld a, (ATTR_P) + ld (hl), a + inc de + ld bc, 31 + ldir + ret +#endif + + +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 + +PRINT_TAB: + ; Tabulates the number of spaces in A register + ; If the current cursor position is already A, does nothing + PROC + LOCAL LOOP + + call __LOAD_S_POSN ; e = current row + sub e + and 31 + ret z + + ld b, a +LOOP: + ld a, ' ' + call __PRINTCHAR + djnz LOOP + ret + ENDP + +PRINT_AT: ; Changes cursor to ROW, COL + ; COL in A register + ; ROW in stack + + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl + + call __IN_SCREEN + ret nc ; Return if out of screen + jp __SAVE_S_POSN + + LOCAL __PRINT_COM + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_BOLD + LOCAL __PRINT_ITA + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + +#ifndef __ZXB_DISABLE_ITALIC + LOCAL __PRINT_ITA2 +#else + __PRINT_ITA EQU __PRINT_NOP +#endif + +#ifndef __ZXB_DISABLE_BOLD + LOCAL __PRINT_BOLD2 +#else + __PRINT_BOLD EQU __PRINT_NOP +#endif + +__PRINT_TABLE: ; Jump table for 0 .. 22 codes + + DW __PRINT_NOP ; 0 + DW __PRINT_NOP ; 1 + DW __PRINT_NOP ; 2 + DW __PRINT_NOP ; 3 + DW __PRINT_NOP ; 4 + DW __PRINT_NOP ; 5 + DW __PRINT_COM ; 6 COMMA + DW __PRINT_NOP ; 7 + DW __PRINT_DEL ; 8 DEL + DW __PRINT_NOP ; 9 + DW __PRINT_NOP ; 10 + DW __PRINT_NOP ; 11 + DW __PRINT_NOP ; 12 + DW __PRINT_0Dh ; 13 + DW __PRINT_BOLD ; 14 + DW __PRINT_ITA ; 15 + DW __PRINT_INK ; 16 + DW __PRINT_PAP ; 17 + DW __PRINT_FLA ; 18 + DW __PRINT_BRI ; 19 + DW __PRINT_INV ; 20 + DW __PRINT_OVR ; 21 + DW __PRINT_AT ; 22 AT + DW __PRINT_TAB ; 23 TAB + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/printf.asm b/src/lib/arch/zx81sd/runtime/printf.asm new file mode 100644 index 000000000..5c88cbb48 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/printf.asm @@ -0,0 +1,37 @@ +; printf.asm (zx81sd) — PRINT de un numero FLOAT +; +; Sustituye a zx48k/runtime/printf.asm, que usa el literal 'str$' ($2Eh) del +; calculador de la ROM (STR$ + STK-STO-$ + heap temporal) para despues +; imprimir la cadena resultante. Aqui se usa directamente fp_tostr.asm +; (conversion simplificada ya portada) e imprime sus caracteres uno a uno, +; sin pasar por el heap. + +#include once +#include once + + push namespace core + +__PRINTF: + ; Entrada: A,E,D,C,B = valor FLOAT + call FP_TO_STR ; HL = puntero al texto, BC = longitud + PROC + LOCAL __PRINTF_LOOP + +__PRINTF_LOOP: + ld a, b + or c + ret z + + ld a, (hl) + push hl + push bc + call __PRINTCHAR + pop bc + pop hl + inc hl + dec bc + jr __PRINTF_LOOP + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/random.asm b/src/lib/arch/zx81sd/runtime/random.asm new file mode 100644 index 000000000..0aa7e620a --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/random.asm @@ -0,0 +1,102 @@ +; RANDOM functions — ZX81 + SD81 Booster +; Adaptación de zx48k/runtime/random.asm: +; - FRAMES usa el contador VSYNC software en SYSVAR_BASE+$19 +; - RANDOM_SEED_LOW en SYSVAR_BASE+$1B (en vez de sysvar Spectrum 23670) +; - RANDOM_SEED_HIGH sigue siendo RAND+1 (operando inline de ld de,imm16) +; - Sin cambios en el algoritmo RNG (xorshift) + +#include once + + push namespace core + +RANDOMIZE: + ; Randomize with 32 bit seed in DE HL + ; if SEED = 0, uses FRAMES counter as seed + PROC + + LOCAL TAKE_FRAMES + + ld a, h + or l + or d + or e + jr z, TAKE_FRAMES + + ld (RANDOM_SEED_LOW), hl + ld (RANDOM_SEED_HIGH), de + ret + +TAKE_FRAMES: + ; Toma la semilla del contador VSYNC (sustituye al contador de frames de la ROM) + ld hl, (FRAMES) + ld (RANDOM_SEED_LOW), hl + ld hl, 0 + ld (RANDOM_SEED_HIGH), hl + ret + + ENDP + +RANDOM_SEED_HIGH EQU RAND + 1 ; Operando inline de ld de,imm16 (self-modifying seed) + +RAND: + PROC + ld de, 0C0DEh ; yw → zt (DE = semilla alta, se sobreescribe cada vez) + ld hl, (RANDOM_SEED_LOW) ; xz → yw + ld (RANDOM_SEED_LOW), de ; x = y, z = w + ld a, e ; w = w ^ (w << 3) + add a, a + add a, a + add a, a + xor e + ld e, a + ld a, h ; t = x ^ (x << 1) + add a, a + xor h + ld d, a + rra ; t = t ^ (t >> 1) ^ w + xor d + xor e + ld d, l ; y = z + ld e, a ; w = t + ld (RANDOM_SEED_HIGH), de + ret + ENDP + +RND: + ; Returns a FLOATING point integer using RAND as mantissa + PROC + LOCAL RND_LOOP + + call RAND + ld b, h + ld c, l + + ld a, e + or d + or c + or b + ret z + + ld l, 81h + ld a, e +RND_LOOP: + dec l + sla b + rl c + rl d + rla + jp nc, RND_LOOP + + ccf + rra + rr d + rr c + rr b + + ld e, a + ld a, l + ret + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/save.asm b/src/lib/arch/zx81sd/runtime/save.asm new file mode 100644 index 000000000..0b9457960 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/save.asm @@ -0,0 +1,100 @@ +; save.asm (zx81sd) — SAVE ... CODE a la tarjeta SD +; +; Sustituye a zx48k/runtime/save.asm (que usa SA-BYTES de cinta de la +; ROM Spectrum). El statement SAVE de zxbasic guarda el bloque en la SD +; del SD81 Booster con el comando 10 del MCU (ver io/sd81_mcu.asm). +; +; Interfaz del compilador (identica a zx48k): +; pila: BC = longitud; DE = direccion origen; +; HL = string de zxbasic con el nombre (hay que liberarlo). +; +; Errores ("blandos", como la version de cinta): nombre nulo/vacio -> +; ERROR_InvalidFileName; longitud 0 -> ERROR_InvalidArg; fallo del MCU +; (SD llena, etc.) -> ERROR_TapeLoadingErr. En todos se retorna con +; ERR_NR puesto. + +#include once +#include once +#include once + + push namespace core + +SAVE_CODE: + PROC + LOCAL SC_LEN, SC_SRC + LOCAL SC_HAVE_NAME, SC_ERR_NAME_NOFREE, SC_DO_SAVE + + pop hl ; direccion de retorno + pop bc ; longitud + pop de ; direccion origen + ex (sp), hl ; CALLEE: HL = string con el nombre + + ld (SC_LEN), bc + ld (SC_SRC), de + + ld a, h + or l + jp z, SC_ERR_NAME_NOFREE ; string nulo + + ; longitud del bloque 0 -> error (liberando el string) + ld a, b + or c + jr nz, SC_HAVE_NAME + call MEM_FREE + ld a, ERROR_InvalidArg + ld (ERR_NR), a + ret + +SC_HAVE_NAME: + ; nombre vacio -> error (tras liberar) + ld e, (hl) + inc hl + ld d, (hl) + dec hl + ld a, d + or e + jr nz, SC_DO_SAVE + + call MEM_FREE + jp SC_ERR_NAME_NOFREE + +SC_DO_SAVE: + ; comando 10 (SAVE) + nombre + longitud + datos + status + push hl + ld a, 10 + call SD81_MCU_SEND + pop hl + push hl + call SD81_MCU_SEND_NAME + pop hl + call MEM_FREE + + ld bc, (SC_LEN) + ld a, c + call SD81_MCU_SEND ; longitud, byte bajo + ld a, b + call SD81_MCU_SEND ; longitud, byte alto + + ld hl, (SC_SRC) + call SD81_MCU_SEND_BLOCK + + call SD81_MCU_RECV ; byte de estado + or a + ret z + ld a, ERROR_TapeLoadingErr + ld (ERR_NR), a + ret + +SC_ERR_NAME_NOFREE: + ld a, ERROR_InvalidFileName + ld (ERR_NR), a + ret + +SC_LEN: + defw 0 +SC_SRC: + defw 0 + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/specfont.bin b/src/lib/arch/zx81sd/runtime/specfont.bin new file mode 100644 index 000000000..458e0a57a Binary files /dev/null and b/src/lib/arch/zx81sd/runtime/specfont.bin differ diff --git a/src/lib/arch/zx81sd/runtime/stackf.asm b/src/lib/arch/zx81sd/runtime/stackf.asm new file mode 100644 index 000000000..9473ddda2 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/stackf.asm @@ -0,0 +1,97 @@ +; stackf.asm (zx81sd) — Gestión de la pila del calculador FP +; +; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como +; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h +; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio +; binario compilado (varían de un programa a otro), así que no se pueden +; usar como constantes — hay que reimplementar ambas rutinas como código +; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número +; de 5 bytes que el motor CALCULATE ya portado). + +#include once + + push namespace core + +; --------------------------------------------------------------------------- +; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP +; Sustituye a STK-STORE ($2AB6h ROM Spectrum) +; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret + +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + + pop af + pop de + pop bc + + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + + jp __FPSTACK_PUSH + + +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + +; --------------------------------------------------------------------------- +; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B +; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) +; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/str.asm b/src/lib/arch/zx81sd/runtime/str.asm new file mode 100644 index 000000000..e603f8dd5 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/str.asm @@ -0,0 +1,54 @@ +; str.asm (zx81sd) — La funcion STR$( ) +; +; Sustituye a zx48k/runtime/str.asm, que usa el literal 'str$' ($2Eh) del +; calculador de la ROM junto con STK-STO-$ y RECLAIM2 (ROM $19E8h) para +; construir la cadena en el area de trabajo de la ROM. Aqui se usa +; directamente fp_tostr.asm (conversion simplificada ya portada) y se copia +; el resultado a un bloque nuevo del heap propio (mem/alloc.asm). + +#include once +#include once + + push namespace core + +__STR: +__STR_FAST: + ; Entrada: A,E,D,C,B = valor FLOAT + ; Salida: HL = puntero a la cadena (heap), formato [longitud(2B)][texto] + call FP_TO_STR ; HL = puntero al texto, BC = longitud + PROC + LOCAL __STR_END + + push hl ; guarda puntero al texto (FP_STR_BUF) + push bc ; guarda longitud + + ld hl, 2 + add hl, bc + ld b, h + ld c, l + call __MEM_ALLOC ; HL = nuevo bloque de (longitud+2) bytes (o NULL) + + pop bc ; longitud del texto + pop de ; puntero al texto (FP_STR_BUF) + + ld a, h + or l + jr z, __STR_END ; sin memoria -> devuelve NULL + + push hl + ld (hl), c + inc hl + ld (hl), b + inc hl ; HL = destino del texto + + ex de, hl ; HL = origen (texto), DE = destino + ldir + + pop hl ; HL = puntero a la cadena resultante + +__STR_END: + ret + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/sysvars.asm b/src/lib/arch/zx81sd/runtime/sysvars.asm new file mode 100644 index 000000000..3cfeb609e --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/sysvars.asm @@ -0,0 +1,108 @@ +; ----------------------------------------------------------------------- +; ZX81 + SD81 Booster System Variables +; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum +; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. +; +; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), +; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir +; el espacio de ejecución del usuario. +; ----------------------------------------------------------------------- + +; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero +; en incluirse en cualquier programa zx81sd) para que sus #init se registren +; en la primera pasada del preprocesador, antes de que emit_prologue() genere +; las llamadas CALL a las rutinas de inicialización. +#include once + + push namespace core + +; --- Variables dinámicas del runtime ($8000+) --------------------------- +; Zona de datos (bloques 4-5), no ejecutable sin MC45. +; +; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: +; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. +; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + +SYSVAR_BASE EQU $8000 + +CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) +UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs +COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) +FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) +ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) +DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT +DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT +S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) +ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) +MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) +ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal +; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) +P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) +MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas +TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla +ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) +FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) +RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + +; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- +; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ +; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área +; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" +; entre el programa y la pila de máquina). +; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el +; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de +; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. +FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP +FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP +FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) +FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) +FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) +FP_CALC_STACK_END EQU FP_CALC_STACK + 60 +FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + +; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- +; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum +; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus +; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). +; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo +; corrompía en cada acceso. El override zx81sd de array.asm usa esta +; zona en su lugar. +ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + +; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- +; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum +; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección +; de retorno mientras reserva memoria para la cadena. Esa dirección cae +; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de +; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. +CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + +; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- +; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y +; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de +; recuperación de pila alrededor de la división en coma flotante (truco +; de "longjmp" para división por cero). En zx81sd ese mecanismo de +; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT +; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí +; en TODAS las divisiones igualmente, no solo cuando hay error — y esas +; direcciones caen dentro del código compilado (bloque 2). El override +; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde +; vive el scratch). +DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) + +; Tamaño total del bloque de sysvars: $91 bytes + +; --- Constantes de pantalla --------------------------------------------- + +SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) +SCR_ROWS EQU 24 ; Filas (24 filas visibles) +SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/val.asm b/src/lib/arch/zx81sd/runtime/val.asm new file mode 100644 index 000000000..df642dbb0 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/val.asm @@ -0,0 +1,210 @@ +; val.asm (zx81sd) — VAL(a$): convierte texto a numero en coma flotante +; +; Sustituye a zx48k/runtime/val.asm, que usa VAL de la ROM del Spectrum: +; ademas de convertir el texto a numero, la ROM real vuelve a meter la +; cadena en el interprete de BASIC y la evalua como una expresion completa +; (por eso en un Spectrum real VAL("2+2") funciona y da 4). Esa parte vive +; en el escaner de lineas de BASIC de la ROM, un subsistema aparte del +; calculador (no portado aqui). +; +; Esta version soporta solo un LITERAL DECIMAL simple: signo opcional, +; digitos, y un punto decimal opcional seguido de mas digitos. NO evalua +; expresiones (VAL("2+2") no funciona; VAL("2.5") o VAL("-13") si). Es el +; uso habitual de VAL(INPUT(...)) para leer numeros tecleados por el +; usuario. Cualquier caracter no numerico corta el parseo en ese punto +; (el resto de la cadena se ignora), en vez de dar un error. +; +; El numero se construye acumulando digito a digito con el propio +; calculador ya portado (fp_calc.asm): valor = valor*10 + digito, y al +; final se divide entre 10^(numero de decimales) si hubo parte fraccionaria. + +#include once +#include once + + push namespace core + +; --- Variables de trabajo (no reentrante, uso transitorio durante VAL) ----- +VAL_PTR: defw 0 ; puntero al siguiente caracter a leer +VAL_LEN: defw 0 ; caracteres restantes por leer +VAL_STRPTR: defw 0 ; puntero original a la cadena (para liberarla) +VAL_FREE_FLAG: defb 0 ; 1 si hay que liberar la cadena al terminar +VAL_NEG: defb 0 ; 1 si el numero es negativo +VAL_INFRAC: defb 0 ; 1 si ya se paso el punto decimal +VAL_DECIMALS: defb 0 ; numero de digitos leidos tras el punto decimal + +VAL: + ; Entrada: HL = direccion de a$ (2 bytes de longitud + datos) + ; A = 1 si hay que liberar a$ al terminar (no es variable) + ; Salida: A EDCB = numero en punto flotante (via __FPSTACK_POP) + PROC + + LOCAL VAL_EMPTY + LOCAL VAL_LOOP + LOCAL VAL_GOT_SIGN + LOCAL VAL_NOT_DOT + LOCAL VAL_DIGIT + LOCAL VAL_DIGIT_ADVANCE + LOCAL VAL_DONE + LOCAL VAL_DIV_LOOP + LOCAL VAL_NOT_NEG + LOCAL VAL_EMPTY_SKIP + LOCAL VAL_NO_FREE + LOCAL PUSH_DIGIT + + ld (VAL_FREE_FLAG), a + ld a, h + or l + jp z, VAL_EMPTY ; cadena NULL -> 0 (jp: VAL_EMPTY queda lejos) + + ld (VAL_STRPTR), hl + + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = longitud de la cadena + ld (VAL_LEN), de + ld (VAL_PTR), hl ; HL = inicio del texto + + xor a + ld (VAL_NEG), a + ld (VAL_INFRAC), a + ld (VAL_DECIMALS), a + + ld hl, (VAL_LEN) + ld a, h + or l + jp z, VAL_DONE ; cadena vacia -> 0 + + ld hl, (VAL_PTR) + ld a, (hl) + cp '-' + jr nz, VAL_GOT_SIGN + ld a, 1 + ld (VAL_NEG), a + inc hl + ld (VAL_PTR), hl + ld de, (VAL_LEN) + dec de + ld (VAL_LEN), de +VAL_GOT_SIGN: + + ; acumulador FP = 0 + xor a + ld e, a + ld d, a + ld c, a + ld b, a + call __FPSTACK_PUSH + +VAL_LOOP: + ld hl, (VAL_LEN) + ld a, h + or l + jp z, VAL_DONE + + ld hl, (VAL_PTR) + ld a, (hl) + + cp '.' + jr nz, VAL_NOT_DOT + ld a, 1 + ld (VAL_INFRAC), a + jr VAL_DIGIT_ADVANCE ; el punto no cuenta como digito, solo avanza + +VAL_NOT_DOT: + cp '0' + jp c, VAL_DONE + cp '9' + 1 + jp nc, VAL_DONE + +VAL_DIGIT: + sub '0' ; A = digito 0-9 + push af + + ld a, 10 + call PUSH_DIGIT + rst 28h + defb $04 ;;multiply + defb $38 ;;end-calc + + pop af + call PUSH_DIGIT + rst 28h + defb $0F ;;addition + defb $38 ;;end-calc + + ld a, (VAL_INFRAC) + or a + jr z, VAL_DIGIT_ADVANCE + ld hl, VAL_DECIMALS + inc (hl) + +VAL_DIGIT_ADVANCE: + ld hl, (VAL_PTR) + inc hl + ld (VAL_PTR), hl + ld hl, (VAL_LEN) + dec hl + ld (VAL_LEN), hl + jp VAL_LOOP + +VAL_DONE: + ld a, (VAL_DECIMALS) + or a + jr z, VAL_NOT_NEG + ld b, a +VAL_DIV_LOOP: + push bc + ld a, 10 + call PUSH_DIGIT + rst 28h + defb $05 ;;division + defb $38 ;;end-calc + pop bc + djnz VAL_DIV_LOOP + +VAL_NOT_NEG: + ld a, (VAL_NEG) + or a + jr z, VAL_EMPTY_SKIP + rst 28h + defb $1B ;;negate + defb $38 ;;end-calc +VAL_EMPTY_SKIP: + + call __FPSTACK_POP ; A EDCB = resultado + + push af + push de + push bc + ld a, (VAL_FREE_FLAG) + or a + jr z, VAL_NO_FREE + ld hl, (VAL_STRPTR) + call __MEM_FREE +VAL_NO_FREE: + pop bc + pop de + pop af + ret + +VAL_EMPTY: + xor a + ld e, a + ld d, a + ld c, a + ld b, a + jp __FPSTACK_POP + +; --- Apila A (0-255) como entero pequeño positivo --------------------------- +PUSH_DIGIT: + ld d, a + xor a + ld e, a + ld c, a + ld b, a + jp __FPSTACK_PUSH + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/vsync.asm b/src/lib/arch/zx81sd/runtime/vsync.asm new file mode 100644 index 000000000..7cb85a3e9 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/vsync.asm @@ -0,0 +1,42 @@ +; ----------------------------------------------------------------------- +; VSYNC — Sincronización con el refresco de pantalla +; SD81 Booster: puerto $AFh +; bit 0 = estado actual de VSYNC (0/1, informativo, no se usa aquí) +; bits 6-1 = contador de pulsos de VSYNC ocurridos desde la última +; lectura del puerto (se resetea con cada IN) +; +; Sustituye a HALT + interrupción IM1 del ZX Spectrum. +; Las interrupciones están desactivadas (DI) en toda la ejecución. +; ----------------------------------------------------------------------- + +#include once + + push namespace core + +SD81_DATA_PORT EQU $AF ; Puerto de datos MCU del SD81 Booster + +; VSYNC_WAIT — Espera a que ocurra al menos un pulso de VSYNC +; Destruye: A +; No modifica ningún otro registro. +VSYNC_WAIT: + PROC + LOCAL WAIT_PULSE + +WAIT_PULSE: + in a, (SD81_DATA_PORT) + and $7E ; bits 6-1 = contador de pulsos + jr z, WAIT_PULSE ; si 0, ningún pulso desde la última lectura + + ret + ENDP + +; VSYNC_TICK — Espera un frame e incrementa el contador FRAMES +; Destruye: A, HL +VSYNC_TICK: + call VSYNC_WAIT + ld hl, (FRAMES) + inc hl + ld (FRAMES), hl + ret + + pop namespace diff --git a/src/lib/arch/zx81sd/stdlib/cb/maskedsprites.bas b/src/lib/arch/zx81sd/stdlib/cb/maskedsprites.bas new file mode 100644 index 000000000..6e2feff2c --- /dev/null +++ b/src/lib/arch/zx81sd/stdlib/cb/maskedsprites.bas @@ -0,0 +1,1369 @@ +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyright (C) 2026 Conrado Badenas +' Ideas taken from +' https://github.com/boriel-basic/zxbasic/blob/main/src/lib/arch/zx48k/stdlib/memorybank.bas +' by Juan Segura (a.k.a. Duefectu), +' https://github.com/oisee/antique-toy/blob/main/chapters/ch16-sprites/draft.md +' by Alice Vinogradova (a.k.a. oisee), and +' https://youtu.be/nBHXtI1Y-xU?t=434 and https://youtu.be/-AUmmzDiGlE?t=434 +' by Benjamín (a.k.a. RetrobenSoft) +' +' Print Masked (AND+OR) Sprites, version 2026.04.05 +' +' Version zx81sd (2026-07-04): la version de zx48k asume paginacion de +' memoria al estilo Spectrum 128K (banco visible en $c000-$ffff via +' puerto $7FFDh, sysvar BANKM en $5B5C) para el subsistema MSFS (Masked +' Sprites File System, un almacen de imagenes de sprite en RAM de +' sobra). Nada de eso existe en zx81sd. Se sustituye por el mapeador de +' memoria propio (puerto $E7h) sobre el BLOQUE 7 ($E000-$FFFF), que en +' nuestro mapa de memoria ya esta reservado para "banking de datos +' (mapas, sprites...)" -- justo este caso de uso. +' +' Las funciones de MSFS (RegisterSpriteImageInMSFS, FindFirstUnusedBlock +' InMSFS, etc.) son agnosticas de banco/direccion: solo llaman a +' GetBankPreservingRegs/SetBankPreservingINTs y leen/escriben la +' variable BASIC MaskedSpritesFileSystemStart. Reescribiendo esas DOS +' primitivas (y el calculo de esa direccion en InitMaskedSpritesFileSystem, +' mas abajo, que en el original asumia RAM plana Spectrum hasta $FFFF) +' el resto del fichero se copia sin tocar una sola linea. +' +' CheckMemoryPaging() aqui solo influye, en el ejemplo, en si se usa +' pantalla visible doble (bancos 5/7 del Spectrum) -- zx81sd tiene un +' unico framebuffer fisico (bloque 6, SCREEN_ADDR fijo en $C000) y esa +' funcionalidad no esta cubierta aqui (ver nota en SetVisibleScreen mas +' abajo). Las funciones de MSFS NO consultan CheckMemoryPaging() para +' decidir si usar el banco -- lo hacen incondicionalmente -- asi que +' devolver 0 (honesto: no hay doble pantalla visible) no le afecta en +' nada a que MSFS funcione. +' +' RESIDENCIA: desde InitMaskedSpritesFileSystem(), la pagina de MSFS se +' queda mapeada de forma PERMANENTE en el bloque 7 (SetBankPreservingINTs +' con valor <> 7 solo anota el numero, no desmapea) -- las rutinas de +' dibujo del bucle principal acceden a la MSFS sin envolver con +' Get/SetBank y necesitan verla siempre. Si un programa usa el bloque 7 +' para su propio banking de datos, debe remapear su pagina el mismo y +' llamar a SetBankPreservingINTs(7) antes de volver a usar MSFS. +' ---------------------------------------------------------------- + +#ifndef __CB_MASKEDSPRITES__ + +REM Avoid recursive / multiple inclusion + +#define __CB_MASKEDSPRITES__ + +#include +#include +#include + + +' ---------------------------------------------------------------- +' Banco de MSFS sobre el mapeador de zx81sd (puerto $E7h, bloque 7) +' +' MaskedSprites_MSFS_Page es la pagina SD81 dedicada a guardar la MSFS. +' Debe evitar las paginas 8-13 (codigo/heap, fijas por el bootstrap, ver +' src/arch/zx81sd/backend/main.py _PAGE_MAP) y 63 (pagina "libre" por +' convencion del cargador SD81: split_sd81.py deja siempre el bloque 7 +' apuntando a la pagina 63 tras cargar, antes de saltar al programa). +' Redefinible con #define ANTES de este #include si el programa ya usa +' la pagina 20 para otra cosa (no hay todavia un asignador global de +' paginas en el proyecto). +' ---------------------------------------------------------------- +#ifndef MaskedSprites_MSFS_Page + #define MaskedSprites_MSFS_Page 20 +#endif + +' Byte de estado puro en ASM (no DIM): un DIM normal se eliminaria por +' "codigo muerto" al no tener ninguna referencia desde BASIC (solo se +' toca desde las dos rutinas ASM de abajo). +ASM +_ZX81SD_MSFS_Bank: + DEFB 0 +END ASM + +' ---------------------------------------------------------------- +' Set a RAM bank in addresses $c000-$ffff, update BANKM, +' and return with INTerrupts preserved (unchanged) +' Only works on 128K and compatible models. +' Parameters: +' Ubyte: bank number 0,1,2,3,4,5,6,7 +' Changes: +' A, B, C +' Preserves: +' D, E, H, L are not used +' +' Version zx81sd: "banco" aqui es puramente logico. Con 7 se asegura que +' la pagina de MSFS este mapeada en el bloque 7; con cualquier otro +' valor SOLO se anota el numero (la pagina de MSFS se queda RESIDENTE, +' no se desmapea). Motivo: SaveBackgroundAndDrawSpriteRegisteredInMSFS +' (el que dibuja en el bucle principal, y el que crea imagenes +' desplazadas bajo demanda) accede a la MSFS SIN envolver con +' Get/SetBank -- en el diseno original no lo necesita porque en 128K el +' banco 7 se queda mapeado en $c000 (SetDrawingScreen7) y en 48K la +' MSFS esta en RAM plana siempre visible. Una primera version de este +' override "liberaba" el bloque 7 a la pagina 63 al restaurar tras +' Init/Register... y el dibujo del bucle principal leia mascaras y +' graficos de la pagina 63 (basura): sprites como lineas verticales. +' Si un programa usa el bloque 7 para su propio banking de datos, +' debe remapear su pagina el mismo y volver a llamar aqui con 7 antes +' de usar MSFS (documentado tambien en la cabecera del fichero). +' +' No hace falta preservar interrupciones: en zx81sd estan +' permanentemente deshabilitadas (DI todo el tiempo). IMPORTANTE: +' escrita en ASM a mano (no en BASIC plano) para respetar EXACTAMENTE +' el contrato de registros de arriba -- RegisterSpriteImageInMSFS y +' companeros confian en que D,E,H,L sobrevivan a esta llamada (por +' ejemplo, para no perder spriteImageAddr, que llega en HL); una +' implementacion en BASIC normal no da esa garantia (usa registros +' libremente por dentro) y provocaba que todos los sprites se +' registraran en la misma direccion. +' ---------------------------------------------------------------- +SUB FASTCALL SetBankPreservingINTs(bankNumber AS UByte) +ASM + PROC + LOCAL NO_MAP + ld (_ZX81SD_MSFS_Bank), a + cp 7 + jr nz, NO_MAP ; banco logico <> 7: solo anotar, la + ; pagina de MSFS se queda residente + push de + push hl + ld d, MaskedSprites_MSFS_Page + ld e, 0 + push de ; parametro 'page' de Map (byte alto = D) + ld a, 7 ; parametro 'block' de Map (bloque 7, fijo) + call _Map + pop hl + pop de +NO_MAP: + ENDP +END ASM +END SUB + + +' ---------------------------------------------------------------- +' Get which RAM bank is set in addresses $c000-$ffff +' Only works on 128K and compatible models. +' Preserves: +' B, C, D, E, H, L are not used +' Returns: +' UByte: bank number 0,1,2,3,4,5,6,7 +' +' Version zx81sd: devuelve el "banco logico" guardado por la ultima +' llamada a SetBankPreservingINTs (ver arriba). En ASM a mano por el +' mismo motivo que SetBankPreservingINTs: preservar B,C,D,E,H,L de +' verdad para las rutinas de MSFS que dependen de ello. +' ---------------------------------------------------------------- +FUNCTION FASTCALL GetBankPreservingRegs() AS UByte +ASM + ld a, (_ZX81SD_MSFS_Bank) +END ASM +END FUNCTION + + +' ---------------------------------------------------------------- +' Check whether memory paging works (128,+2,...) or not (16,48) +' Returns: +' UByte: 1 if paging works, 0 if it does not +' +' Version zx81sd: siempre devuelve 0. No existe paginacion de pantalla +' visible al estilo Spectrum 128K (un unico framebuffer fisico, bloque +' 6); ver la nota de cabecera de este fichero sobre por que esto no +' afecta a que MSFS funcione (usa el mapeador propio, no esta funcion). +' ---------------------------------------------------------------- +FUNCTION FASTCALL CheckMemoryPaging() AS UByte + RETURN 0 +END FUNCTION + + +' ---------------------------------------------------------------- +' Set the visible screen (either in bank5 or bank7) +' and updates the system variable BANKM. +' Only works on 128K and compatible models. +' Parameters: +' Ubyte: bank number 5 or 7 +' Preserves: +' D, E, H, L are not used +' +' Version zx81sd: no existe hardware de doble pantalla visible +' intercambiable (bancos 5/7 del Spectrum 128K) -- zx81sd solo tiene un +' framebuffer fisico (bloque 6, SCREEN_ADDR fijo en $C000). Se deja como +' stub seguro (no toca $5B5C ni el puerto $7FFD, que no existen aqui); +' CheckMemoryPaging() devuelve 0, asi que en la practica nunca se llama +' de verdad. Doble buffer de pantalla real (alternando que pagina +' respalda el bloque 6 via el mapeador) seria una funcionalidad aparte, +' no cubierta aqui. +' ---------------------------------------------------------------- +SUB FASTCALL SetVisibleScreen(bankNumber AS UByte) +END SUB + + +' ---------------------------------------------------------------- +' Returns the bank of visible screen (either 5 or 7) +' according to system variable BANKM. +' Only works on 128K and compatible models. +' Returns: +' UByte: bank 5 or 7 +' +' Version zx81sd: stub seguro, ver SetVisibleScreen arriba. +' ---------------------------------------------------------------- +FUNCTION FASTCALL GetVisibleScreen() AS UByte + RETURN 5 +END FUNCTION + + +' ---------------------------------------------------------------- +' Toggles the visible screen (from 5 to 7, or from 7 to 5) +' and updates the system variable BANKM. +' Only works on 128K and compatible models. +' +' Version zx81sd: stub seguro, ver SetVisibleScreen arriba. +' ---------------------------------------------------------------- +SUB FASTCALL ToggleVisibleScreen() +END SUB + + +' ---------------------------------------------------------------- +' Copy contents of screen5 to screen7 (display file + attribs) +' Only works on 128K and compatible models. +' +' Version zx81sd: stub seguro, ver SetVisibleScreen arriba. +' ---------------------------------------------------------------- +SUB FASTCALL CopyScreen5ToScreen7() +END SUB + + +' ---------------------------------------------------------------- +' Copy contents of screen7 to screen5 (display file + attribs) +' Only works on 128K and compatible models. +' +' Version zx81sd: stub seguro, ver SetVisibleScreen arriba. +' ---------------------------------------------------------------- +SUB FASTCALL CopyScreen7ToScreen5() +END SUB + + +' ---------------------------------------------------------------- +' Set ScreenBufferAddr and AttrBufferAddr to screen5 +' Only works on 128K and compatible models. +' +' Version zx81sd: stub seguro, ver SetVisibleScreen arriba. IMPORTANTE: +' NO se puede simplemente copiar el original -- SetScreenBufferAddr($4000) +' redirigiria SCREEN_ADDR a plena zona de codigo/runtime ($1000-$7FFF en +' nuestro mapa de memoria), corrompiendo cualquier PRINT/grafico +' posterior. Se deja vacia. +' ---------------------------------------------------------------- +SUB FASTCALL SetDrawingScreen5() +END SUB + + +' ---------------------------------------------------------------- +' Put screen7 at $c000 (in case it is not), and +' Set ScreenBufferAddr and AttrBufferAddr to screen7 +' Only works on 128K and compatible models. +' Returns: +' Bank7 is set at $c000, old bank is removed +' UByte: bank that was at $c000 (to restore it manually IYW) +' +' Version zx81sd: stub seguro, ver SetVisibleScreen arriba. $c000/$d800 +' coinciden por casualidad con nuestro SCREEN_ADDR/SCREEN_ATTR_ADDR +' reales, pero esta funcion tambien mapea banco7 (aqui reinterpretado +' como el banco de MSFS) al bloque 7, no al bloque 6 (pantalla) -- no +' hay overlap real posible entre "pantalla" y "banco 7" en zx81sd, asi +' que se deja como no-op en vez de fingir un intercambio que no existe. +' ---------------------------------------------------------------- +FUNCTION FASTCALL SetDrawingScreen7() AS UByte + RETURN 5 +END FUNCTION + + +' ---------------------------------------------------------------- +' Toggle ScreenBufferAddr and AttrBufferAddr between screen5,7 +' Only works on 128K and compatible models. +' +' Version zx81sd: stub seguro, ver SetVisibleScreen arriba. +' ---------------------------------------------------------------- +SUB FASTCALL ToggleDrawingScreen() +END SUB + + +' ---------------------------------------------------------------- +' MaskedSpritesBackgroundSet = 0 or 1 is the Set of Backgrounds +' +' MaskedSpritesBackground(i) is the address where Background i begins +' +' NumberofMaskedSprites is a MACRO that should be #define-d +' before #include-ing this file +' +' MaskedSprites_USE_STACK_TRANSFER is a MACRO that should be #define-d +' if you want this library to use Stack PUSH+POP instructions to speed-up +' transfer of information between different parts of the RAM +' (this library will disable interrupts before using Stack Transfer) +' +' ChangeMaskedSpritesBackgroundSet() changes the Set of Backgrounds +' Returns: +' Byte: new value of MaskedSpritesBackgroundSet (IYW to use it) +' ---------------------------------------------------------------- +dim MaskedSpritesBackgroundSet AS UByte = 0 + +#define MaskedSpritesBackground(i) ( $db00+48*CAST(UInteger,i)+48*CAST(UInteger,NumberofMaskedSprites)*MaskedSpritesBackgroundSet ) + +FUNCTION FASTCALL ChangeMaskedSpritesBackgroundSet() AS UByte + MaskedSpritesBackgroundSet = MaskedSpritesBackgroundSet bXOR 1 + RETURN MaskedSpritesBackgroundSet +END FUNCTION + + +' ---------------------------------------------------------------- +' MaskedSprites_NEXT_ROW is a MACRO of ASM code, based on code from +' https://zonadepruebas.com/viewtopic.php?f=15&t=8372&start=40#p81507 +' and found by Joaquin Ferrero +' ---------------------------------------------------------------- +#define MaskedSprites_NEXT_ROW \ + ld a,e ; 4 A = E \ + sub 224 ; 7 A = E + 32 (SUB 224 is similar to +32) \ + ; CF = 0/1 iff E >=/< 224 iff a third is/isn't crossed \ + ld e,a ; 4 \ + sbc a,a ; 4 A = 0/255 \ + and 248 ; 7 A = 0/248 (248 = -8) \ + add a,d ; 4 A = D/D-8 iff a third is/isn't crossed \ + ld d,a ; 4 += 34 Ts + + +' ---------------------------------------------------------------- +' Save background and Draw sprite in screen +' Parameters: +' UByte: X coordinate (0:left to 240:right) +' UByte: Y coordinate (0:up to 176:down) +' UInteger: Address where background will be saved +' UInteger: Address where sprite image begins +' ---------------------------------------------------------------- +SUB FASTCALL SaveBackgroundAndDrawSprite(X AS UByte, Y AS UByte, backgroundAddr AS UInteger, spriteImageAddr AS UInteger) +ASM + PROC + LOCAL shiftright, shiftleft, noshift + LOCAL loopSR, loopSL, loopNS, loopR, loopL, branchSR, branchSL, branchNS + ; A = X + pop de ; returnAddr + exx + pop bc ; B = Y + ld c,a ; C = X +; BEGIN code from https://skoolkid.github.io/rom/asm/22AA.html + rlca + rlca + rlca ; A = %c4c3c2c1c0c7c6c5 + xor b + and %11000111 + xor b ; A = %c4c3b5b4b3c7c6c5 + rlca + rlca + ld e,a ; E = %b5b4b3c7c6c5c4c3 + ld a,b + and %11111000 + rra + rra + rra ; A = %.0.0.0b7b6b5b4b3 + xor b + and %11111000 + xor b + ld d,a ; D = %.0.0.0b7b6b2b1b0 +; END code from https://skoolkid.github.io/rom/asm/22AA.html + ld hl,(.core.SCREEN_ADDR) + add hl,de + ex de,hl ; DE = screenAddr where drawing will start + ld a,c; ; A = X + and 7 + jr z,noshift ; jump if X is a multiple of 8 (unlikely) + ; continue if sprite must be shifted + cp 4 ; is >= 4 ? + jp nc,shiftleft ; shift left if X MOD 8 = 4,5,6,7 + ; shift right if X MOD 8 = 1,2,3 +shiftright: + pop bc ; backgroundAddr + exx + pop hl ; spriteImageAddr + push de ; returnAddr + push ix + ld ixh,16 ; 16 scanlines + ld ixl,a ; IXl = X MOD 8 = 1,2,3,4 +loopSR: + ld a,(hl) ; mask1 + inc hl + ld c,(hl) ; graph1 + inc hl + ld d,(hl) ; mask2 + inc hl + ld e,(hl) ; graph2 + inc hl + push hl ; spriteImageAddr + + ld hl,$FF00 ; H = 255 , L = 0 + ld b,ixl +loopR: + scf ; 4 + rra ; 4; SCF + RRA injects a 1 in bit7 of A + rr d ; 8 + rr h ; 8 + srl c ; 8; ShiftRightLogical injects a 0 in bit7 of C + rr e ; 8 + rr l ; 8 + djnz loopR ; 4+4+8+8+8+8+8 = 48 Ts + ld b,a + push hl ; H,L = mask,graph 3rd byte + push de ; D,E = mask,graph 2nd byte + push bc ; B,C = mask,graph 1st byte + exx + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 1st byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 2nd byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 3rd byte done + dec e + dec e + + inc d + ld a,d + and 7 + jr z,branchSR ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + exx + pop hl ; spriteImageAddr + dec ixh + jp nz,loopSR + pop ix + ret +branchSR: + MaskedSprites_NEXT_ROW + exx + pop hl ; spriteImageAddr + dec ixh + jp nz,loopSR + pop ix + ret + +noshift: + pop bc ; backgroundAddr + pop hl ; spriteImageAddr + exx + push de ; returnAddr + exx + push ix + ld ixh,16 ; 16 scanlines +loopNS: + ld a,(de) ; screen + ld (bc),a; ; save + inc bc + and (hl); ; mask + inc hl + or (hl) ; graph + inc hl + ld (de),a ; 1st byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + and (hl); ; mask + inc hl + or (hl) ; graph + inc hl + ld (de),a ; 2nd byte done + dec e + + inc d + ld a,d + and 7 + jr z,branchNS ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + dec ixh + jp nz,loopNS + pop ix + ret +branchNS: + MaskedSprites_NEXT_ROW + dec ixh + jp nz,loopNS + pop ix + ret + +shiftleft: + pop bc ; backgroundAddr + exx + pop hl ; spriteImageAddr + push de ; returnAddr + push ix + ld ixh,16 ; 16 scanlines + sub 8 + neg ; A = 8 - oldA + ld ixl,a ; IXl = 8 - (X MOD 8) = 8 - 4,5,6,7 = 4,3,2,1 +loopSL: + ld a,(hl) ; mask1 + inc hl + ld c,(hl) ; graph1 + inc hl + ld d,(hl) ; mask2 + inc hl + ld e,(hl) ; graph2 + inc hl + push hl ; spriteImageAddr + + ld hl,$FF00 ; H = 255 , L = 0 + ld b,ixl +loopL: + sll d ; 8; ShiftLeftLogical injects a 1 in bit0 of D + rla ; 4 + rl h ; 8 + sla e ; 8; ShiftLeftArithmetic injects a 0 in bit0 of E + rl c ; 8 + rl l ; 8 + djnz loopL ; 8+4+8+8+8+8 = 44 Ts + ld b,a + push de ; D,E = mask,graph 3rd byte + push bc ; B,D = mask,graph 2nd byte + push hl ; H,L = mask,graph 1st byte + exx + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 1st byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 2nd byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 3rd byte done + dec e + dec e + + inc d + ld a,d + and 7 + jr z,branchSL ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + exx + pop hl ; spriteImageAddr + dec ixh + jp nz,loopSL + pop ix + ret +branchSL: + MaskedSprites_NEXT_ROW + exx + pop hl ; spriteImageAddr + dec ixh + jp nz,loopSL + pop ix + ret + ENDP +END ASM +END SUB + + +' ---------------------------------------------------------------- +' Restore background in screen +' Parameters: +' UByte: X coordinate (0:left to 240:right) +' UByte: Y coordinate (0:up to 176:down) +' UInteger: Address where saved background begins +' ---------------------------------------------------------------- +SUB FASTCALL RestoreBackground(X AS UByte, Y AS UByte, backgroundAddr AS UInteger) +ASM + PROC + LOCAL loop2b, loop3b, branch2b, branch3b + ; A = X + pop de ; returnAddr + exx + pop bc ; B = Y + ld c,a ; C = X +; BEGIN code from https://skoolkid.github.io/rom/asm/22AA.html + rlca + rlca + rlca ; A = %c4c3c2c1c0c7c6c5 + xor b + and %11000111 + xor b ; A = %c4c3b5b4b3c7c6c5 + rlca + rlca + ld e,a ; E = %b5b4b3c7c6c5c4c3 + ld a,b + and %11111000 + rra + rra + rra ; A = %.0.0.0b7b6b5b4b3 + xor b + and %11111000 + xor b + ld d,a ; D = %.0.0.0b7b6b2b1b0 +; END code from https://skoolkid.github.io/rom/asm/22AA.html + ld hl,(.core.SCREEN_ADDR) + add hl,de + ex de,hl ; DE = screenAddr where restoring will start + pop hl ; backgroundAddr + exx + push de ; returnAddr + exx + ld a,c; ; A = X + ld bc,$10FF ; B = 16, C = 255 (up to 255 LDIs do not change B) + and 7 + jr z,loop2b ; jump if X is a multiple of 8 (unlikely) + ; continue if restoring 3 bytes per scanline +; 3bytes per scanline +loop3b: + ldi ; 16 Ts vs 7+7+6+4=24 Ts + ldi + ldi ; 3 bytes background restored to screen + dec de ; last LDI could have increased D if initially E=253... + dec e ; ...so DEC DE restores D in that case + dec e + + inc d + ld a,d + and 7 + jr z,branch3b ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + djnz loop3b + ret +branch3b: + MaskedSprites_NEXT_ROW + djnz loop3b + ret +; 2bytes per scanline +loop2b: + ldi ; 16 Ts vs 7+7+6+4=24 Ts + ldi ; 2 bytes background restored to screen + dec de ; last LDI could have increased D if initially E=254... + dec e ; ...so DEC DE restores D in that case + + inc d + ld a,d + and 7 + jr z,branch2b ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + djnz loop2b + ret +branch2b: + MaskedSprites_NEXT_ROW + djnz loop2b + ret + ENDP +END ASM +END SUB + + +' ---------------------------------------------------------------- +' Structure of the Masked Sprites FileSystem (MSFS): +' +' MSFS consists of many blocks of 96 bytes +' MSFS starts at address stored in MaskedSpritesFileSystemStart, e.g., 56736 +' MSFS length is a multiple of 96 bytes, e.g., 8736 = 91*96 bytes +' With that length, MSFS ranges from 56736 to 65471 +' MSFS stores Images (mask+graph) of Masked Sprites +' Blocks used are marked in the FSB (https://en.wikipedia.org/wiki/Free-space_bitmap) +' +' First block of the MSFS (superblock, block number = 0) +' start+0 DEFB number of blocks in MSFS = bits of the FSB, e.g., 91 +' start+1 DEFB number of bytes of the FSB, e.g., 12 (91/8 = 11.4) +' start+2-13 DEFS 12 is the FSB (12 bytes = 96 bits is enough for 91 blocks) +' start+14-15 unused +' +' Block of an unshifted image (block number n = 0,...,90) +' start+n*96 \ +' ... | 16 bytes unused for n>0, superblock for n=0 +' start+n*96+15 / +' start+n*96+16-17 DEFW start+n*96+32 = start of unshifted image +' start+n*96+18-19 DEFW address of block for image shifted 1 pixel, or 0 if not used +' start+n*96+20-21 DEFW address of block for image shifted 2 pixels, or 0 if not used +' ... +' start+n*96+30-31 DEFW address of block for image shifted 7 pixels, or 0 if not used +' start+n*96+32-95 DEFS 64 the unshifted image (mask+graph) +' +' Block number 0 (n=0) is special because it contains +' 16 bytes for the superblock (including 2 unused bytes) + +' 16 bytes for addressing unshifted and shifted versions of the first image + +' 64 bytes for the first unshifted image +' +' Block of a shifted image (block number m = 1,...,90, note m>0) +' start+m*96+0 DEFS 96 the shifted image (mask+graph) +' ---------------------------------------------------------------- +dim MaskedSpritesFileSystemStart AS UInteger = 0 +' ---------------------------------------------------------------- +' MaskedSpritesFileSystemStart = address where Masked Sprites FileSystem starts +' +' InitMaskedSpritesFileSystem() inits the MSFS +' Returns: +' UInteger: value of MaskedSpritesFileSystemStart (IYW to use it) +' +' Version zx81sd: MaskedSpritesFileSystemStart es una direccion FIJA +' dentro del bloque 7 ($E000, la pagina dedicada MaskedSprites_MSFS_Page) +' en vez de "lo que quede hasta $FFFF" (el original asumia RAM plana +' Spectrum ahi, que en zx81sd es una ventana paginada de 8K, no RAM +' contigua). Con $E000: l = Int((65536-57344)/96) = 85 bloques -- toda +' la MSFS cabe de sobra en los 8K del bloque 7. El resto de la funcion +' (calculo de bytes del FSB, pokes) es identico al original. Se llama +' siempre a SetBankPreservingINTs(7) (no gated por CheckMemoryPaging(), +' que aqui es irrelevante para MSFS -- ver cabecera del fichero). +' ---------------------------------------------------------------- +FUNCTION FASTCALL InitMaskedSpritesFileSystem() AS UInteger + DIM b AS UByte + DIM l, j AS UInteger + + b = GetBankPreservingRegs() + if b<>7 then SetBankPreservingINTs(7) + ' Llamada BASIC explicita a Map(), redundante con la que ya hace + ' SetBankPreservingINTs por ASM a mano (mapea el mismo bloque/pagina + ' otra vez, sin efecto): el analisis de "codigo muerto" del + ' compilador no cuenta las llamadas hechas desde ASM como uso, y sin + ' esto Map() se elimina del binario -> "Undefined GLOBAL label" al + ' intentar llamarla desde dentro de SetBankPreservingINTs. + Map(7, MaskedSprites_MSFS_Page) + MaskedSpritesFileSystemStart = $E000 + l = -MaskedSpritesFileSystemStart + l = Int(l/96) + poke MaskedSpritesFileSystemStart, l ' MSFS blocks = FSB bits + if l=0 then STOP + l = 1+Int((l-1)/8) + poke MaskedSpritesFileSystemStart+1,l ' FSB bytes + ' Limpiar el FSB (bitmap de bloques libres, bytes start+2..start+1+l). + ' Ni esta version ni la original de zx48k lo inicializaban: en el + ' Spectrum no hace falta porque el test de RAM de la ROM deja toda + ' la memoria a cero en el arranque, asi que el bitmap nace "todo + ' libre" gratis. En zx81sd la pagina del bloque 7 llega con basura + ' de fabrica: todos los bits aparecian a 1 ("ocupado"), + ' FindFirstUnusedBlockInMSFS recorria el FSB entero sin encontrar + ' hueco y RegisterSpriteImageInMSFS devolvia siempre 0 (lleno). + FOR j = MaskedSpritesFileSystemStart+2 TO MaskedSpritesFileSystemStart+1+l + poke j, 0 + NEXT j + if b<>7 then SetBankPreservingINTs(b) + return MaskedSpritesFileSystemStart + +' Now, some assembly routines needed for next SUB/FUNCTIONs +ASM +; ---------------------------------------------------------------- +; Find memory addres in MSFS for the start of a block +; Parameters: +; L = blocknumber = n = 0,1,2,... (probably less than 200) +; Preserves: +; A, B, C are not used +; Returns: +; HL = start+n*96 +; DE = start +; ---------------------------------------------------------------- +FindMemoryAdressForBlockInMSFS: + PROC + ld h,0 + ld d,h + ld e,l ; HL = DE = n = blocknumber + add hl,de + add hl,de ; HL = DE*3 + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl ; HL = DE*3*(2^5) = DE*96 + ld de,(_MaskedSpritesFileSystemStart) + add hl,de; ; HL = start+n*96 + ret + ENDP + +; ---------------------------------------------------------------- +; Find First Unused Block in MSFS and (optionally) Book it +; Parameters: +; CarryFlag = 0/1 don't/do Book it +; Preserves: +; C is not used +; Returns: +; CarryFlag = 0 if found, 1 if not found +; A = FirstUnusedBlock if found +; HL = start+A*96 if found +; ---------------------------------------------------------------- +FindFirstUnusedBlockInMSFS: + PROC + LOCAL loop1, loop2, full, found, loop3, compute + ex af,af' ; saves CarryFlag + ld hl,(_MaskedSpritesFileSystemStart) + ld d,(hl) + ld e,d ; D = E = number of bits in the FSB = N + inc hl + inc hl ; HL points to the first byte in the FSB +loop1: + ld a,(hl) + ld b,8 +loop2: + rrca + jr nc,found + dec e + jr z,full + djnz loop2 + inc hl + jp loop1 +full: ; E = 0 + scf ; CarryFlag=1 = ERROR + ret +found: ; E = N,N-1,N-2,...,1 + ex af,af' + jr nc,compute + ex af,af' + rlca ; undo last RRCA + or 1 ; mark this block as used +loop3: + rrca ; finish the 8-bit rotation to... + djnz loop3 ; leave bits where they were + ld (hl),a ; effective booking +compute: + ld a,d ; A = N + sub e ; A = 0,1,2,...,N-1 = blocknumber + ld l,a + call FindMemoryAdressForBlockInMSFS + and a ; CarryFlag=0 = OK + ret ; A = blocknumber, HL = start+A*96 + ENDP +END ASM + +END FUNCTION + + +' ---------------------------------------------------------------- +' Get Number of Free Blocks in MSFS +' Returns: +' UByte: number of free blocks in MSFS +' ---------------------------------------------------------------- +FUNCTION FASTCALL GetNumberofFreeBlocksInMSFS() AS UByte +ASM + PROC + LOCAL loop1, loop2, exit + call _GetBankPreservingRegs + cp 7 ; ZeroFlag=0 (NZ) iff _GetBankPreservingRegs returns A<>7 + push af ; ZF and original RAM bank when FUNCTION was called + ld a,7 + call nz,_SetBankPreservingINTs ; set RAM7 if _GetBankPreservingRegs returns A<>7 + xor a ; A = 0 = number of reset bits in the FSB + ld hl,(_MaskedSpritesFileSystemStart) + ld d,(hl) ; D = number of bits in the FSB = N + ld e,a ; E = 0 always + inc hl + inc hl ; HL points to the first byte in the FSB +loop1: + ld c,(hl) + ld b,8 +loop2: + rr c + ccf ; CarryFlag = 0/1 = bit in the FSB is set/reset + adc a,e ; A += CarryFlag = number of reset bits in the FSB + dec d + jr z,exit ; return A if all bits in FSB have been checked + djnz loop2 + inc hl + jr loop1 +exit: + ex af,af' ; A' = number of free blocks in MSFS + pop af ; ZF and original RAM bank when FUNCTION was called + call nz,_SetBankPreservingINTs + ex af,af' ; A = number of free blocks in MSFS + ret + ENDP +END ASM +END FUNCTION + + +' ---------------------------------------------------------------- +' Register spriteImage in MSFS +' Parameters: +' UInteger: address where spriteImage begins +' Returns: +' UInteger: registry number in the MSFS = start+n*96+16 if OK +' 0 if not OK +' ---------------------------------------------------------------- +FUNCTION FASTCALL RegisterSpriteImageInMSFS(spriteImageAddr AS UInteger) AS UInteger +ASM + PROC + LOCAL full, exit + call _GetBankPreservingRegs + cp 7 ; ZeroFlag=0 (NZ) iff _GetBankPreservingRegs returns A<>7 + push af ; ZF and original RAM bank when FUNCTION was called + push hl ; spriteImageAddr + ld a,7 + call nz,_SetBankPreservingINTs ; set RAM7 if it was not set + scf + call FindFirstUnusedBlockInMSFS ; and book it (SCF) + jr c,full + ld bc,16 + add hl,bc + push hl ; HL = start+A*96+16 + ld d,h + ld e,l + inc de ; DE = start+A*96+17 + dec bc ; BC = 15 + ld (hl),0 + ldir ; reset RAM from start+A*96+16 to start+A*96+31 (incl.) + pop hl ; HL = start+A*96+16 + ld b,h + ld c,l ; BC = start+A*96+16 + ld (hl),e ; DE = start+A*96+32 (after last LDIR) + inc hl + ld (hl),d ; start+A*96+16-17 DEFW start+A*96+32 + pop hl ; spriteImageAddr + push bc ; BC = start+A*96+16 + ld bc,64 + ldir ; transfer from spriteImageAddr to start+A*96+32 + pop hl ; HL = start+A*96+16 +exit: + pop af ; ZF and original RAM bank when FUNCTION was called + call nz,_SetBankPreservingINTs + ret +full: + pop hl ; spriteImageAddr + ld hl,0 + jr exit + ENDP +END ASM +END FUNCTION + + +' ---------------------------------------------------------------- +' Register spriteGraph and spriteMask in MSFS +' (useful when different Graphs share the same Mask) +' +' Data in spriteGraph and spriteMask MUST be in "putchars format" +' +' Parameters: +' UInteger: address where spriteGraph begins +' UInteger: address where spriteMask begins +' Returns: +' UInteger: registry number in the MSFS = start+n*96+16 if OK +' 0 if not OK +' ---------------------------------------------------------------- +FUNCTION FASTCALL RegisterSpriteGraphAndMaskInMSFS(spriteGraphAddr AS UInteger,spriteMaskAddr AS UInteger) AS UInteger +ASM + PROC + LOCAL full, exit, loop1, loop2 + pop de ; returnAddr + pop bc ; spriteMaskAddr + push de ; returnAddr +; stack is empty. Now we will push data to be preserved + exx ; HL' = spriteGraphAddr, BC' = spriteMaskAddr + call _GetBankPreservingRegs + cp 7 ; ZeroFlag=0 (NZ) iff _GetBankPreservingRegs returns A<>7 + push af ; ZF and original RAM bank when FUNCTION was called + ld a,7 + call nz,_SetBankPreservingINTs ; set RAM7 if it was not set + scf + call FindFirstUnusedBlockInMSFS ; and book it (SCF) + jr c,full + ld bc,16 + add hl,bc + push hl ; HL = start+A*96+16 + ld d,h + ld e,l + inc de ; DE = start+A*96+17 + dec bc ; BC = 15 + ld (hl),0 + ldir ; reset RAM from start+A*96+16 to start+A*96+31 (incl.) + pop hl ; HL = start+A*96+16 + ld (hl),e ; DE = start+A*96+32 (after last LDIR) + inc hl + ld (hl),d ; start+A*96+16-17 DEFW start+A*96+32 + dec hl + push hl ; return value HL = start+A*96+16 + push de ; DE = start+A*96+32 DEFS 64 the unshifted image (mask+graph) + exx ; HL = spriteGraphAddr, BC = spriteMaskAddr + pop de ; DE = DEFS 64 the unshifted image (mask+graph) + push ix + push de ; DE = start+A*96+32 + ld ixl,16 +loop1: + ld a,(bc) ; mask + ld (de),a + inc bc + inc de + ld a,(hl) ; graph + ld (de),a + inc hl + inc de + inc de + inc de + dec ixl + jp nz,loop1 + pop de ; DE = start+A*96+32 + inc de + inc de ; DE = start+A*96+32 +2 + ld ixl,16 +loop2: + ld a,(bc) ; mask + ld (de),a + inc bc + inc de + ld a,(hl) ; graph + ld (de),a + inc hl + inc de + inc de + inc de + dec ixl + jp nz,loop2 + pop ix + pop hl ; return value HL = start+A*96+16 +exit: + pop af ; ZF and original RAM bank when FUNCTION was called + call nz,_SetBankPreservingINTs + ret +full: + ld hl,0 + jr exit + ENDP +END ASM +END FUNCTION + + +' ---------------------------------------------------------------- +' Save background and Draw sprite registered in the MSFS +' Parameters: +' UByte: X coordinate (0:left to 240:right) +' UByte: Y coordinate (0:up to 176:down) +' UInteger: address where background will be saved +' UInteger: registry number in the MSFS for the spriteImage +' ---------------------------------------------------------------- +SUB FASTCALL SaveBackgroundAndDrawSpriteRegisteredInMSFS(X AS UByte, Y AS UByte, backgroundAddr AS UInteger, spriteImageReg AS UInteger) +ASM + PROC + LOCAL full, makeShiftedImage, loopMSI, loopMSI1 + LOCAL useShiftedImage, loopUSI, branchUSI, exitUSI + LOCAL noshift, loopNS, branchNS, exitNS + ; A = X + pop de ; returnAddr + exx + pop bc ; B = Y + ld c,a ; C = X +; BEGIN code from https://skoolkid.github.io/rom/asm/22AA.html + rlca + rlca + rlca ; A = %c4c3c2c1c0c7c6c5 + xor b + and %11000111 + xor b ; A = %c4c3b5b4b3c7c6c5 + rlca + rlca + ld e,a ; E = %b5b4b3c7c6c5c4c3 + ld a,b + and %11111000 + rra + rra + rra ; A = %.0.0.0b7b6b5b4b3 + xor b + and %11111000 + xor b + ld d,a ; D = %.0.0.0b7b6b2b1b0 +; END code from https://skoolkid.github.io/rom/asm/22AA.html + ld hl,(.core.SCREEN_ADDR) + add hl,de + ex de,hl ; DE = screenAddr where drawing will start + ld a,c; ; A = X, keep it secret, keep it safe + pop bc ; BC = backgroundAddr + pop hl ; HL = spriteImageReg + exx + push de ; returnAddr + exx +; stack is empty. Now we will push data to be preserved + push ix + ld ixh,16 ; 16 scanlines + and 7 + jr z,noshift ; jump if X is a multiple of 8 (unlikely) + push de ; DE = screenAddr where drawing will start + push bc ; BC = backgroundAddr + ld b,0 + ld c,a ; BC = A = 1,2,3,4,5,6,7 + add hl,bc + add hl,bc ; HL = start+n*96+16+C*2 DEFW address of block for image shifted C pixels, or 0 if not used + ld e,(hl) + inc hl + ld d,(hl) ; DE = address for image shifted C pixels, or 0 if not used + ld a,d + or e + jp z,makeShiftedImage +;UseShiftedImage (USI) +useShiftedImage: + ex de,hl ; HL = address for image shifted C pixels + pop bc ; BC = backgroundAddr + pop de ; DE = screenAddr where drawing will start +#ifdef MaskedSprites_USE_STACK_TRANSFER + ld a,i ; IFF2=0/1=DI/EI is saved in PF=0/1=Odd/Even + jp pe,1f ; if PF=Even=1, it is sure that IFF2=1=EI + ld a,i ; read IFF2 again to ensure that IFF2=0=DI +1: ex af,af' + di + ld (exitUSI+1),sp + ld sp,hl +#endif +loopUSI: + ld a,(de) ; screen + ld (bc),a; ; save + inc bc +#ifdef MaskedSprites_USE_STACK_TRANSFER + pop hl + and l ; mask + or h ; graph +#else + and (hl); ; mask + inc l + or (hl) ; graph + inc hl +#endif + ld (de),a ; 1st byte done + inc e + + ld a,(de) ; screen + ld (bc),a; ; save + inc bc +#ifdef MaskedSprites_USE_STACK_TRANSFER + pop hl + and l ; mask + or h ; graph +#else + and (hl); ; mask + inc l + or (hl) ; graph + inc hl +#endif + ld (de),a ; 2nd byte done + inc e + + ld a,(de) ; screen + ld (bc),a; ; save + inc bc +#ifdef MaskedSprites_USE_STACK_TRANSFER + pop hl + and l ; mask + or h ; graph +#else + and (hl); ; mask + inc l + or (hl) ; graph + inc hl +#endif + ld (de),a ; 3rd byte done + dec e + dec e + + inc d + ld a,d + and 7 + jr z,branchUSI ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + dec ixh + jp nz,loopUSI +exitUSI: +#ifdef MaskedSprites_USE_STACK_TRANSFER + ld sp,$1234 + pop ix + ex af,af' + ret po ; Return with DI if IFF2=0=DI at the beginning + ei ; Return with EI if IFF2=1=EI at the beginning + ret +#else + pop ix + ret +#endif +branchUSI: + MaskedSprites_NEXT_ROW + dec ixh + jp nz,loopUSI +#ifdef MaskedSprites_USE_STACK_TRANSFER + jp exitUSI +#else + pop ix + ret +#endif +;NoShift (NS) +noshift: + ld a,(hl) ; HL = spriteImageReg + inc hl + ld h,(hl) + ld l,a ; HL = start of unshifted image +#ifdef MaskedSprites_USE_STACK_TRANSFER + ld a,i ; IFF2=0/1=DI/EI is saved in PF=0/1=Odd/Even + jp pe,1f ; if PF=Even=1, it is sure that IFF2=1=EI + ld a,i ; read IFF2 again to ensure that IFF2=0=DI +1: ex af,af' + di + ld (exitNS+1),sp + ld sp,hl +#endif +loopNS: + ld a,(de) ; screen + ld (bc),a; ; save + inc bc +#ifdef MaskedSprites_USE_STACK_TRANSFER + pop hl + and l ; mask + or h ; graph +#else + and (hl); ; mask + inc l + or (hl) ; graph + inc hl +#endif + ld (de),a ; 1st byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc +#ifdef MaskedSprites_USE_STACK_TRANSFER + pop hl + and l ; mask + or h ; graph +#else + and (hl); ; mask + inc l + or (hl) ; graph + inc hl +#endif + ld (de),a ; 2nd byte done + dec e + + inc d + ld a,d + and 7 + jr z,branchNS ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + dec ixh + jp nz,loopNS +exitNS: +#ifdef MaskedSprites_USE_STACK_TRANSFER + ld sp,$1234 + pop ix + ex af,af' + ret po ; Return with DI if IFF2=0=DI at the beginning + ei ; Return with EI if IFF2=1=EI at the beginning + ret +#else + pop ix + ret +#endif +branchNS: + MaskedSprites_NEXT_ROW + dec ixh + jp nz,loopNS +#ifdef MaskedSprites_USE_STACK_TRANSFER + jp exitNS +#else + pop ix + ret +#endif +;MakeShiftedImage (MSI) +makeShiftedImage: + ld a,8 + sub c ; C = X MOD 8 = 1,2,3,4,5,6,7 to the right + ld ixl,a ; IXl = 8 - C = 7,6,5,4,3,2,1 to the left + push hl ; HL = start+n*96+16+C*2 + 1 + scf + call FindFirstUnusedBlockInMSFS ; and book it (SCF) + jr c,full + ex de,hl ; DE = start+m*96 = address for the shiftedimage-to-be + pop hl ; HL = start+n*96+16+C*2 + 1 + ld (hl),d + dec hl + ld (hl),e; ; HL = start+n*96+16+C*2 DEFW address for the shifted image + ld b,0 + sbc hl,bc + sbc hl,bc + ld a,(hl) ; HL = spriteImageReg + inc hl + ld h,(hl) + ld l,a ; HL = start of unshifted image + push de ; DE = address for image shifted C pixels + push de ; two PUSH because we will POP one just before useShiftedImage + exx + pop hl ; HL' = address for the shiftedimage-to-be + exx +loopMSI: + ld a,(hl) ; mask1 + inc hl + ld c,(hl) ; graph1 + inc hl + ld d,(hl) ; mask2 + inc hl + ld e,(hl) ; graph2 + inc hl + push hl ; spriteImageAddr += 4 + ld hl,$FF00 ; H = 255 , L = 0 + ld b,ixl +loopMSI1: + sll d ; 8; ShiftLeftLogical injects a 1 in bit0 of D + rla ; 4 + rl h ; 8 + sla e ; 8; ShiftLeftArithmetic injects a 0 in bit0 of E + rl c ; 8 + rl l ; 8 + djnz loopMSI1; 8+4+8+8+8+8 = 44 Ts + ld b,a + push de ; D,E = mask,graph 3rd byte + push bc ; B,D = mask,graph 2nd byte + push hl ; H,L = mask,graph 1st byte + exx + pop de ; D',E' = mask,graph 1st byte + ld (hl),d + inc hl + ld (hl),e + inc hl + pop de ; D',E' = mask,graph 2nd byte + ld (hl),d + inc hl + ld (hl),e + inc hl + pop de ; D',E' = mask,graph 3rd byte + ld (hl),d + inc hl + ld (hl),e + inc hl ; HL' += 6 in this loop + exx + pop hl ; spriteImageAddr + dec ixh + jp nz,loopMSI + pop de ; DE = address for image shifted C pixels + ld ixh,16 ; 16 scanlines + jp useShiftedImage +full: + pop hl ; HL = start+n*96+16+C*2 + 1 + pop bc ; BC = backgroundAddr + pop de ; DE = screenAddr where drawing will start + pop ix + ret + ENDP +END ASM +END SUB + + +#endif + diff --git a/src/lib/arch/zx81sd/stdlib/dbuf.bas b/src/lib/arch/zx81sd/stdlib/dbuf.bas new file mode 100644 index 000000000..39a7453da --- /dev/null +++ b/src/lib/arch/zx81sd/stdlib/dbuf.bas @@ -0,0 +1,80 @@ +' SD81 Booster — double buffer (present-blit) control, port $E7 pseudo-block 8. +' +' Hardware spec (SD81 Booster, ay author): while enabled, the video +' stops reading the live screen block (HFILE) and instead scans out a +' private front buffer inside the FPGA's own screen shadow RAM. On every +' vertical blanking period the hardware copies the live screen into that +' front buffer, so what's on screen is always a complete snapshot taken +' at the last VSYNC -- no tearing even if the program erases/redraws in +' the middle of a scan. +' +' Two control paths exist on real hardware: POKE 2057 (memory-mapped IO) +' and this port $E7 pseudo-block-8 path. zx81sd's boot (tools/boot1.asm) +' always disables memory-mapped IO (POKE 2056,0) and leaves the $E7 +' mapper in full-paging mode before jumping to the compiled program, so +' every zx81sd binary is in exactly the state the port path needs -- the +' POKE 2057 path is NOT usable from zxbasic (memory-mapped IO is off). +' +' Front buffer block: this is NOT one of the program's own blocks 0-5 +' (code/data, mapped via boot1.asm/vectors.asm) nor block 6 (the live +' screen, $C000) nor block 7 (data banking, see Map() in mcu.bas) -- the +' front buffer lives in the FPGA's private screen shadow RAM, addressed +' with its own independent 0-7 block index. The hardware spec recommends +' 4 or 5 and explicitly says to avoid 0-3 and 6-7. +' +' Untested on the port $E7 path as of this writing (the POKE 2057 path +' was the one validated on real hardware) -- this library is meant to be +' its first test. See examples/sd81/bounce_sd81.bas. + +' Turns the double buffer ON. frontBlk: 0-7, see notes above (4 or 5 +' recommended). +sub fastcall SD81DBufOn(frontBlk as ubyte) + asm + ; A = frontBlk (fastcall) + and 7 + or 32 ; bit5 = enable + ld b, a + ld a, 8 ; pseudo-block 8 (D3=1, D2:0=0) + ld c, $E7 + out (c), a + end asm +end sub + +' Turns the double buffer OFF. +sub SD81DBufOff() + asm + ld b, 0 + ld a, 8 + ld c, $E7 + out (c), a + end asm +end sub + +' Waits for the rising edge of VSYNC (bit 0 of port $AF): if currently +' inside the vsync pulse, waits for it to end, then waits for the next +' one to start. This is the point right after the double buffer's +' automatic blit finishes (the copy starts as soon as the visible area +' ends), so it's the right place to erase/redraw when using SD81DBufOn. +' +' Note: this is a different wait than VSYNC_TICK/PAUSE (which just waits +' for at least one pulse since the last read, using bits 6-1 as a pulse +' counter) -- this one specifically detects the edge to align with the +' double buffer's blit timing. +sub SD81WaitVSync() + asm + PROC + LOCAL SDVS_INSIDE, SDVS_WAIT + +SDVS_INSIDE: + in a, ($AF) + rrca + jr c, SDVS_INSIDE ; still inside the vsync pulse: wait for it to end + +SDVS_WAIT: + in a, ($AF) + rrca + jr nc, SDVS_WAIT ; wait for the next pulse to start + + ENDP + end asm +end sub diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/academy.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/academy.fzx new file mode 100644 index 000000000..a2a7f2756 Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/academy.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/belegost1.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/belegost1.fzx new file mode 100644 index 000000000..f77eaf27b Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/belegost1.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/belegost2.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/belegost2.fzx new file mode 100644 index 000000000..08c1cde10 Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/belegost2.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/bigbold.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/bigbold.fzx new file mode 100644 index 000000000..3f5ce6556 Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/bigbold.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/cobra.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/cobra.fzx new file mode 100644 index 000000000..e56c75498 Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/cobra.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/crash.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/crash.fzx new file mode 100644 index 000000000..5ab6ba53f Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/crash.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/d_o_c.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/d_o_c.fzx new file mode 100644 index 000000000..e67f4edfc Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/d_o_c.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/eclipse.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/eclipse.fzx new file mode 100644 index 000000000..d6a3e4b2e Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/eclipse.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/extra.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/extra.fzx new file mode 100644 index 000000000..d7961f77c Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/extra.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/hijack.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/hijack.fzx new file mode 100644 index 000000000..e365183dd Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/hijack.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/italika.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/italika.fzx new file mode 100644 index 000000000..a252f264b Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/italika.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/just6.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/just6.fzx new file mode 100644 index 000000000..709689c25 Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/just6.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/locomotion.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/locomotion.fzx new file mode 100644 index 000000000..e22bc25de Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/locomotion.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/midnight.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/midnight.fzx new file mode 100644 index 000000000..7c08a9637 Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/midnight.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/moonalert.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/moonalert.fzx new file mode 100644 index 000000000..ff4a59f02 Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/moonalert.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/nether.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/nether.fzx new file mode 100644 index 000000000..4045e4b4b Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/nether.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/neverend.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/neverend.fzx new file mode 100644 index 000000000..1b598e042 Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/neverend.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/roman.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/roman.fzx new file mode 100644 index 000000000..965e28676 Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/roman.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/script.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/script.fzx new file mode 100644 index 000000000..411de3443 Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/script.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/script2.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/script2.fzx new file mode 100644 index 000000000..2ac2fb3b0 Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/script2.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/standard.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/standard.fzx new file mode 100644 index 000000000..0fd4024fa Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/standard.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/tomahawk.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/tomahawk.fzx new file mode 100644 index 000000000..67592d045 Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/tomahawk.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/ultrabold.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/ultrabold.fzx new file mode 100644 index 000000000..f7fecd495 Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/ultrabold.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/upcasebold.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/upcasebold.fzx new file mode 100644 index 000000000..7ba37c2bf Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/upcasebold.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/wildvest.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/wildvest.fzx new file mode 100644 index 000000000..3dc5c7880 Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/wildvest.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/fzx_fonts/winter.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/winter.fzx new file mode 100644 index 000000000..adc7e36d2 Binary files /dev/null and b/src/lib/arch/zx81sd/stdlib/fzx_fonts/winter.fzx differ diff --git a/src/lib/arch/zx81sd/stdlib/input.bas b/src/lib/arch/zx81sd/stdlib/input.bas new file mode 100644 index 000000000..13320a11c --- /dev/null +++ b/src/lib/arch/zx81sd/stdlib/input.bas @@ -0,0 +1,197 @@ +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' +' Simple INPUT routine (not as powerful as Sinclair BASIC's). +' Usage: A$ = INPUT(MaxChars) +' +' Version zx81sd: la version de zx48k depende de la ROM del ZX +' Spectrum (system var LAST_K actualizada por interrupcion, y la +' rutina BEEPER de la ROM). En zx81sd no hay ROM mapeada en tiempo +' de ejecucion y las interrupciones estan deshabilitadas (DI), asi +' que no existe ningun mecanismo que actualice LAST_K por si solo: +' el teclado se escanea directamente bajo demanda (ver +' runtime/io/keyboard/keyscan.asm). El pitido de tecla usa el +' beeper emulado del SD81 Booster en modo Spectrum (puerto ULA +' $FBh, ver runtime/beep.asm), no la rutina BEEPER de la ROM. +' ---------------------------------------------------------------- +#ifndef __LIBRARY_INPUT__ + +REM Avoid recursive / multiple inclusion + +#define __LIBRARY_INPUT__ + +#include once +#include once + +#pragma push(case_insensitive) +#pragma case_insensitive = True + +' ------------------------------------------------------------------ +' Function 'PRIVATE' to this module. +' Escanea el teclado hasta que se pulsa una tecla, y hasta que se +' suelta antes de devolver su codigo ASCII (evita que una pulsacion +' se registre varias veces mientras dura el bucle DO/LOOP). +' ------------------------------------------------------------------ +FUNCTION FASTCALL PRIVATEInputWaitKey AS UBYTE + ASM + push namespace core + PROC + LOCAL WAIT_PRESS + LOCAL WAIT_RELEASE + +WAIT_PRESS: + call __ZX81SD_KEYSCAN + or a + jr z, WAIT_PRESS + + ld b, a + push bc ; __ZX81SD_KEYCLICK usa B como contador de retardo + call __ZX81SD_KEYCLICK + pop bc + +WAIT_RELEASE: + push bc + call __ZX81SD_KEYSCAN + pop bc + or a + jr nz, WAIT_RELEASE + + ld a, b + ENDP + pop namespace + END ASM +END FUNCTION +#require "io/keyboard/keyscan.asm" +#require "beep.asm" + +' ------------------------------------------------------------------ +' Function 'PRIVATE' to this module. +' Dado el codigo ASCII de una tecla normal (sin modificadores), da el +' simbolo del ZX81 que le corresponde bajo el antiguo SHIFT del ZX81 +' (o 0 si no tiene). Ver runtime/io/keyboard/keyscan.asm: SHIFT se ha +' redefinido para dar mayuscula, asi que esos simbolos ya no se pueden +' teclear pulsando dos teclas a la vez (no da tiempo entre pulsaciones +' humanas, sobre todo leyendo desde INPUT); en su lugar, aqui se pulsan +' de manera secuencial: primero "." y luego la tecla del simbolo. +' ------------------------------------------------------------------ +FUNCTION FASTCALL PRIVATEInputSymbolFor(ch AS UByte) AS UByte + ASM + push namespace core + call __ZX81SD_SYMBOL_FOR + pop namespace + END ASM +END FUNCTION + + +FUNCTION input(MaxLen AS UINTEGER) AS STRING + DIM result$ AS STRING + DIM i as UINTEGER + DIM LastK as UByte + DIM sym as UByte + + result$ = "" + + DO + PRIVATEInputShowCursor() + + REM Espera a que se pulse una tecla y a que se suelte (debounce) + LastK = PRIVATEInputWaitKey() + + PRIVATEInputHideCursor() + + IF LastK = CODE(".") THEN + REM Posible simbolo compuesto: "." seguido de otra tecla, + REM pulsadas una detras de otra (no a la vez). Se lee la + REM siguiente tecla y se comprueba si tiene simbolo. + PRIVATEInputShowCursor() + LastK = PRIVATEInputWaitKey() + PRIVATEInputHideCursor() + + IF LastK = CODE(".") THEN + REM Punto dos veces seguidas: confirma el primer punto + REM como literal y descarta el combo (la coma ya sale + REM directamente con SHIFT+"."). La segunda pulsacion + REM se consume sin mas: no abre un nuevo combo ni se + REM imprime por si misma, asi la tecla que venga a + REM continuacion se lee como una pulsacion nueva, sin + REM combinar - permite escribir un punto delante de una + REM letra que de otro modo formaria simbolo con ella. + IF LEN(result$) < MaxLen THEN + LET result$ = result$ + "." + PRINT "."; + END IF + LET LastK = 0 + ELSE + REM sym=12 (RUBOUT, tecla "0") se excluye a proposito: ya + REM se alcanza comodamente con SHIFT+0, y aceptarlo aqui + REM haria que escribir un numero decimal terminado en + REM ".0" (muy habitual) borrase el caracter anterior en + REM vez de escribir el "0". + sym = PRIVATEInputSymbolFor(LastK) + IF sym <> 0 AND sym <> 12 THEN + LET LastK = sym + ELSEIF LEN(result$) < MaxLen THEN + LET result$ = result$ + "." + PRINT "."; + END IF + REM Si no habia simbolo valido, LastK sigue siendo la + REM segunda tecla real y se procesa a continuacion con + REM normalidad: DEL, ENTER, o un caracter mas. + END IF + END IF + + IF LastK = 12 THEN + IF LEN(result$) THEN REM "Del" key code is 12 + IF LEN(result$) = 1 THEN + LET result$ = "" + ELSE + LET result$ = result$( TO LEN(result$) - 2) + END IF + PRINT CHR$(8); + END IF + ELSEIF LastK >= CODE(" ") AND LEN(result$) < MaxLen THEN + LET result$ = result$ + CHR$(LastK) + PRINT CHR$(LastK); + END IF + + LOOP UNTIL LastK = 13 : REM "Enter" key code is 13 + + FOR i = 1 TO LEN(result$): + PRINT OVER 0; CHR$(8) + " " + chr$(8); + NEXT + + RETURN result$ + +END FUNCTION + +#pragma pop(case_insensitive) + +' ------------------------------------------------------------------ +' Function 'PRIVATE' to this module. +' Shows a flashing cursor +' ------------------------------------------------------------------ +SUB PRIVATEInputShowCursor + REM Print a Flashing cursor at current print position + DIM x, y as UBYTE + y = csrlin() + x = pos() + PRINT AT y, x; OVER 0; FLASH 1; " "; AT y, x; +END SUB + + +' ------------------------------------------------------------------ +' Function 'PRIVATE' to this module. +' Hides the flashing cursor +' ------------------------------------------------------------------ +SUB PRIVATEInputHideCursor + REM Print a Flashing cursor at current print position + DIM x, y as UBYTE + y = csrlin() + x = pos() + PRINT AT y, x; OVER 0; FLASH 0; " "; AT y, x; +END SUB + +#endif diff --git a/src/lib/arch/zx81sd/stdlib/joy.bas b/src/lib/arch/zx81sd/stdlib/joy.bas new file mode 100644 index 000000000..2e48f8425 --- /dev/null +++ b/src/lib/arch/zx81sd/stdlib/joy.bas @@ -0,0 +1,77 @@ +' ---------------------------------------------------------------------- +' joy.bas (zx81sd) — Configuracion del joystick programable del SD81 +' +' El SD81 Booster mapea el joystick fisico a pulsaciones de teclado. +' El mapeo se configura con el comando 21 ($15) del MCU, que recibe 5 +' codigos de tecla ZX81: arriba, abajo, izquierda, derecha y fuego. +' +' Uso: +' #include +' dim st as ubyte +' st = Joy("QAOPM") ' arriba=Q abajo=A izq=O der=P fuego=M +' st = Joy("7657 ") ' cursores de Sinclair (fuego=espacio) +' +' La cadena debe tener exactamente 5 caracteres, en el orden +' ARRIBA, ABAJO, IZQUIERDA, DERECHA, FUEGO. Caracteres validos: +' letras (A-Z, indistinto mayus/minus), digitos (0-9) y espacio. +' +' Devuelve el byte de estado del MCU: 0 = OK, 14 = parametro invalido. +' Si la cadena es invalida (longitud o caracteres) devuelve 14 sin +' llegar a llamar al MCU. +' ---------------------------------------------------------------------- + +#pragma once + +#include + +#pragma push(explicit) +#pragma explicit = true + +const _JOY_CMD as ubyte = $15 ' comando 21 del MCU: configurar joystick + +' Convierte un caracter ASCII al codigo de tecla ZX81 que espera el MCU +' (0 = espacio, 28-37 = digitos, 38-63 = letras). +' Devuelve 255 si el caracter no corresponde a ninguna tecla valida. +function _JoyKeyCode(c as ubyte) as ubyte + if c = code(" ") then + return 0 + end if + if c >= code("0") and c <= code("9") then + return 28 + (c - code("0")) + end if + if c >= code("a") and c <= code("z") then + c = c - 32 + end if + if c >= code("A") and c <= code("Z") then + return 38 + (c - code("A")) + end if + return 255 +end function + +' Configura el mapeo del joystick programable. +' keys: 5 caracteres en orden ARRIBA, ABAJO, IZQUIERDA, DERECHA, FUEGO. +' Devuelve el estado del MCU (0 = OK). +function Joy(keys as string) as ubyte + dim i as ubyte + dim kc as ubyte + dim zxcodes as string + + if len(keys) <> 5 then + return 14 ' mismo codigo que usa el MCU + end if + + ' valida y convierte ANTES de tocar el MCU, para no dejar el + ' protocolo a medias si hay un caracter invalido + zxcodes = "" + for i = 0 to 4 + kc = _JoyKeyCode(code(keys(i))) + if kc = 255 then + return 14 + end if + zxcodes = zxcodes + chr(kc) + next i + + return McuCmdStr(_JOY_CMD, zxcodes) +end function + +#pragma pop(explicit) diff --git a/src/lib/arch/zx81sd/stdlib/mcu.bas b/src/lib/arch/zx81sd/stdlib/mcu.bas new file mode 100644 index 000000000..1a7df9966 --- /dev/null +++ b/src/lib/arch/zx81sd/stdlib/mcu.bas @@ -0,0 +1,904 @@ +' ---------------------------------------------------------------------- +' mcu.bas (zx81sd) — Comunicacion con el MCU del SD81 Booster +' +' Primitivas del protocolo + wrappers de los comandos del MCU. +' Los comandos con nombre propio (joy.bas, etc.) se apoyan en esta +' libreria. +' +' Protocolo (puertos $A7 datos / $AF reloj): +' El bit 7 del puerto $AF es el reloj de sincronizacion: el MCU lo +' invierte cuando ha procesado cada lectura/escritura del puerto de +' datos $A7. Tras cada operacion en $A7 hay que esperar ese cambio +' antes de la siguiente. +' +' ATENCION: escribir CUALQUIER valor en $AF resetea el MCU (peligroso +' si esta escribiendo en la SD). Esta libreria solo LEE $AF. +' +' Las transferencias de datos estan implementadas en ensamblador (son +' el camino critico de LOAD/SAVE/F_READ/F_WRITE); el BASIC solo prepara +' parametros y convierte codificaciones. +' +' Codificacion: los comandos de fichero/texto viajan en codigos de +' caracter ZX81 (el MCU los convierte internamente). Esta libreria +' convierte automaticamente desde ASCII. Excepciones ya en crudo: +' F_OPEN (ASCII), BINARY_SAY (alofonos) y JOY (codigos de tecla). +' +' Convencion de resultados: las funciones devuelven el byte de estado +' del MCU (0 = exito; ver tabla de errores del manual). Las que +' devuelven datos (McuLoad, McuPwd, ...) dejan el estado en la variable +' global McuStatus. +' ---------------------------------------------------------------------- + +#pragma once + +#pragma push(explicit) +#pragma explicit = true + +' Estado devuelto por el ultimo comando "con datos" (McuLoad, McuPwd...) +dim McuStatus as ubyte + +' Resultado de McuFree() en KB +dim McuFreeTotalKb as ulong +dim McuFreeFreeKb as ulong + +' ====================================================================== +' PRIMITIVAS DEL PROTOCOLO (ASM) +' ====================================================================== + +' Envia un byte al MCU y espera a que lo procese. +sub fastcall McuSend(value as ubyte) + asm + PROC + LOCAL WAIT + ; A = value (convencion fastcall para ubyte) + push bc + ld b, a ; salva el dato + in a, ($AF) + ld c, a ; C = estado previo del reloj (bit 7) + ld a, b + out ($A7), a ; envia + WAIT: + in a, ($AF) + xor c + jp p, WAIT ; espera a que el bit 7 cambie + pop bc + ENDP + end asm +end sub + +' Lee un byte del MCU (respuesta o status) y espera el toggle posterior. +function fastcall McuRecv() as ubyte + asm + PROC + LOCAL WAIT + push bc + in a, ($AF) + ld c, a ; reloj previo + in a, ($A7) ; lee el dato (provoca el toggle del MCU) + ld b, a + WAIT: + in a, ($AF) + xor c + jp p, WAIT + ld a, b ; devuelve el dato en A (fastcall ubyte) + pop bc + ENDP + end asm +end function + +' Envia un bloque de memoria al MCU (un byte por transaccion de reloj). +' Bucle integramente en ASM: camino critico de SAVE / F_WRITE. +sub fastcall McuSendBlock(addr as uinteger, size as uinteger) + asm + PROC + LOCAL LOOP, WAIT, DONE + ; HL = addr (fastcall); pila: [ret][size] + pop de ; DE = direccion de retorno + pop bc ; BC = size + push de ; restaura el retorno + + ld a, b + or c + jr z, DONE + LOOP: + in a, ($AF) + ld e, a ; E = reloj previo + ld a, (hl) + out ($A7), a + WAIT: + in a, ($AF) + xor e + jp p, WAIT + inc hl + dec bc + ld a, b + or c + jr nz, LOOP + DONE: + ENDP + end asm +end sub + +' Recibe un bloque del MCU en memoria (un byte por transaccion de reloj). +' Bucle integramente en ASM: camino critico de LOAD / F_READ. +sub fastcall McuRecvBlock(addr as uinteger, size as uinteger) + asm + PROC + LOCAL LOOP, WAIT, DONE + ; HL = addr (fastcall); pila: [ret][size] + pop de + pop bc + push de + + ld a, b + or c + jr z, DONE + LOOP: + in a, ($AF) + ld e, a ; reloj previo + in a, ($A7) ; lee dato + ld (hl), a + WAIT: + in a, ($AF) + xor e + jp p, WAIT + inc hl + dec bc + ld a, b + or c + jr nz, LOOP + DONE: + ENDP + end asm +end sub + +' ====================================================================== +' CONVERSION ASCII <-> CODIGOS DE CARACTER ZX81 +' ====================================================================== + +' ASCII -> codigo ZX81. Caracteres sin equivalente -> '?' ($0F). +function _McuToZx(c as ubyte) as ubyte + if c >= 97 and c <= 122 then + c = c - 32 ' minusculas a mayusculas + end if + if c >= 65 and c <= 90 then + return 38 + (c - 65) ' A-Z + end if + if c >= 48 and c <= 57 then + return 28 + (c - 48) ' 0-9 + end if + if c = 32 then return 0 ' espacio + if c = 34 then return 11 ' " + if c = 36 then return 13 ' $ + if c = 58 then return 14 ' : + if c = 63 then return 15 ' ? + if c = 40 then return 16 ' ( + if c = 41 then return 17 ' ) + if c = 62 then return 18 ' > + if c = 60 then return 19 ' < + if c = 61 then return 20 ' = + if c = 43 then return 21 ' + + if c = 45 then return 22 ' - + if c = 42 then return 23 ' * + if c = 47 then return 24 ' / + if c = 59 then return 25 ' ; + if c = 44 then return 26 ' , + if c = 46 then return 27 ' . + return 15 ' resto: '?' +end function + +' Codigo ZX81 -> ASCII (ignora el bit 7 de video inverso). +function _McuFromZx(z as ubyte) as ubyte + z = z band 127 + if z >= 38 and z <= 63 then + return 65 + (z - 38) ' A-Z + end if + if z >= 28 and z <= 37 then + return 48 + (z - 28) ' 0-9 + end if + if z = 0 then return 32 ' espacio + if z = 11 then return 34 ' " + if z = 13 then return 36 ' $ + if z = 14 then return 58 ' : + if z = 15 then return 63 ' ? + if z = 16 then return 40 ' ( + if z = 17 then return 41 ' ) + if z = 18 then return 62 ' > + if z = 19 then return 60 ' < + if z = 20 then return 61 ' = + if z = 21 then return 43 ' + + if z = 22 then return 45 ' - + if z = 23 then return 42 ' * + if z = 24 then return 47 ' / + if z = 25 then return 59 ' ; + if z = 26 then return 44 ' , + if z = 27 then return 46 ' . + return 63 ' resto: '?' +end function + +' Convierte una cadena ASCII completa a codigos ZX81. +function McuZxStr(s as string) as string + dim i as uinteger + dim r as string + r = "" + for i = 0 to len(s) - 1 + r = r + chr(_McuToZx(code(s(i)))) + next i + return r +end function + +' ====================================================================== +' PATRONES GENERICOS +' ====================================================================== + +' Envia una cadena Pascal (longitud + bytes crudos, max 255). +sub _McuSendPascal(dat as string) + dim addr as uinteger + dim l as uinteger + addr = peek(uinteger, @dat) + l = peek(uinteger, addr) + if l > 255 then l = 255 + McuSend(cast(ubyte, l)) + if l > 0 then + McuSendBlock(addr + 2, l) + end if +end sub + +' Comando sin parametros ni respuesta. +sub McuCmd(cmd as ubyte) + McuSend(cmd) +end sub + +' Comando + cadena en crudo + status. +function McuCmdStr(cmd as ubyte, dat as string) as ubyte + McuSend(cmd) + _McuSendPascal(dat) + return McuRecv() +end function + +' Comando + cadena (convertida a ZX81) + status. Para comandos de +' fichero/texto (CD, DEL, LOAD, SAY...). +function McuCmdStrZx(cmd as ubyte, dat as string) as ubyte + return McuCmdStr(cmd, McuZxStr(dat)) +end function + +' Recibe un stream de caracteres (protocolo NEXTCH $0D hasta EOT $6F) +' y lo devuelve como string ASCII. $76 (newline ZX81) -> CHR(13). +' Deja el status en McuStatus. Para respuestas cortas (PWD, RTC...). +function _McuRecvStream() as string + dim c as ubyte + dim r as string + r = "" + do + McuSend($0D) ' CMD_NEXTCH: pide un caracter + c = McuRecv() + if c = $6F then ' EOT + exit do + end if + if c = $76 then + r = r + chr(13) ' nueva linea ZX81 + else + r = r + chr(_McuFromZx(c)) + end if + loop + McuStatus = McuRecv() ' byte de estado final + return r +end function + +' Recibe un stream y lo imprime linea a linea (para listados largos: +' DIR, TYPE, FREE_TXT). Devuelve el status. +function _McuStreamPrint(newLine as ubyte) as ubyte + dim c as ubyte + dim line as string + line = "" + do + McuSend($0D) + c = McuRecv() + if c = $6F then + exit do + end if + if c = $76 then + ' print + print line; + if len(line) <> 32 then print + line = "" + else + line = line + chr(_McuFromZx(c)) + 'print c; " "; + end if + loop + if len(line) > 0 then + if newLine=1 then + print line + else + print line; + end if + end if + return McuRecv() +end function + +' ====================================================================== +' COMANDOS DE SISTEMA +' ====================================================================== + +' Cmd 0: sin operacion (sincroniza el reloj). +sub McuNop() + McuCmd(0) +end sub + +' Cmd 1: version del firmware del MCU. +function McuVersion() as ubyte + McuSend(1) + return McuRecv() +end function + +' Cmd 32: lee un byte de la memoria interna del MCU. +' Indices 0-127: variables volatiles. 128-255: EEPROM (persistente). +function McuGetByte(index as ubyte) as ubyte + McuSend(32) + McuSend(index) + return McuRecv() +end function + +' Cmd 33: escribe un byte en la memoria interna del MCU. +sub McuSetByte(index as ubyte, value as ubyte) + McuSend(33) + McuSend(index) + McuSend(value) +end sub + +' ====================================================================== +' COMANDOS DE SISTEMA DE ARCHIVOS +' ====================================================================== + +' Cmd 2: directorio actual. Status en McuStatus. +function McuPwd() as string + McuSend(2) + return _McuRecvStream() +end function + +' Cmd 3: cambia de directorio (rutas absolutas con / o relativas). +function McuCd(path as string) as ubyte + return McuCmdStrZx(3, path) +end function + +' Cmd 4: borra un archivo del directorio actual. +function McuDel(fname as string) as ubyte + return McuCmdStrZx(4, fname) +end function + +' Cmd 5: crea un subdirectorio. +function McuMkdir(dname as string) as ubyte + return McuCmdStrZx(5, dname) +end function + +' Cmd 6: elimina un directorio vacio. +function McuRmdir(dname as string) as ubyte + return McuCmdStrZx(6, dname) +end function + +' Cmd 7/8: renombra-mueve / copia un archivo (dos cadenas seguidas). +function _McuTwoStr(cmd as ubyte, src as string, dst as string) as ubyte + McuSend(cmd) + _McuSendPascal(McuZxStr(src)) + _McuSendPascal(McuZxStr(dst)) + return McuRecv() +end function + +function McuMove(src as string, dst as string) as ubyte + return _McuTwoStr(7, src, dst) +end function + +function McuCopy(src as string, dst as string) as ubyte + return _McuTwoStr(8, src, dst) +end function + +' Cmd 9: carga un archivo de la SD en memoria a partir de addr. +' Devuelve el numero de bytes cargados (0 si error); status en McuStatus. +' Ojo: extensiones especiales — .ROM resetea la CPU (no retorna), +' .WAV se reproduce (devuelve 0 bytes). +function McuLoad(fname as string, addr as uinteger) as uinteger + dim lo as ubyte + dim hi as ubyte + dim size as uinteger + + McuSend(9) + _McuSendPascal(McuZxStr(fname)) + lo = McuRecv() + hi = McuRecv() + size = cast(uinteger, hi) * 256 + lo + if size > 0 then + McuRecvBlock(addr, size) + end if + McuStatus = McuRecv() + return size +end function + +' Cmd 10: guarda un bloque de memoria como archivo en la SD. +function McuSave(fname as string, addr as uinteger, size as uinteger) as ubyte + McuSend(10) + _McuSendPascal(McuZxStr(fname)) + McuSend(cast(ubyte, size band 255)) + McuSend(cast(ubyte, size >> 8)) + if size > 0 then + McuSendBlock(addr, size) + end if + return McuRecv() +end function + +' Cmd 11: imprime el contenido de un archivo de texto. +' Si el nombre empieza por '*' busca en /MAN/ con extension .TXT. +function McuTypePrint(fname as string) as ubyte + McuSend(11) + _McuSendPascal(McuZxStr(fname)) + return _McuStreamPrint(1) +end function + +' Cmd 12: imprime el listado de un directorio (admite comodines). +function McuDirPrint(mask as string) as ubyte + McuSend(12) + _McuSendPascal(McuZxStr(mask)) + return _McuStreamPrint(0) +end function + +' Cmd 14: imprime el espacio total/libre de la SD como texto. +function McuFreeTxtPrint() as ubyte + McuSend(14) + return _McuStreamPrint(1) +end function + +' Cmd 15: espacio total y libre en KB -> McuFreeTotalKb / McuFreeFreeKb. +' Devuelve el status. +function McuFree() as ubyte + dim i as ubyte + dim v as ulong + + McuSend(15) + v = 0 + for i = 0 to 3 + v = v bor (cast(ulong, McuRecv()) << (i * 8)) + next i + McuFreeTotalKb = v + v = 0 + for i = 0 to 3 + v = v bor (cast(ulong, McuRecv()) << (i * 8)) + next i + McuFreeFreeKb = v + return McuRecv() +end function + +' Cmds 16/17/18: OPENDIR / GETROWLEN / GETROW — navegador de directorio. +' ATENCION: no emulados por EightyOne a fecha de hoy (solo HW real); +' en el emulador se quedarian esperando la respuesta. +function McuOpenDir(mask as string) as ubyte + return McuCmdStrZx(16, mask) +end function + +' Devuelve la longitud del nombre de la entrada index; status en McuStatus. +function McuGetRowLen(index as uinteger) as ubyte + dim l as ubyte + McuSend(17) + McuSend(cast(ubyte, index band 255)) + McuSend(cast(ubyte, index >> 8)) + l = McuRecv() + McuStatus = McuRecv() + return l +end function + +' Devuelve el nombre de la entrada index (0 = directorio actual); +' los directorios van entre < y >. Status en McuStatus. +function McuGetRow(index as uinteger) as string + dim l as ubyte + dim i as ubyte + dim r as string + McuSend(18) + McuSend(cast(ubyte, index band 255)) + McuSend(cast(ubyte, index >> 8)) + l = McuRecv() + r = "" + if l > 0 then + for i = 1 to l + r = r + chr(_McuFromZx(McuRecv())) + next i + end if + McuStatus = McuRecv() + return r +end function + +' ====================================================================== +' ACCESO A FICHEROS GRANDES (handles 0-3, tamano 32 bits) +' ====================================================================== + +' Cmd 53: abre un fichero EXISTENTE para acceso aleatorio. El MCU asigna +' el primer handle libre y lo devuelve (0-3, o $FF si error/no existe). +' El nombre viaja en ASCII crudo (sin conversion; es el fopen de CP/M). +function McuFOpen(fname as string) as ubyte + McuSend(53) + _McuSendPascal(fname) ' ASCII directo, sin conversion + return McuRecv() +end function + +' Cmd 58: igual que McuFOpen pero el nombre viaja en codigos ZX81 +' (la conversion desde ASCII la hace esta funcion). +function McuFOpenZx(fname as string) as ubyte + McuSend(58) + _McuSendPascal(McuZxStr(fname)) + return McuRecv() +end function + +' Cmd 54: situa el puntero de lectura/escritura (offset 32 bits LE). +function McuFSeek(handle as ubyte, offset as ulong) as ubyte + dim i as ubyte + McuSend(54) + McuSend(handle) + for i = 0 to 3 + McuSend(cast(ubyte, (offset >> (i * 8)) band 255)) + next i + return McuRecv() +end function + +' Cmd 55: lee count bytes al buffer addr. Si el fichero se acaba, +' rellena con ceros (status 1 = lectura corta/EOF). +function McuFRead(handle as ubyte, addr as uinteger, count as uinteger) as ubyte + McuSend(55) + McuSend(handle) + McuSend(cast(ubyte, count band 255)) + McuSend(cast(ubyte, count >> 8)) + if count > 0 then + McuRecvBlock(addr, count) + end if + return McuRecv() +end function + +' Cmd 56: escribe count bytes desde addr. +function McuFWrite(handle as ubyte, addr as uinteger, count as uinteger) as ubyte + McuSend(56) + McuSend(handle) + McuSend(cast(ubyte, count band 255)) + McuSend(cast(ubyte, count >> 8)) + if count > 0 then + McuSendBlock(addr, count) + end if + return McuRecv() +end function + +' Cmd 57: cierra el fichero. +function McuFClose(handle as ubyte) as ubyte + McuSend(57) + McuSend(handle) + return McuRecv() +end function + +' ====================================================================== +' CONTROL DEL HARDWARE +' ====================================================================== +' ATENCION: los cambios de paginacion/RAM pueden dejar sin sentido el +' mapa de memoria del programa en ejecucion. Usar solo sabiendo lo que +' se hace. + +' Mapeador de memoria del SD81 (puerto $E7, directo a la FPGA — no pasa +' por el MCU). Equivale al LOAD *MAP bloque,pagina del BASIC. +' +' El espacio de 64K se divide en 8 bloques de 8K (bloque 0 = $0000-$1FFF +' ... bloque 7 = $E000-$FFFF); cada bloque puede apuntar a cualquier +' pagina fisica de 8K de la RAM de 512K (0-31 en paginacion simple, +' 0-63 en paginacion completa — ver McuFullPaging/McuHalfPaging). +' +' La escritura vale para AMBOS modos: pone la pagina en B (A8-A13, que +' es de donde la lee el modo completo) Y en los bits 7-3 del dato (de +' donde la lee el modo simple). +' +' CUIDADO: remapear un bloque donde vive el propio programa, las +' sysvars ($8000, bloque 4) o la pantalla ($C000, bloque 6) cuelga o +' corrompe el sistema. +sub fastcall Map(block as ubyte, page as ubyte) + asm + ; A = block (fastcall); pila: [ret][page en byte alto] + pop hl ; direccion de retorno + pop de ; D = page + push hl + and 7 + ld l, a ; L = bloque (0-7) + ld b, d ; B = pagina completa (aparece en A8-A15) + ld c, $E7 + ld a, d + and 31 + rlca + rlca + rlca ; (pagina AND 31) << 3 + or l ; dato = pagina<<3 | bloque (modo simple) + out (c), a ; una sola escritura vale para ambos modos + end asm +end sub + +' Lee la pagina actualmente asignada a un bloque (lectura de $E7 con +' el bloque en A10-A8). +function fastcall MapGet(block as ubyte) as ubyte + asm + ; A = block (fastcall) + and 7 + ld b, a ; B aparece en A8-A15 durante IN A,(C) + ld c, $E7 + in a, (c) ; A = pagina asignada (resultado fastcall) + end asm +end function + +sub McuEnableMc45() + McuCmd(19) +end sub + +sub McuDisableMc45() + McuCmd(20) +end sub + +sub McuSel128Chars() + McuCmd(27) +end sub + +sub McuSel64Chars() + McuCmd(28) +end sub + +sub McuFullPaging() + McuCmd(29) +end sub + +sub McuHalfPaging() + McuCmd(30) +end sub + +sub McuRam48On() + McuCmd(48) +end sub + +sub McuRam48Off() + McuCmd(49) +end sub + +' ====================================================================== +' SINTESIS DE VOZ +' ====================================================================== + +' Cmd 23: convierte texto a fonemas y lo reproduce. +' Si el primer caracter es '*', reproduce en background. +function McuSay(text as string) as ubyte + return McuCmdStrZx(23, text) +end function + +' Cmd 22: reproduce alofonos SP0256-AL2 en crudo (codigos $00-$3F). +' Sincrono: bloquea hasta terminar. +function McuBinarySay(allophones as string) as ubyte + return McuCmdStr(22, allophones) +end function + +' ====================================================================== +' AY POR MCU (chip 2: el AY "B" del SD81) Y PLAY EN BACKGROUND +' ====================================================================== + +' Cmd 24: escribe un registro del AY del MCU (chip 2). Sin status. +sub McuAySetReg(reg as ubyte, value as ubyte) + McuSend(24) + McuSend(reg) + McuSend(value) +end sub + +' Cmd 25: lee un registro del AY del MCU (chip 2). +function McuAyGetReg(reg as ubyte) as ubyte + McuSend(25) + McuSend(reg) + return McuRecv() +end function + +' Convierte una cadena MML de PLAY conservando la semantica de octavas +' del interprete del MCU: minuscula ASCII -> letra ZX81 normal +' (octava base - 1), MAYUSCULA ASCII -> letra ZX81 en video inverso +' (octava base). Igual que el PLAY local de play.bas. +function _McuPlayStr(s as string) as string + dim i as uinteger + dim c as ubyte + dim r as string + r = "" + for i = 0 to len(s) - 1 + c = code(s(i)) + if c >= 65 and c <= 90 then + r = r + chr(128 + 38 + (c - 65)) ' mayuscula: inverso + else + r = r + chr(_McuToZx(c)) + end if + next i + return r +end function + +' Cmd 26: PLAY de hasta 3 canales en el AY del MCU (chip 2). +' Si el canal A empieza por '*', reproduce en background. +function McuAyPlay(chanA as string, chanB as string, chanC as string) as ubyte + McuSend(26) + _McuSendPascal(_McuPlayStr(chanA)) + _McuSendPascal(_McuPlayStr(chanB)) + _McuSendPascal(_McuPlayStr(chanC)) + return McuRecv() +end function + +' ====================================================================== +' REPRODUCTOR VGM +' ====================================================================== + +' Cmd 34: abre un archivo VGM (anade .vgm si no tiene extension) y lo +' prepara; iniciar con McuContVgm(). +function McuPlayVgm(fname as string) as ubyte + return McuCmdStrZx(34, fname) +end function + +sub McuStopVgm() + McuCmd(35) +end sub + +sub McuPauseVgm() + McuCmd(36) +end sub + +sub McuContVgm() + McuCmd(37) +end sub + +' Cmd 38: modo de bucle (0 = no, 1 = si). Sin status. +sub McuLoopVgm(mode as ubyte) + McuSend(38) + McuSend(mode) +end sub + +' ====================================================================== +' PEG (Programmable Effects Generator) +' ====================================================================== + +' Cmd 40: carga instrucciones en la memoria PEG a partir de address. +' dat contiene los bytes crudos (2 bytes little-endian por instruccion). +' Sin status. +sub McuLoadPeg(address as ubyte, dat as string) + McuSend(40) + McuSend(address) + _McuSendPascal(dat) +end sub + +' Cmd 41: arranca un hilo PEG (0-2) en la direccion dada. Sin status. +sub McuPlayPeg(thread as ubyte, address as ubyte) + McuSend(41) + McuSend(thread) + McuSend(address) +end sub + +sub McuStopPeg(thread as ubyte) + McuSend(42) + McuSend(thread) +end sub + +sub McuPausePeg(thread as ubyte) + McuSend(43) + McuSend(thread) +end sub + +sub McuContPeg(thread as ubyte) + McuSend(44) + McuSend(thread) +end sub + +' Cmd 45: carga un archivo .PEB de la SD en la memoria PEG (max 512B). +function McuSdLoadPeg(fname as string, address as ubyte) as ubyte + McuSend(45) + _McuSendPascal(McuZxStr(fname)) + McuSend(address) + return McuRecv() +end function + +' ====================================================================== +' RTC Y BATERIA +' ====================================================================== + +' Cmd 50 (lectura): devuelve "AAAA-MM-DD HH:MM:SS.CC" (22 caracteres). +' Status en McuStatus. +function McuRtc() as string + dim i as ubyte + dim r as string + McuSend(50) + McuSend(0) ' cadena vacia = lectura + r = "" + for i = 1 to 22 + r = r + chr(_McuFromZx(McuRecv())) + next i + McuStatus = McuRecv() + return r +end function + +' Cmd 50 (escritura): ajusta el reloj. Formatos: "AAAA-MM-DD HH:MM:SS", +' "AAAA-MM-DD", "HH:MM:SS", "HH:MM", etc. +function McuRtcSet(datetime as string) as ubyte + return McuCmdStrZx(50, datetime) +end function + +' Cmd 52: nivel de bateria del RTC como texto "V.mmm" (5 caracteres). +' Status en McuStatus. +function McuBat() as string + dim i as ubyte + dim r as string + McuSend(52) + r = "" + for i = 1 to 5 + r = r + chr(_McuFromZx(McuRecv())) + next i + McuStatus = McuRecv() + return r +end function + +' ====================================================================== +' UTILIDADES (equivalentes de las extensiones BASIC del SD81) +' ====================================================================== +' Para *LDIR/*LDDR usar MemMove de (stdlib estandar), que +' ademas elige el sentido de copia correcto cuando hay solapamiento. +' Para *OUT/*IN y los PEEK/POKE de 16 bits, zxbasic ya tiene OUT, IN(), +' peek(uinteger, ...) y poke uinteger nativos. + +' Valor de un digito hexadecimal ASCII, o 255 si no lo es. +function _HexDigit(c as ubyte) as ubyte + if c >= 48 and c <= 57 then + return c - 48 ' 0-9 + end if + if c >= 65 and c <= 70 then + return c - 55 ' A-F + end if + if c >= 97 and c <= 102 then + return c - 87 ' a-f + end if + return 255 +end function + +' Equivalente de LOAD *HEX: vuelca una cadena hexadecimal en memoria +' ("0A014020" -> bytes $0A,$01,$40,$20 a partir de addr). +' Devuelve el numero de bytes escritos (se detiene en el primer +' caracter no hexadecimal o si la cadena tiene longitud impar). +function HexPoke(addr as uinteger, hx as string) as uinteger + dim i as uinteger + dim n as uinteger + dim hi as ubyte + dim lo as ubyte + + n = 0 + i = 0 + do while i + 2 <= len(hx) + hi = _HexDigit(code(hx(i))) + lo = _HexDigit(code(hx(i + 1))) + if hi = 255 or lo = 255 then + exit do + end if + poke addr + n, (hi << 4) bor lo + n = n + 1 + i = i + 2 + loop + return n +end function + +' Equivalente de LOAD *INV: invierte el bit 7 de todos los caracteres. +' Nota: en zxbasic el video inverso al imprimir se hace con PRINT +' INVERSE; esto sirve para preparar buffers en codificacion ZX81 +' (p.ej. cadenas para McuAyPlay o para escribir en pantalla nativa). +function StrInv(s as string) as string + dim i as uinteger + dim r as string + r = "" + for i = 0 to len(s) - 1 + r = r + chr(code(s(i)) bxor 128) + next i + return r +end function + +' Equivalente de LOAD *BOLD: fuerza a 1 el bit 7 de todos los caracteres. +function StrBold(s as string) as string + dim i as uinteger + dim r as string + r = "" + for i = 0 to len(s) - 1 + r = r + chr(code(s(i)) bor 128) + next i + return r +end function + +#pragma pop(explicit) diff --git a/src/lib/arch/zx81sd/stdlib/play.bas b/src/lib/arch/zx81sd/stdlib/play.bas new file mode 100644 index 000000000..84eab2546 --- /dev/null +++ b/src/lib/arch/zx81sd/stdlib/play.bas @@ -0,0 +1,510 @@ +' ---------------------------------------------------------------------- +' play.bas (zx81sd) — COPIA de zx48k/stdlib/play.bas adaptada al hardware +' AY del SD81 Booster (interfaz ZonX-81). Mantener sincronizada con el +' original de zx48k; los UNICOS cambios respecto a el son: +' +' 1. _PLAY_WRITE_TO_REGISTER: puertos ZonX del chip A (latch $CF, +' dato $0F) en vez de los del Spectrum 128 ($FFFD/$BFFD). +' 2. _Play_NoteDividers: tabla regenerada para reloj AY de 1.625 MHz +' (la FPGA del SD81 clockea los AY a 3.25MHz/2) en vez de los +' 1.7734 MHz del Spectrum 128. divisor = round(1625000/16/f). +' 3. CpuCyclesPerSecond: 3250000 (ZX81) en vez de 3546900 (128K), +' para que el tempo sea correcto. +' 4. Sin EI al terminar: el runtime zx81sd funciona con las +' interrupciones deshabilitadas (la FPGA genera el video). +' ---------------------------------------------------------------------- + +' ---------------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyright (C) 2026 +' by Oleg S. Kostenko (a.k.a. Ollibony) +' ---------------------------------------------------------------------- + +#pragma once + +#pragma push(explicit) +#pragma push(strict) + +#pragma explicit = true +#pragma strict = true + +' --------------------------------------------------------------------------------------------------------------------- +' Plays the given MML strings on the AY music chip. +' The syntax is compatible with the Sinclair Basic Play routine. +' The documentation can be found here: https://fizyka.umk.pl/~jacek/zx/doc/man128/sp128p09.html +' +' This is work in progress. + +' The following commands are already implemented: +' - cdefgab, CDEFGAB - gives pitch of note within current octave range +' - $ - flattens note following it +' - # - sharpens note following it +' - & - denotes a rest +' - 1-12 - sets length of notes +' - _ - creates tied notes (sums multiple durations) +' - O - followed by a number 0 to 8 sets current octave range +' - V - followed by a number 0 to 15 sets volume of notes +' - T - followed by a number 60 to 240 sets tempo of music +' - N - separates two numbers (actually, any unexpected character does this, including space) +' +' The following commands are not implemented yet: +' - W, U, X - set volume effects +' - () - repetition +' - !! - comments +' - H - stop +' - M - channel mixer control +' - Y, Z - MIDI control (also you can't now pass more than 3 parameters to Play). +' +' Notes: +' - Unlike Sinclair Basic Play routine, this one doesn't insert tiny pauses between adjacent notes. +' I consider this to be a feature, rather than a bug. +' - There are no checks for incorrect commands or parameters. Unknown commands are silently ignored, +' and incorrect parameters cause undefined behavior. +' - This routine is more flexible in the way it parses commands than Sinclair Basic Play routine. +' Some combinations that give errors in Sinclair Basic, will play fine in this implementation. +' - This sub tends to provide more accurate timings than the original Sinclair Basic Play routine. +' However, perfect timing is not guaranteed, it may fluctuate depending on the complexity of the melody. +' - There can be subtle difference in behaviour between this sub and Sinclair Play, +' especially in undocumented edge cases (such as using ties together with triplets). +' - This sub disables interrupts at the start, and enables them in the end, +' regardless of whether they were enabled or not before. +' - The strings are passed by value and thus are copied on the routine invocation. +' The memory-effective version of this routine is yet to be implemented. +' - The compiler gives warning `[W150] Parameter 'microticks' is never used`. +' This is false positive and, unfortunately, cannot be suppressed on library level. +' --------------------------------------------------------------------------------------------------------------------- +declare sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") + + +' Implementation ------------------------------------------------------------------------------------------------------ + +' How many ticks there are in a bar (a whole note). +' A tick is a single iteration of the main processing loop. +const _Play_TicksPerBar as ubyte = 96 + +const _Play_NotesPerOctave as ubyte = 12 +const _Play_TotalOctaves as ubyte = 9 + +' Maps note length to the corresponding number of ticks. +dim _Play_NoteLengthsInTicks(1 to 12) as ubyte => { _ + _Play_TicksPerBar / 16, _ '1 - semi-quaver + _Play_TicksPerBar / 16 * 1.5, _ '2 - dotted semi-quaver + _Play_TicksPerBar / 8, _ '3 - quaver + _Play_TicksPerBar / 8 * 1.5, _ '4 - dotted quaver + _Play_TicksPerBar / 4, _ '5 - crotchet + _Play_TicksPerBar / 4 * 1.5, _ '6 - dotted crotchet + _Play_TicksPerBar / 2, _ '7 - minim + _Play_TicksPerBar / 2 * 1.5, _ '8 - dotted minim + _Play_TicksPerBar, _ '9 - semi-breve + _Play_TicksPerBar / 24, _ '10 - triplet semi-quaver + _Play_TicksPerBar / 12, _ '11 - triplet quaver + _Play_TicksPerBar / 6 _ '12 - triplet crotchet +} + +' Divider values that need to be sent to the audio chip registers to play the notes. +' Note that the lowest notes in octave 0 are unplayable because of 12-bit overflow and probably wrong notes will be +' played instead of them. +' TODO: replace them with maximum possible values or zeros? See how it's done in Sinclair Play. +' TODO: in Sinclair Play it is possible to play notes in higher octaves (using several sharps in a row). +' Need to add more values to the table. +dim _Play_NoteDividers(0 to _Play_NotesPerOctave * _Play_TotalOctaves - 1) as uinteger = { _ +_ ' C C# D D# E F F# G G# A A# B + 6211, 5863, 5534, 5223, 4930, 4653, 4392, 4145, 3913, 3693, 3486, 3290, _ 'octave 0 + 3106, 2931, 2767, 2611, 2465, 2327, 2196, 2073, 1956, 1847, 1743, 1645, _ 'octave 1 + 1553, 1466, 1383, 1306, 1232, 1163, 1098, 1036, 978, 923, 871, 823, _ 'octave 2 + 776, 733, 692, 653, 616, 582, 549, 518, 489, 462, 436, 411, _ 'octave 3 + 388, 366, 346, 326, 308, 291, 274, 259, 245, 231, 218, 206, _ 'octave 4 + 194, 183, 173, 163, 154, 145, 137, 130, 122, 115, 109, 103, _ 'octave 5 + 97, 92, 86, 82, 77, 73, 69, 65, 61, 58, 54, 51, _ 'octave 6 + 49, 46, 43, 41, 39, 36, 34, 32, 31, 29, 27, 26, _ 'octave 7 + 24, 23, 22, 20, 19, 18, 17, 16, 15, 14, 14, 13 _ 'octave 8 +} + +' Maps ascii code of a letter to the corresponding index of `_Play_NoteDividers` (octave 0). +dim _Play_NoteIndexes(code("A") to code("G")) as ubyte = { _ + /'A'/ 9, _ + /'B'/ 11, _ + /'C'/ 0, _ + /'D'/ 2, _ + /'E'/ 4, _ + /'F'/ 5, _ + /'G'/ 7 _ +} + +' Pointer to the current channel context. +' Made global for better performance, and also because it would be problematic to access it from nested subs if it were +' local (see 'Implementation note' on `Play`). +dim _Play_ContextPtr as uinteger + +' Switches to the first channel context. +#define _PLAY_CTX_FIRST_CHANNEL() let _Play_ContextPtr = ChannelContextBufferPtr + +' Swithes to the next channel context. +#define _PLAY_CTX_NEXT_CHANNEL() let _Play_ContextPtr = _Play_ContextPtr + ChannelContextSize + +' Gets the value of the given `type` at the given `offset` of the current channel context. +#define _PLAY_CTX_GET(type, offset) (peek(type, _Play_ContextPtr + (offset))) + +' Sets the given `value` of the given `type` to the given `offset` of the current channel context. +#define _PLAY_CTX_SET(type, offset, value) poke type _Play_ContextPtr + (offset), (value) + +' Arithmetically adds the given `value` to the value of the given `type` stored at the given `offset` +' of the current channel context. +#define _PLAY_CTX_ADD(type, offset, value) _PLAY_CTX_SET(type, offset, _PLAY_CTX_GET(type, offset) + (value)) + +' Write the value to the given register of the sound chip. +#define _PLAY_WRITE_TO_REGISTER(register, value) out $cf, (register) : out $0f, (value) + + +' Main sub. +' +' Implementation note: +' If you want to extend the inner subs or functions, or add new ones, +' please beware that the current compiler version (v1.19.0-beta7 at the time of writing) +' doesn't support accessing outer local vars from the inner sub/function, +' if the inner sub/function has its own vars or params. +' +' TODO: add sub variants that accept strings byref, and that accept an array. +' TODO: check valid ranges of command parameters, handle integer overflow/underflow +' +sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") + + const CpuCyclesPerSecond as ulong = 3250000 + const CpuCyclesPerMicrotick as ubyte = 27 ' see the `Wait` sub + const BeatsPerBar as ubyte = 4 + const SecondsPerMinute as ubyte = 60 + const ChannelCount as ubyte = 3 + + const DefaultTempo as ubyte = 120 + const DefaultOctave as ubyte = 5 + const DefaultNoteLength as ubyte = 5 + const DefaultVolume as ubyte = 15 + const DefaultMixer as ubyte = %11111000 + + ' General processing overhead compensation. Applied to every `Wait` invocation. + ' Determined experimentally. + ' TODO: adjust if needed after everything is implemented. + const TickGeneralOverheadInMicroticks as uinteger = 90 + + ' Overhead compensation for commands processing of a single channel. + ' Applied only on those ticks when there are commands processed for the channel. + ' Determined experimentally. + ' TODO: adjust if needed after everything is implemented. + const TickChannelCommandsOverheadInMicroticks as uinteger = 140 + + ' Channel numbers are zero-based, because it's better in terms of performance (less arithmetics in runtime needed). + const MaxChannel as ubyte = ChannelCount - 1 + + ' Size of a single channel context in bytes. Don't forget to increase this if you add more context fields. + ' Note: this is used in macro `_PLAY_CTX_NEXT_CHANNEL`. + const ChannelContextSize as ubyte = 15 + + ' Channel context data is stored here. + dim ChannelContextBuffer(0 to ChannelContextSize * ChannelCount - 1) as ubyte + + ' Pointer to context data. + ' Note: this is used in macro `_PLAY_CTX_FIRST_CHANNEL`. + dim ChannelContextBufferPtr as uinteger + ChannelContextBufferPtr = @ChannelContextBuffer(0) + + ' Offsets of fields in a channel context. + ' If you add more fields, don't forget to increase `ChannelContextSize`. + const _CharPtr as ubyte = 0 ' (uinteger) Pointer to the current character in the channel string. + const _StringEndPtr as ubyte = 2 ' (uinteger) Pointer to the first byte after the last char of the channel + ' string. + const _TickBackCounter as ubyte = 4 ' (uinteger) How many ticks to wait before proceeding to the next command + ' in the channel string. Zero means we need to proceed now. + const _PrimaryNoteLengthInTicks as ubyte = 6 ' (uinteger) Current note length in ticks. + const _ActualNoteLengthInTicks as ubyte = 8 ' (uinteger) Actual note length in ticks. + ' Mostly the same as `_PrimaryNoteLengthInTicks`, + ' but may differ for triplets and ties. + const _ResetNoteLengthBackCount as ubyte = 10 ' (ubyte) How many notes left to play before actual note length must + ' be reset to primary note length. + const _BaseDividerIndex as ubyte = 11 ' (ubyte) Divider index (see `_Play_NoteDividers`) that corresponds to + ' note C of the current octave. + const _SemitoneAdjustment as ubyte = 12 ' (byte) How many semitones to add or subtract from the next note. + const _FinishedFlag as ubyte = 13 ' (ubyte) If nonzero, then the channel has finished playing. + const _Volume as ubyte = 14 ' (ubyte) Current volume. + + ' Current tempo as beats per minute. A 'beat' is a 1/4-length note. + dim Tempo as ubyte + + ' Current tempo as microticks per tick. + ' For 'microtick' definition, see the `CpuCyclesPerMicrotick` const. + ' For 'tick' definition, see the `_Play_TicksPerBar` const. + dim MicroticksPerTick as uinteger + + dim LastChar as ubyte ' Last char read by `ReadChar` sub. + dim LastNumber as uinteger ' Last number read by `ReadNumber` sub. + + ' Reads a char from the current channel string and puts it to `LastChar` variable. + ' Puts 0 if there's nothing left to read. + ' This is a sub, not a function, for performance reasons. + sub ReadChar + if _PLAY_CTX_GET(uinteger, _CharPtr) = _PLAY_CTX_GET(uinteger, _StringEndPtr) then + LastChar = 0 + return + end if + + LastChar = peek(_PLAY_CTX_GET(uinteger, _CharPtr)) + _PLAY_CTX_ADD(uinteger, _CharPtr, 1) + end sub + + ' Reads the number from the current channel string and puts it in `LastNumber` variable. + ' Puts 0 if the number is unreadable. + sub ReadNumber + LastNumber = 0 + + do + ReadChar + + if LastChar >= code("0") and LastChar <= code("9") then + LastNumber = LastNumber * 10 + LastChar - code("0") + else + ' The number has ended. + if LastChar <> 0 then + ' Step back if the string has not ended. + _PLAY_CTX_ADD(uinteger, _CharPtr, -1) + end if + exit do + end if + loop + end sub + + ' Sets octave for the current channel. + sub SetOctave(octave as ubyte) + _PLAY_CTX_SET(ubyte, _BaseDividerIndex, octave * _Play_NotesPerOctave) + end sub + + ' Sets `MicroticksPerTick` according to the current `Tempo` value. + sub UpdateMicroticksPerTick + MicroticksPerTick = _ + CpuCyclesPerSecond * SecondsPerMinute * BeatsPerBar _ + / CpuCyclesPerMicrotick / _Play_TicksPerBar / Tempo + end sub + + ' Waits for the given amount of microticks. + ' One microtick is 27 CPU cycles (if you change it, also change `CpuCyclesPerMicrotick` const). + ' TODO: is it possible to suppress the 'unused parameter' warning? + sub fastcall Wait(microticks as uinteger) + asm + proc + local loop + + ld bc, 1 ; bc = 1 + or a ; reset carry flag + loop: + sbc hl, bc ; microticks = microticks - bc ; 15 cycles + jr nz, loop ; if microticks <> 0 then goto loop ; 12 cycles + + endp + end asm + end sub + + ' Set pitch on the sound chip for a channel. + sub SetChipPitchDivider(channel as ubyte, divider as uinteger) + _PLAY_WRITE_TO_REGISTER(channel * 2, divider band $ff) + _PLAY_WRITE_TO_REGISTER(channel * 2 + 1, divider >> 8) + end sub + + ' Set volume on the sound chip for a channel. + sub SetChipVolume(channel as ubyte, volume as ubyte) + _PLAY_WRITE_TO_REGISTER(channel + 8, volume) + end sub + + ' Set mixer mode on the sound chip. + sub SetChipMixer(value as ubyte) + _PLAY_WRITE_TO_REGISTER(7, value) + end sub + + ' If macro `_PLAY_BENCHMARK_MODE` is defined, then interrupts are not disabled, and the system timer is used to + ' measure the duration of play. The duration in ticks is printed on the screen after playing. + ' Note that interrupts add overhead and inaccuracies to timings, so this mode is not intended for fine-tuning + ' timings. This should only be used for differential analysis of code optimization efficiency. + #ifndef _PLAY_BENCHMARK_MODE + asm + di + end asm + #endif + + _PLAY_CTX_FIRST_CHANNEL() + _PLAY_CTX_SET(uinteger, _CharPtr, @channel0) + + _PLAY_CTX_NEXT_CHANNEL() + _PLAY_CTX_SET(uinteger, _CharPtr, @channel1) + + _PLAY_CTX_NEXT_CHANNEL() + _PLAY_CTX_SET(uinteger, _CharPtr, @channel2) + + dim channel as ubyte + dim strLen as uinteger + + _PLAY_CTX_FIRST_CHANNEL() + + for channel = 0 to MaxChannel + ' We need low-level access to the strings to achieve good performance. + + ' dereference the pointer to the heap + _PLAY_CTX_SET(uinteger, _CharPtr, peek(uinteger, _PLAY_CTX_GET(uinteger, _CharPtr))) + + ' read the string length + strLen = peek(uinteger, _PLAY_CTX_GET(uinteger, _CharPtr)) + + ' adjust the pointer so it points to the first char + _PLAY_CTX_ADD(uinteger, _CharPtr, 2) + + ' calculate and store the pointer to the string end + _PLAY_CTX_SET(uinteger, _StringEndPtr, _PLAY_CTX_GET(uinteger, _CharPtr) + strLen) + + SetOctave DefaultOctave + _PLAY_CTX_SET(ubyte, _Volume, DefaultVolume) + _PLAY_CTX_SET(uinteger, _PrimaryNoteLengthInTicks, _Play_NoteLengthsInTicks(DefaultNoteLength)) + _PLAY_CTX_SET(uinteger, _ActualNoteLengthInTicks, _Play_NoteLengthsInTicks(DefaultNoteLength)) + _PLAY_CTX_SET(uinteger, _TickBackCounter, 0) + _PLAY_CTX_SET(byte, _SemitoneAdjustment, 0) + _PLAY_CTX_SET(ubyte, _ResetNoteLengthBackCount, 0) + _PLAY_CTX_SET(ubyte, _FinishedFlag, 0) + + _PLAY_CTX_NEXT_CHANNEL() + next channel + + Tempo = DefaultTempo + UpdateMicroticksPerTick + SetChipMixer DefaultMixer + + dim processedChannels as ubyte + dim finishedChannels as ubyte + dim lengthInTicks as uinteger + dim dividerIndex as ubyte + + #ifdef _PLAY_BENCHMARK_MODE + dim SysFrames as uinteger at $5c78 + dim startTime as uinteger = SysFrames + #endif + + do + finishedChannels = 0 + processedChannels = 0 + + _PLAY_CTX_FIRST_CHANNEL() + + for channel = 0 to MaxChannel + if _PLAY_CTX_GET(uinteger, _TickBackCounter) = 0 then + do + ReadChar + + if LastChar = 0 then + ' This channel has finished playing. + _PLAY_CTX_SET(ubyte, _FinishedFlag, 1) + ' While other channels are still playing, this one will do rests. + SetChipVolume channel, 0 + exit do + + else if LastChar = code("&") then + SetChipVolume channel, 0 + exit do + + else if (LastChar >= code("a") and LastChar <= code("g")) _ + or (LastChar >= code("A") and LastChar <= code("G")) then + + dividerIndex = _PLAY_CTX_GET(ubyte, _BaseDividerIndex) + + if LastChar >= code("a") then + ' if lowercase, then transpose down 1 octave and make uppercase + dividerIndex = dividerIndex - _Play_NotesPerOctave + LastChar = LastChar - 32 + end if + + dividerIndex = dividerIndex + _ + _Play_NoteIndexes(LastChar) + _PLAY_CTX_GET(byte, _SemitoneAdjustment) + + _PLAY_CTX_SET(byte, _SemitoneAdjustment, 0) + + SetChipPitchDivider channel, _Play_NoteDividers(dividerIndex) + SetChipVolume channel, _PLAY_CTX_GET(ubyte, _Volume) + + exit do + + else if LastChar = code("$") then + _PLAY_CTX_ADD(byte, _SemitoneAdjustment, -1) + + else if LastChar = code("#") then + _PLAY_CTX_ADD(byte, _SemitoneAdjustment, 1) + + else if LastChar >= code("0") and LastChar <= code("9") + _PLAY_CTX_ADD(uinteger, _CharPtr, -1) ' step back + ReadNumber + lengthInTicks = _Play_NoteLengthsInTicks(LastNumber) + + _PLAY_CTX_SET(uinteger, _ActualNoteLengthInTicks, lengthInTicks) + + if LastNumber >= 10 then + ' triplet mode (temporary length) + _PLAY_CTX_SET(ubyte, _ResetNoteLengthBackCount, 3) + else + _PLAY_CTX_SET(uinteger, _PrimaryNoteLengthInTicks, lengthInTicks) + end if + + else if LastChar = code("_") then + ' TODO: ties with triplets work differently in Sinclair ROM. + ReadNumber + lengthInTicks = _Play_NoteLengthsInTicks(LastNumber) + + _PLAY_CTX_SET(ubyte, _ResetNoteLengthBackCount, 1) + _PLAY_CTX_ADD(uinteger, _ActualNoteLengthInTicks, lengthInTicks) + _PLAY_CTX_SET(uinteger, _PrimaryNoteLengthInTicks, lengthInTicks) + + else if LastChar = code("O") then + ReadNumber + SetOctave LastNumber + + else if LastChar = code("V") then + ReadNumber + _PLAY_CTX_SET(ubyte, _Volume, LastNumber) + + else if LastChar = code("T") then + ReadNumber + + if channel = 0 then + Tempo = LastNumber + UpdateMicroticksPerTick + end if + + ' TODO: process other commands here + end if + loop + + if _PLAY_CTX_GET(ubyte, _ResetNoteLengthBackCount) > 0 then + _PLAY_CTX_ADD(ubyte, _ResetNoteLengthBackCount, -1) + else + _PLAY_CTX_SET(uinteger, _ActualNoteLengthInTicks, _PLAY_CTX_GET(uinteger, _PrimaryNoteLengthInTicks)) + end if + + _PLAY_CTX_SET(uinteger, _TickBackCounter, _PLAY_CTX_GET(uinteger, _ActualNoteLengthInTicks)) + + processedChannels = processedChannels + 1 + end if + + _PLAY_CTX_ADD(uinteger, _TickBackCounter, -1) + + finishedChannels = finishedChannels + _PLAY_CTX_GET(ubyte, _FinishedFlag) + + _PLAY_CTX_NEXT_CHANNEL() + next channel + + Wait MicroticksPerTick _ + - TickGeneralOverheadInMicroticks _ + - TickChannelCommandsOverheadInMicroticks * processedChannels + loop until finishedChannels = ChannelCount + + #ifdef _PLAY_BENCHMARK_MODE + print "Play duration: "; SysFrames - startTime; " ticks." + #endif + + ' (zx81sd) Sin EI final: el runtime funciona con las interrupciones + ' deshabilitadas y rehabilitarlas podria disparar una INT sin handler. +end sub + +#pragma pop(explicit) +#pragma pop(strict) diff --git a/src/lib/arch/zx81sd/stdlib/print42.bas b/src/lib/arch/zx81sd/stdlib/print42.bas new file mode 100644 index 000000000..605676c28 --- /dev/null +++ b/src/lib/arch/zx81sd/stdlib/print42.bas @@ -0,0 +1,586 @@ +' vim:ts=4:et: + +#ifndef __PRINT42__ +#define __PRINT42__ + +' -------------------------------------------------- +' A PRINT Routine with 42 COLUMNS! +' +' Contributed by Britlion +' +' Override zx81sd: la version zx48k calcula direcciones de pantalla y +' atributos con constantes fijas de la ROM del Spectrum ($4000/$5800, +' aqui "64"/"88" como bytes altos) y con sysvars fijos de la ROM +' ($5C7B = UDG, 23693 = ATTR_P, 15360 = CHARS-256). En zx81sd: +' - UDG/ATTR_P/CHARS son sysvars propios en $8000+ (ver sysvars.asm, +' namespace core, de ahi el prefijo .core. en las referencias de +' mas abajo), y CHARS/UDG son VARIABLES (punteros), no constantes, +' asi que se leen con LD (r16),(CHARS) en vez de un LD inmediato. +' - SCREEN_ADDR/SCREEN_ATTR_ADDR tambien son variables (no EQU fijos: +' bloque 6 en $C000/$D800 en vez de $4000/$5800), pero el byte alto +' se sigue sumando dentro de un ADD A,n con constante inmediata (el +' algoritmo de "tercios" de pantalla es identico al del Spectrum, +' solo cambia la base). Se resuelve parcheando esa constante una +' vez al entrar en print42(), leyendola de SCREEN_ADDR+1/ +' SCREEN_ATTR_ADDR+1 (automodificacion de codigo, igual que hacia +' la ROM original con sus propias rutinas). +' -------------------------------------------------- + +#pragma push(case_insensitive) +#pragma case_insensitive = TRUE + +SUB printat42 (y as uByte, x as uByte) + POKE @printAt42Coords,x + POKE @printAt42Coords+1,y +END SUB + + +SUB print42(characters$ as string) +asm + ; No hace '#include once ' aqui: si esta libreria fuera + ; la primera en incluirlo en todo el programa, arrastraria tambien + ; bootstrap.asm/charset.asm (con su INCBIN del font completo) justo + ; en medio de este cuerpo de funcion, ejecutandose como si fueran + ; instrucciones (bug real, encontrado al portar este fichero). Las + ; referencias a CHARS/UDG/ATTR_P/SCREEN_ADDR/SCREEN_ATTR_ADDR de mas + ; abajo confian en que sysvars.asm ya este incluido por el resto del + ; runtime (CLS/PRINT lo requieren siempre, y este fichero no tiene + ; sentido usarlo sin ellos). + PROC + + ; Parchea las constantes de base de pantalla/atributos usadas mas + ; abajo (ver __P42_SCR_HI/__P42_ATTR_HI) con los bytes altos reales + ; de .core.SCREEN_ADDR/.core.SCREEN_ATTR_ADDR de este programa (variables en + ; zx81sd, no EQU fijos como en el Spectrum). + LOCAL __P42_SCR_HI, __P42_ATTR_HI + ld a, (.core.SCREEN_ADDR+1) + ld (__P42_SCR_HI+1), a + ld a, (.core.SCREEN_ATTR_ADDR+1) + ld (__P42_ATTR_HI+1), a + + LD A, H + OR L + JP Z, print42end + + LD C,(HL) + INC HL + LD B,(HL) ; all told, LD BC with the length of the string. + + LD A, C + OR B + JP Z, print42end ; Is the length of the string 0? If so, quit. + + INC HL ;Puts HL to the first real character in the string. + +LOCAL examineChar +examineChar: + LD A,(HL) ; Grab the character at our pointer position + CP 165 ; Too high to print? + JR NC, nextChar ; Then we go to the next + + CP 144 + JR NC, prn ; char is a UDG + + CP 128 + JR NC, nextChar ; char is a block graphic + + CP 22 ; Is this an AT? + JR NZ, isNewline ; If not jump over the AT routine to isNewline + +LOCAL isAt +isAt: + EX DE,HL ; Get DE to hold HL for a moment + ;;AND A ; Plays with the flags. One of the things it does is reset Carry. + ;;LD HL,00002 + ;;SBC HL,BC ; Subtract length of string from HL. + LD HL, -2 + ADD HL, BC + EX DE,HL ; Get HL back from DE + JP NC, print42end ; If the result WASN'T negative, return. (We need AT to have parameters to make sense) + + INC HL ; Onto our Y co-ordinate + LD D,(HL) ; Put it in D + DEC BC ; and move our string remaining counter down one + INC HL ; Onto our X co-ordinate + LD E,(HL) ; Put the next one in E + DEC BC ; and move our string remaining counter down one + ld (xycoords), de + JR nextChar + +LOCAL isNewline +isNewline: + CP 13 ; Is this character a newline? + JR NZ, checkdel ; If not, jump forward + +LOCAL newline +newline: + ld de, (xycoords) + CALL nxtline ; move to next line + ld (xycoords), de + JR nextChar + +LOCAL checkdel +checkdel: + CP 8 + JR NZ, checkvalid + ld de, (xycoords) + dec de + ld (xycoords), de + ld a, 41 + cp e + JR NC, nextChar + ld e, a + ld (xycoords), de + ld a, 23 + cp d + JR NC, nextChar + ld d, a + ld (xycoords), de + JR nextChar + +LOCAL checkvalid +checkvalid: + CP 31 ; Is character <31? + JR C, nextChar ; If not go to next character + +LOCAL prn +prn: + PUSH HL ; Save our position + PUSH BC ; Save our countdown of chars left + CALL printachar ; Go print a character + POP BC ; Recover our count + POP HL ; Recover our position + +LOCAL nextChar +nextChar: + INC HL ; Move to the next position + DEC BC ; count off a character + LD A,B + OR C ; Did we hit the end of our string? (BC=0?) + JR NZ, examineChar ; If not, we need to go look at the next character. + JP print42end ; End the print routine + + +; This routine forms the new 6-bit wide characters and +; alters the colours to match the text. The y,x co-ordinates and eight +; bytes of workspace are located at the end of this chunk. +; it starts with the character ascii code in the accumulator + +LOCAL printachar +printachar: + EXX + PUSH HL ; Store H'L' where we can get it. + EXX + + CP 144 + jr nc,printudg + + ld c, a ; Put a copy of the character in C + ld h, 0 + ld l, a ; Put the Character in HL + + ld de, whichcolumn-32 ; the character is at least 32, so space = 0th entry. + add hl, de ; HL -> table entry for char. + ld a, (hl) ; Load our column slice data from the table. + cp 32 ; Is it less than 32? + jr nc, calcChar ; If so, go to the calculated character subroutine + +; This is the special case 'we defined the character in the table' option + ld de, characters ; Point DE at our table + ld l, a ; Put our character number from our table lookup that's in HL in a + call mult8 ; multiplies L by 8 and adds in DE [so HL points at our table entry] + ld b, h + ld c, l ; Copy our character data address into BC + jr printdata ; We have our data source, so we print it. + +LOCAL printudg +printudg + sub 144 ; get the udg offset + ld hl, .core.UDG ; examine our own .core.UDG sysvar for the udg address + ld e,(hl) ; lsb for udg address + inc hl + ld d,(hl) ; mbs for udg address + ld l,a ; character offset for udg + call mult8 ; multiplies L by 8 and adds in DE [so HL points at our table entry] + ld b, h + ld c, l ; Copy our character data address into BC + jr printdata ; We have our data source, so we print it. + +LOCAL calcChar +calcChar: ; this is the calculate-from-charset option + ; a holds the column kill data + ld de, (.core.CHARS) ; Character set-256 (variable en zx81sd, no 15360 fijo) + ld l, c ; Get our character back from C + call mult8 ; Multiply l by 8 and add to DE. (HL points at the charset data for our character now) + + ld de, workspace ; Point DE at our 8 byte workspace. + push de ; Save it + exx ; + ld c, a ; Put our kill column in C' + cpl ; Invert + ld b, a ; Put the inverse in B' + exx ; + ld b, 8 ; 8 bytes to a character loop counter + +LOCAL loop1 +loop1: + ld a, (hl) ; Load a byte of character data + inc hl ; point at the next byte + exx ; + ld e, a ; Put it in e' + and c ; keep the left column block we're using + ld d, a ; and put it in d' + ld a, e ; grab our original back + rla ; shift it left (which pushes out our unwanted column) + and b ; keep just the right block + or d ; mix with the left block + exx ; + ld (de), a ; put it into our workspace + inc de ; next workspace byte + djnz loop1 ; go round for our other bytes + + pop bc ; Recover a pointer to our workspace. + +LOCAL printdata +printdata: + call testcoords ; check our position, and wrap around if necessary. [returns with d=y,e=x] + inc e ; Bump along to next co-ordinate + ld (xycoords), de ; Store our coordinates for the next character + dec e ; Bump back to our current one + ld a, e ; get x + sla a ; Shift Left Arithmetic - *2 + ld l, a ; put x*2 into L + sla a ; make it x*4 + add a, l ; (x*2)+(x*4)=6x + ld l, a ; put 6x into L [Since we're in a 6 pixel font, L now contains the # of first pixel we're interested in] + srl a ; divide by 2 + srl a ; divide by another 2 (/4) + srl a ; divide by another 2 (/8) + ld e, a ; Put the result in e (Since the screen has 8 pixel bytes, pixel/8 = which char pos along our first pixel is in) + ld a, l ; Grab our pixel number again + and 7 ; And do mod 8 [So now we have how many pixels into the character square we're starting at] + push af ; Save A + ex af, af' + ld a, d ; Put y Coord into A' + sra a ; Divide by 2 + sra a ; Divide by another 2 (/4 total) + sra a ; Divide by another 2 (/8) [Gives us a 1/3 of screen number] +__P42_ATTR_HI: + add a, 88 ; Add in start of screen attributes high byte (parcheado con .core.SCREEN_ATTR_ADDR+1 al entrar en print42) + ld h, a ; And put the result in H + ld a, d ; grab our Y co-ord again + and 7 ; Mod 8 (why? *I thought to give a line in this 1/3 of screen, but we're in attrs here) + rrca ; + rrca + rrca ; Bring the bottom 3 bits to the top - Multiply by 32 + ; (since there are 32 bytes across the screen), here, in other words. [Faster than 5 SLA instructions] + add a, e ; add in our x coordinate byte to give us a low screen byte + ld l, a ; Put the result in L. So now HL -> screen byte at the top of the character + + ld a, (.core.ATTR_P) ; ATTR P Permanent current colours, etc (as set up by colour statements). + ld e, a ; Copy ATTR into e + ld (hl), e ; Drop ATTR value into screen + inc hl ; Go to next position along + pop af ; Pull how many pixels into this square we are + cp 3 ; It more than 2? + jr c, hop1 ; No? It all fits in this square - jump changing the next attribute + + ld (hl), e ; Must be yes - we're setting the attributes in the next square too. + +LOCAL hop1 +hop1: + dec hl ; Back up to last position + ld a, d ; Y Coord into A' + and 248 ; Turn it into 0,8 or 16. (y=0-23) +__P42_SCR_HI: + add a, 64 ; Turn it into screen bitmap high byte (parcheado con .core.SCREEN_ADDR+1 al entrar en print42) + ld h, a ; Stick it in H + push hl ; Save it + exx ; Swap registers + pop hl ; Put it into H'L' + exx ; Swap Back + ld a, 8 + +LOCAL hop4 +hop4: + push af ; Save Accumulator + ld a, (bc) ; Grab a byte of workspace + exx ; Swap registers + push hl ; Save h'l' + ld c, 0 ; put 0 into c' + ld de, 1023 ; Put 1023 into D'E' + ex af, af' ; Swap AF + and a ; Flags on A + jr z, hop3 ; If a is zero jump forward + + ld b, a ; A -> B + ex af, af' ; Swap to A'F' + +LOCAL hop2 +hop2:; Slides a byte right to the right position in the block (and puts leftover bits in the left side of c) + and a ; Clear Carry Flag + rra ; Rotate Right A + rr c ; Rotate right C (Rotates a carry flag off A and into C) + scf ; Set Carry Flag + rr d ; Rotate Right D + rr e ; Rotate Right E (D flows into E, with help from the carry bit) + djnz hop2 ; Decrement B and loop back + + ex af, af' + +LOCAL hop3 +hop3: + ex af, af' + ld b, a + ld a, (hl) + and d + or b + ld (hl), a ; Write out our byte + inc hl ; Go one byte right + ld a, (hl) ; Bring it in + and e + or c ; mix those leftover bits into the next block + ld (hl), a ; Write it out again + pop hl + inc h ; Next line + exx + inc bc ; Next workspace byte + pop af + dec a + jr nz, hop4 ; And go back! + + exx ; Tidy up + pop hl ; Clear stack leftovers + exx ; And... + ret ; Go home. + +LOCAL mult8 +mult8: ; Multiplies L by 8 -> HL and adds it to DE. Used for 8 byte table vectors. + ld h, 0 + add hl, hl + add hl, hl + add hl, hl + add hl, de + ret + +LOCAL testcoords +testcoords: + ld de, (xycoords) ; get our current screen co-ordinates (d=y,e=x - little endian) + +LOCAL nxtchar +nxtchar: + ld a, e + cp 42 ; Are we >42? + jr c, ycoord ; if not, hop forward + +LOCAL nxtline +nxtline: + inc d ; if so, so bump us to the next line down + ld e, 0 ; and reset x to left edge + +LOCAL ycoord +ycoord: + ld a, d + cp 24 ; are we >24 lines? + ret c ; if no, exit subroutine + ld d, 0 ; if yes, wrap around to top line again. + ret ; exit subroutine +end asm +printAt42Coords: +asm +LOCAL xycoords +xycoords: + defb 0 ; x coordinate + defb 0 ; y coordinate + +LOCAL workspace +workspace: + defb 0 + defb 0 + defb 0 + defb 0 + defb 0 + defb 0 + defb 0 + defb 0 + +; The data below identifies a column in the character to remove. It consists of 1's +; from the left edge. First zero bit is the column we're removing. +; If the leftmost bit is NOT 1, then the byte represents a redefined character position +; in the lookup table. + +LOCAL whichcolumn +whichcolumn: + defb 254 ; SPACE + defb 254 ; ! + defb 128 ; "" + defb 224 ; # + defb 128 ; $ + defb 0 ; % (Redefined below) + defb 1 ; & (Redefined below) + defb 128 ; ' + defb 128 ; ( + defb 128 ; ) + defb 128 ; * + defb 128 ; + + defb 128 ; , + defb 128 ; - + defb 128 ; . + defb 128 ; / + defb 2 ; 0 (Redefined below) + defb 128 ; 1 + defb 224 ; 2 + defb 224 ; 3 + defb 252 ; 4 + defb 224 ; 5 + defb 224 ; 6 + defb 192 ; 7 + defb 240 ; 8 + defb 240 ; 9 + defb 240 ; : + defb 240 ; ; + defb 192 ; < + defb 240 ; = + defb 192 ; > + defb 192 ; ? + defb 248 ; @ + defb 240 ; A + defb 240 ; B + defb 240 ; C + defb 240 ; D + defb 240 ; E + defb 240 ; F + defb 240 ; G + defb 240 ; H + defb 128 ; I + defb 240 ; J + defb 192 ; K + defb 240 ; L + defb 240 ; M + defb 248 ; N + defb 240 ; O + defb 240 ; P + defb 248 ; Q + defb 240 ; R + defb 240 ; S + defb 3 ; T + defb 240 ; U + defb 240 ; V + defb 240 ; W + defb 240 ; X + defb 4 ; Y + defb 252 ; Z + defb 224 ; [ + defb 252 ; \ + defb 240 ; ] + defb 252 ; ^ + defb 6 ; _ + defb 240 ; UK Pound (Currency) Symbol + defb 255 ; a + defb 128 ; b + defb 255 ; c + defb 255 ; d + defb 255 ; e + defb 255 ; f + defb 255 ; g + defb 255 ; h + defb 255 ; i + defb 255 ; j + defb 255 ; k + defb 255 ; l + defb 255 ; m + defb 255 ; n + defb 255 ; o + defb 255 ; p + defb 255 ; q + defb 255 ; r + defb 255 ; s + defb 255 ; t + defb 255 ; u + defb 255 ; v + defb 255 ; w + defb 255 ; x + defb 255 ; y + defb 255 ; z + defb 128 ; { + defb 128 ; | + defb 255 ; } + defb 128 ; ~ + defb 5 ; (c) end column data + + +LOCAL characters +characters: + defb 0 ; % + defb 0 + defb 100 + defb 104 + defb 16 + defb 44 + defb 76 + defb 0 + + defb 0 ; & + defb 32 + defb 80 + defb 32 + defb 84 + defb 72 + defb 52 + defb 0 + + defb 0 ; digit 0 + defb 56 + defb 76 + defb 84 + defb 84 + defb 100 + defb 56 + defb 0 + + defb 0 ; Letter T + defb 124 + defb 16 + defb 16 + defb 16 + defb 16 + defb 16 + defb 0 + + defb 0 ; Letter Y + defb 68 + defb 68 + defb 40 + defb 16 + defb 16 + defb 16 + defb 0 + + defb 0 ; (c) symbol + defb 48 + defb 72 + defb 180 + defb 164 + defb 180 + defb 72 + defb 48 + + defb 0 + defb 0 + defb 0 + defb 0 + defb 0 + defb 0 + defb 0 + defb 0xFC + +LOCAL print42end +print42end: + ENDP + +end asm +END SUB + +#pragma pop(case_insensitive) + +#endif diff --git a/src/lib/arch/zx81sd/stdlib/print64.bas b/src/lib/arch/zx81sd/stdlib/print64.bas new file mode 100644 index 000000000..bd6c134c5 --- /dev/null +++ b/src/lib/arch/zx81sd/stdlib/print64.bas @@ -0,0 +1,320 @@ +' vim:ts=4:et: +' --------------------------------------------------------- +' 64 Characters wide PRINT Routine for ZX BASIC +' Contributed by Britlion +' +' Override zx81sd: la version zx48k asume las direcciones fijas de la +' ROM del Spectrum para pantalla/atributos ($4000/$5800, aqui "64"/"88" +' como bytes altos) y el sysvar fijo ATTR_P (23693). En zx81sd, +' SCREEN_ADDR/SCREEN_ATTR_ADDR son variables (bloque 6, $C000/$D800) y +' ATTR_P vive en $800E (namespace core, ver sysvars.asm -- de ahi el +' prefijo .core. en las referencias de mas abajo). El charset de este +' fichero (p64_charset) es propio, no depende de la ROM, asi que no +' hace falta tocarlo. Igual que en print42.bas, las dos constantes de +' base de pantalla/atributos se parchean una vez al entrar en +' print64(), leyendo el byte alto real de SCREEN_ADDR/SCREEN_ATTR_ADDR. +' --------------------------------------------------------- + +#ifndef __PRINT64__ +#define __PRINT64__ + +#pragma push(case_insensitive) +#pragma case_insensitive = TRUE + +' Changes print coordinates. +SUB printat64 (y as uByte, x as uByte) + POKE @p64coords,x + POKE @p64coords+1,y +END sub + +' Print given string at current position +SUB print64 (characters$ as String) + asm + ; No hace '#include once ' aqui: si esta libreria fuera + ; la primera en incluirlo en todo el programa, arrastraria tambien + ; bootstrap.asm/charset.asm (con su INCBIN del font completo) justo + ; en medio de este cuerpo de funcion, ejecutandose como si fueran + ; instrucciones (bug real, encontrado al portar este fichero). Las + ; referencias a ATTR_P/SCREEN_ADDR/SCREEN_ATTR_ADDR de mas abajo + ; confian en que sysvars.asm ya este incluido por el resto del + ; runtime (CLS/PRINT lo requieren siempre, y este fichero no tiene + ; sentido usarlo sin ellos). + PROC ; Declares begin of procedure + ; so we can now use LOCAL labels + + ; Parchea las constantes de base de pantalla/atributos usadas mas + ; abajo (ver p64_SCR_HI/p64_ATTR_HI) con los bytes altos reales de + ; .core.SCREEN_ADDR/.core.SCREEN_ATTR_ADDR de este programa. + LOCAL p64_SCR_HI, p64_ATTR_HI + ld a, (.core.SCREEN_ADDR+1) + ld (p64_SCR_HI+1), a + ld a, (.core.SCREEN_ATTR_ADDR+1) + ld (p64_ATTR_HI+1), a + + LD L,(IX+4) + LD H,(IX+5) ; Get String address of characters$ into HL. + + ld a, h + or l + jp z, p64_END ; Return if NULL string + + ; Load BC with length of string, and move HL to point to first character. + ld c, (hl) + inc hl + ld b, (hl) + inc hl + + ; Test string length. If Zero, exit. + ld a, c + or b + jp z, p64_END + + LOCAL examineChar + examineChar: + ld a, (hl) ; Grab the character + cp 128 ; too high to print? + jr nc, nextChar ; then we go to next. + + cp 22 ; Is this an AT? + jr nz, newLine ; If not, hop to newLine routine. + ex de, hl ; Swap DE and HL + and a ; Clear Carry + ld hl, 2 ; + sbc hl, bc ; Can we Shorten our string length by 2? If not then at (y,x) doesn't make sense. + ex de, hl ; Swap DE and HL back + jp nc, p64_END ; If we went negative, there wasn't anything to AT to, so we return. + + inc hl ; Onto our Y co-ordinate + ld d, (hl) ; And load it into D + dec bc ; Shorten our remaining string counter. + inc hl ; Onto the X Co-ordinate + ld e, (hl) ; Load it into E + dec bc ; Shorten our remaining string counter + call p64_test_X ; Make xy legal + jr p64_eaa3 ; Go to save coords + + LOCAL newLine + newLine: + cp 13 ; Is this a newline character? + jr nz, p64_isPrintable ; If not, hop to testing to see if we can print this + ld de, (p64_coords) ; Get coords + call p64_nxtLine ; Go to next line. + + LOCAL p64_eaa3 + p64_eaa3: + ld (p64_coords), de + jr nextChar + + LOCAL p64_isPrintable + p64_isPrintable: + cp 31 ; Bigger than 31? + jr c, nextChar ; If not, get the next one. + push hl ; Save position + push bc ; Save Count + call p64_PrintChar ; Call Print SubRoutine + pop bc ; Recover length count + pop hl ; Recover Position + + LOCAL nextChar + nextChar: + inc hl ; Point to next character + dec bc ; Count off this character + ld a, b ; Did we run out? + or c + jr nz, examineChar ; If not, examine the next one + jp p64_END ; Otherwise hop to END. + + LOCAL p64_PrintChar + p64_PrintChar: + exx + push hl ; Save HL' + exx + sub 32 ; Take out 32 to convert ascii to position in charset + ld h, 0 + rra ; Divide by 2 + ld l, a ; Put our halved value into HL + ld a, 240 ; Set our mask to LEFT side + jr nc, p64_eacc ; If we didn't have a carry (even #), hop forward. + ld a, 15 ; If we were ab idd #, set our mask to RIGHT side instead + + LOCAL p64_eacc + p64_eacc: + add hl, hl + add hl, hl + add hl, hl ; Multiply our char number by 8 + ld de, p64_charset ; Get our Charset position + add hl, de ; And add our character count, so we're now pointed at the first + ; byte of the right character. + exx + ld de, (p64_coords) + ex af, af' + call p64_loadAndTest + ex af, af' + inc e + ld (p64_coords), de ; Put position+1 into coords + dec e + ld b, a + rr e ; Divide X position by 2 + ld c, 0 + rl c ; Bring carry flag into C (result of odd/even position) + and 1 ; Mask out lowest bit in A + xor c ; XOR with C (Matches position RightLeft with Char RightLeft) + ld c, a + jr z, p64_eaf6 ; If they are both the same, skip rotation. + ld a, b + rrca + rrca + rrca + rrca + ld b, a + + LOCAL p64_eaf6 + p64_eaf6: + ld a, d ; Get Y coord + sra a + sra a + sra a ; Multiply by 8 + p64_ATTR_HI: + add a, 88 ; Attribute area high byte (parcheado con .core.SCREEN_ATTR_ADDR+1 al entrar en print64) + ld h, a ; Put high byte value for attribute into H. + ld a, d + and 7 + rrca + rrca + rrca + add a, e + ld l, a ; Put low byte for attribute into l + ld a, (.core.ATTR_P) ; Get permanent Colours from our own sysvar + ld (hl), a ; Write new attribute + + ld a, d + and 248 + p64_SCR_HI: + add a, 64 ; Screen bitmap high byte (parcheado con .core.SCREEN_ADDR+1 al entrar en print64) + ld h, a + ld a, b + cpl + ld e, a + exx + ld b, 8 + + LOCAL p64_eb18 + p64_eb18: + ld a, (hl) + exx + bit 0, c + jr z, p64_eb22 + rrca + rrca + rrca + rrca + + LOCAL p64_eb22 + p64_eb22: + and b + ld d, a + ld a, (hl) + and e + or d + ld (hl), a + inc h + exx + inc hl + djnz p64_eb18 + exx + pop hl + exx + ret + + LOCAL p64_loadAndTest + p64_loadAndTest: + ld de, (p64_coords) + + ; SubRoutine to go to legal character position. + LOCAL p64_test_X + p64_test_X: + ld a, e ; Get column from e + cp 64 ; more than 64 ? + jr c, p64_test_Y ; If not, then jump over nextline + + LOCAL p64_nxtLine + p64_nxtLine: + inc d ; Move down 1 + ld e, 0 ; reset x co-ord to zero + + LOCAL p64_test_Y + p64_test_Y: + ld a, d ; get Y co-ord + cp 24 ; Past 24? + ret c ; Return if not. + ld d, 0 ; Rest y co-ord to top of screen. + ret ; Return. + end asm + p64coords: + asm + LOCAL p64_coords; + p64_coords: + defb 64; X Coordinate store + defb 23; Y Coordinate Store + + LOCAL p64_charset + p64_charset: + DEFB 0,2,2,2,2,0,2,0 ; Space ! + DEFB 0,80,82,7,2,7,2,0 ; "" # + DEFB 0,37,113,66,114,20,117,32 ; $ % + DEFB 0,34,84,32,96,80,96,0 ; & ' + DEFB 0,36,66,66,66,66,36,0 ; ( ) + DEFB 0,0,82,34,119,34,82,0 ; * + + DEFB 0,0,0,0,7,32,32,64 ; , - + DEFB 0,1,1,2,2,100,100,0 ; . / + DEFB 0,34,86,82,82,82,39,0 ; 0 1 + DEFB 0,34,85,18,33,69,114,0 ; 2 3 + DEFB 0,87,84,118,17,21,18,0 ; 4 5 + DEFB 0,55,65,97,82,84,36,0 ; 6 7 + DEFB 0,34,85,37,83,85,34,0 ; 8 9 + DEFB 0,0,2,32,0,34,2,4 ; : ; + DEFB 0,0,16,39,64,39,16,0 ; < = + DEFB 0,2,69,33,18,32,66,0 ; > ? + DEFB 0,98,149,183,181,133,101,0 ; @ A Changed from ;0,2,37,87,117,85,53,0 + DEFB 0,98,85,100,84,85,98,0 ; B C + DEFB 0,103,84,86,84,84,103,0 ; D E + DEFB 0,114,69,116,71,69,66,0 ; F G + DEFB 0,87,82,114,82,82,87,0 ; H I + DEFB 0,53,21,22,21,85,37,0 ; J K + DEFB 0,69,71,71,69,69,117,0 ; L M + DEFB 0,82,85,117,117,85,82,0 ; N O + DEFB 0,98,85,85,103,71,67,0 ; P Q + DEFB 0,98,85,82,97,85,82,0 ; R S + DEFB 0,117,37,37,37,37,34,0 ; T U + DEFB 0,85,85,85,87,39,37,0 ; V W + DEFB 0,85,85,37,82,82,82,0 ; X Y + DEFB 0,119,20,36,36,68,119,0 ; Z [ + DEFB 0,71,65,33,33,17,23,0 ; \ ] + DEFB 0,32,112,32,32,32,47,0 ; ^ _ + DEFB 0,32,86,65,99,69,115,0 ; £ a + DEFB 0,64,66,101,84,85,98,0 ; b c + DEFB 0,16,18,53,86,84,35,0 ; d e + DEFB 0,32,82,69,101,67,69,2 ; f g + DEFB 0,66,64,102,82,82,87,0 ; h i + DEFB 0,20,4,53,22,21,85,32 ; j k + DEFB 0,64,69,71,71,85,37,0 ; l m + DEFB 0,0,98,85,85,85,82,0 ; n o + DEFB 0,0,99,85,85,99,65,65 ; p q + DEFB 0,0,99,84,66,65,70,0 ; r s + DEFB 0,64,117,69,69,85,34,0 ; t u + DEFB 0,0,85,85,87,39,37,0 ; v w + DEFB 0,0,85,85,35,81,85,2 ; x y + DEFB 0,0,113,18,38,66,113,0 ; z { + DEFB 0,32,36,34,35,34,36,0 ; | { + DEFB 0,6,169,86,12,6,9,6 ; ~ (c) + +LOCAL p64_END + p64_END: + ENDP + end asm + + end sub + +#pragma pop(case_insensitive) + +#endif diff --git a/src/lib/arch/zx81sd/stdlib/puttile.bas b/src/lib/arch/zx81sd/stdlib/puttile.bas new file mode 100644 index 000000000..f94b18544 --- /dev/null +++ b/src/lib/arch/zx81sd/stdlib/puttile.bas @@ -0,0 +1,325 @@ +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008-2023 +' by Paul Fisher (a.k.a. BritLion) +' ---------------------------------------------------------------- +' +' zx81sd override: the zx48k version computes the screen bitmap and +' attribute addresses with IMMEDIATE constants (`add a,64` / `add +' a,88`, the high bytes of the Spectrum ROM's fixed $4000/$5800), not +' by reading SCREEN_ADDR/SCREEN_ATTR_ADDR. On zx81sd the screen lives +' at $C000/$D800 (block 6, a variable, not a literal), so those two +' constants are wrong here -- same bug class found and fixed while +' porting print42.bas/print64.bas. Fixed by patching both constants +' once on entering putTile(), reading the real high byte of +' SCREEN_ADDR/SCREEN_ATTR_ADDR at that point (self-modifying code, +' same technique as print42.bas/print64.bas). Everything else in this +' file (the interrupt save/restore dance, the stack-based pixel +' pushing) is architecture-agnostic and copied unchanged: the +' `ld a,i` / `jp po` idiom naturally never re-enables interrupts on +' zx81sd, since IFF2 is always reset here (the runtime never does EI). +' ---------------------------------------------------------------- + +#ifndef __LIBRARY_PUTTILE__ + +REM Avoid recursive / multiple inclusion + +#define __LIBRARY_PUTTILE__ + +#pragma push(case_insensitive) +#pragma case_insensitive = True + + +' ---------------------------------------------------------------- +' SUB putTile +' +' Routine to place a 16 pixel by 16 pixel "Tile" onto the screen at character position x,y from adddress given. +' Data must be in the format of 16 bit rows, followed by attribute data. +' (c) 2010 Britlion, donated to the ZX BASIC project. +' Thanks to Boriel, LCD and Na_than for inspiration behind this. + +' This routine could be used as the basis for a fast sprite system, provided all sprites can be in 4 character blocks. +' It can also be used to clean up dirty background (erase sprites), or put backgrounds from tiled blocks onto a screen. +' +' Note the comments about Self Modifying code should be ignored. This has been updated with IX+n methods, which overall +' are faster than accessing and changing the code. +' (They would have to be accessed to change the memory anyway - may as well just access them directly.) + +' Parameters: +' x - x coordinate (cell column) +' y - y coordinate (cell row) +' graphicsAddr - Memory address of the graphicc +' +' ---------------------------------------------------------------- + +SUB putTile(x as uByte, y as uByte, graphicsAddr as uInteger) + ASM + PROC + LOCAL ptstackSave, pt_start, ptNextThird3, ptSameThird3 + LOCAL ptAddrDone3, ptNextSprite2, pt_nointerrupts + LOCAL PT_SCR_HI, PT_ATTR_HI + + ; Patch the two screen/attribute base constants below with the + ; real high byte of SCREEN_ADDR/SCREEN_ATTR_ADDR (variables on + ; zx81sd, not the Spectrum ROM's fixed $4000/$5800). + ld a, (.core.SCREEN_ADDR+1) + ld (PT_SCR_HI+1), a + ld a, (.core.SCREEN_ATTR_ADDR+1) + ld (PT_ATTR_HI+1), a + + jp pt_start + +ptstackSave: + defb 0,0 + +pt_start: + ld a,i + push af ; Save interrupt status. + + ; Routine to save the background to the buffer + di ; we REALLY can NOT be having interrupts while the stack and IX and IY are pointed elsewhere. + + push ix + push iy + ld d,(ix+9) + ld e,(ix+8) + ex de,hl + + ;; Print sprites routine + ld (ptstackSave), sp ; Save Stack Pointer + ld sp,hl ; now SP points at the start of the graphics. + + ; This function returns the address into HL of the screen address + ld a,(IX+5) ; Load in x - note the Self Modifying value + ld IYH, a ; save it + ld l,a + ld a,(IX+7) ; Load in y - note the Self Modifying value + ld IYL, a ; save it + ld d,a + and 24 +PT_SCR_HI: + add a,64 + ld h,a + ld a,d + and 7 + rrca + rrca + rrca + or l + add a,2 ; Need to be to the right so backwards writing pushes land properly. + ld l,a + + ; SO now, HL -> Screen address, and SP -> Graphics. Time to start loading. + + pop bc ; row 0 + pop de ; row 1 + ex af,af' + pop af ; row 2 + ex af,af' + exx + pop bc ; row 3 + pop de ; row 4 + pop hl ; row 5 + exx + + ; All right. We're loaded. Time to dump! + + ld ix,0 + add ix,sp ; Save our stack pointer into ix + + ld sp,hl ; point at the screen. + push bc ; row 0 + + inc h + ld sp,hl + push de ; row 1 + + inc h + ld sp,hl + ex af,af' + push af ; row 2 + + inc h + ld sp,hl + exx + push bc ; row 3 + exx + + inc h + ld sp,hl + exx + push de ; row 4 + exx + + inc h + ld sp,hl + exx + push hl ; row 5 + exx + + ; We're empty. Time to load up again. + + ld sp,ix + pop bc ; row 6 + pop de ; row 7 + ex af,af' + pop af ; row 8 + ex af,af' + exx + pop bc ; row 9 + pop de ; row 10 + pop hl ; row 11 + exx + + ; and we're loaded up again! Time to dump this graphic on the screen. + + ld ix,0 + add ix,sp ; save SP in IX + + inc h + ld sp,hl + push bc ; row 6 + + inc h + ld sp,hl + push de ; row 7 + + dec hl + dec hl + + ; Aha. Snag. We're at the bottom of a character. What's the next address down? + ld a,l + and 224 + cp 224 + jr nz, ptNextThird3 + +ptSameThird3: + ld de,32 + add hl,de + jp ptAddrDone3 + +ptNextThird3: + ld de,-1760 + add hl,de + +ptAddrDone3: + inc hl + inc hl + + ld sp,hl + ex af,af' + push af ; row 8 + + inc h + ld sp,hl + exx + push bc ; row 9 + exx + + inc h + ld sp,hl + exx + push de ; row 10 + exx + + inc h + ld sp,hl + exx + push hl ; row 11 + exx + + ; Okay. Registers empty. Reload time! + ld sp,ix + pop bc ; row 12 + pop de ; row 13 + + exx + pop bc ; row 14 + pop de ; row 15 + pop hl ; top attrs + exx + + ex af,af' + pop af ; bottom attrs + ex af,af' + + ; and the last dump to screen + + inc h + ld sp,hl + push bc + + inc h + ld sp,hl + push de + + inc h + ld sp,hl + exx + push bc + exx + + inc h + ld sp,hl + exx + push de + exx + + ; Pixels done. Just need to do the attributes. + ; So set HL to the attr address: + + ld a,iyl ;ypos + + rrca + rrca + rrca ; Multiply by 32 + ld l,a ; Pass to L + and 3 ; Mask with 00000011 +PT_ATTR_HI: + add a,88 ; patched with the real SCREEN_ATTR_ADDR high byte on entry + ld h,a ; Put it in the High Byte + ld a,l ; We get y value *32 + and 224 ; Mask with 11100000 + ld l,a ; Put it in L + ld a,iyh ; xpos + adc a,l ; Add it to the Low byte + ld l,a ; Put it back in L, and we're done. HL=Address. + inc hl ; we need to be to the right of the attr point as pushes write backwards. + inc hl + + ; attr + ld sp,hl + exx + push hl ; top row + exx + + ld hl,34 ; we need to move down to the next row. We already backed up 2, so we add 34. + add hl,sp + ld sp,hl + ex af,af' ; bottom row + push af + +ptNextSprite2: + ; done. Cleanup. + ld sp,(ptstackSave) ; put our stack back together. + + ; done all 4 final clean up + + pop iy + pop ix + + pop af ; recover interrupt status + jp po, pt_nointerrupts + ei ; Okay. We put everything back. If you need interrupts, you can go with em. + +pt_nointerrupts: + ENDP + END ASM +END SUB + +#require "sysvars.asm" + +#pragma pop(case_insensitive) + +#endif diff --git a/src/lib/arch/zx81sd/stdlib/scroll.bas b/src/lib/arch/zx81sd/stdlib/scroll.bas new file mode 100644 index 000000000..303349adf --- /dev/null +++ b/src/lib/arch/zx81sd/stdlib/scroll.bas @@ -0,0 +1,1043 @@ +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008, 2025 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' and Conrado Badenas +' +' Version zx81sd: identica a la de zx48k/stdlib/scroll.bas salvo por el +' calculo de direccion de pixel. El original llama directamente a +' PIXEL-ADD ($22ACh), rutina de la ROM del Spectrum; en zx81sd no hay ROM +' mapeada en tiempo de ejecucion, asi que esa direccion cae en mitad de +' nuestro propio codigo compilado (causaba HALT via un salto salvaje). +' Se sustituye por runtime/pixel_addr.asm, nuestra propia implementacion +' con el MISMO contrato de registros que la rutina de ROM (documentado +' en el propio fichero): entrada A=191,B=Y,C=X: salida HL=offset, +' A=X AND 7; destruye B, preserva D y E — por eso encaja sin mas cambios +' en el codigo de mas abajo, que fue escrito para la ROM del Spectrum. +' SP.PixelDown/SP.PixelUp no se tocan: son aritmetica pura sobre +' SCREEN_ADDR, sin llamadas a ROM, y ya funcionan igual en zx81sd. +' ---------------------------------------------------------------- + +#ifndef __LIBRARY_SCROLL__ + +REM Avoid recursive / multiple inclusion + +#define __LIBRARY_SCROLL__ + +#pragma push(case_insensitive) +#pragma case_insensitive = True + +' ---------------------------------------------------------------- +' sub ScrollRight +' pixel by pixel right scroll +' scrolls 1 pixel right the window defined by (x1, y1, x2, y2) +' ---------------------------------------------------------------- +sub fastcall ScrollRight(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) + asm + push namespace core + + PROC + LOCAL LOOP1, LOOP2, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + +; Read parameters, return if they are bad + ; a = x1 + pop hl ; RET address + pop bc ; b = y1 + pop de ; d = x2 + ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now + + ld c, a ; BC = y1x1 + ld a, d + sub c + ret c ; x1 > x2 + + ld a, h + sub b + ret c ; y1 > y2 + +; Compute height and masks + inc a + ld e, a ; e = y2 - y1 + 1 + + ld a,c ;e.g. x1 = 3, mask1 = %11100000, CPL = %00011111 + and 7 + inc a + ld b,a ;B = 1 + (x1 MOD 8) = 1,2,...,8 for x1 = 0,1,...,7 + 8*n + xor a +MASK1: + rra + scf ;inject B-1 1s from the left + djnz MASK1 + ld (AND1+1),a ;e.g. %11100000 + ld (AND5+1),a + cpl + ld (AND2+1),a ;e.g. %00011111 + ld (AND7+1),a + + ld a,d ;e.g. x2 = 5, mask2 = %11111100, CPL = %00000011 + and 7 + inc a + ld b,a ;B = 1 + (x2 MOD 8) = 1,2,...,8 for x2 = 0,1,...,7 + 8*n + xor a +MASK2: + scf ;inject B 1s from the left + rra + djnz MASK2 + ld (AND4+1),a ;e.g. %11111100 + ld (AND8+1),a + cpl + ld (AND3+1),a ;e.g. %00000011 + ld (AND6+1),a + +; Compute Ncols and Display File address, and choose branch + ld a,c ;x1 + and %11111000 + rrca + rrca + rrca + ld b,a ;col1 + ld a,d ;x2 + and %11111000 + rrca + rrca + rrca ;col2 + sub b ;A = Ncols - 1 = col2 - col1 + ex af,af' ;save Ncols-1 and ZeroFlag + + ld b, h ; BC = y2x1 + ld a, 191 + call PIXEL_ADDR + res 6, h ; Starts from 0 + ld bc, (SCREEN_ADDR) + add hl, bc ; Now current offset + + ex af,af' ;load Ncols-1 and ZeroFlag + jr z,NEQ1 + +; N > 1 (there are 2 or more columns) + dec a ;A = Ncols - 2 + ld d,a ;D = N-2 +LOOP1: + push hl +; Scroll column 1 + ld a,(hl) +AND1: + and %11100000 ;e.g. x1 = 3 + ld c,a ;get out-window part of byte + ld a,(hl) +AND2: + and %00011111 ;e.g. x1 = 3 + rra ;scroll in-window part of byte + rl b ;CF is stored as bit0 of B + or c ;put out-window part of byte + ld (hl),a + inc hl +; Scroll columns 2,3,...,N-1 + ld a,d ;N-2 + and a ;A=0 iff N=2 + jr z,COLUMNN + rr b ;CF is on stage + ld b,a +LOOP2: + rr (hl) + inc hl + djnz LOOP2 + rl b ;CF is stored as bit0 of B +; Scroll column N +COLUMNN: + ld a,(hl) +AND3: + and %00000011 ;e.g. x2 = 5 + ld c,a ;get out-window part of byte + ld a,(hl) + rr b ;CF is on stage + rra ;scroll in-window part of byte +AND4: + and %11111100 ;e.g. x2 = 5 + or c ;put out-window part of byte + ld (hl),a +; Scroll another line + pop hl + dec e + ret z + call SP.PixelDown + jp LOOP1 + +; N = 1 (there is only one column) +NEQ1: + ld b,(hl) + ld a,b +AND5: + and %11100000 ;e.g. x1 = 3 + ld c,a + ld a,b +AND6: + and %00000011 ;e.g. x2 = 5 + or c + ld c,a ;get out-window part of byte + ld a,b +AND7: + and %00011111 ;e.g. x1 = 3 + rra ;scroll in-window part of byte +AND8: + and %11111100 ;e.g. x2 = 5 + or c ;put out-window part of byte + ld (hl),a +; Scroll another line + dec e + ret z + call SP.PixelDown + jp NEQ1 + ENDP + + pop namespace + end asm +end sub + +' ---------------------------------------------------------------- +' sub ScrollLeft +' pixel by pixel left scroll +' scrolls 1 pixel left the window defined by (x1, y1, x2, y2) +' ---------------------------------------------------------------- +sub fastcall ScrollLeft(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) + asm + push namespace core + + PROC + LOCAL LOOP1, LOOP2, MASK1, MASK2, COLUMN1, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + +; Read parameters, return if they are bad + ; a = x1 + pop hl ; RET address + pop bc ; b = y1 + pop de ; d = x2 + ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now + + ld c, a ; BC = y1x1 + ld a, d + sub c + ret c ; x1 > x2 + + ld a, h + sub b + ret c ; y1 > y2 + +; Compute height and masks + inc a + ld e, a ; e = y2 - y1 + 1 + + ld a,c ;e.g. x1 = 3, mask1 = %11100000, CPL = %00011111 + and 7 + inc a + ld b,a ;B = 1 + (x1 MOD 8) = 1,2,...,8 for x1 = 0,1,...,7 + 8*n + xor a +MASK1: + rra + scf ;inject B-1 1s from the left + djnz MASK1 + ld (AND3+1),a ;e.g. %00000011 + ld (AND6+1),a + cpl + ld (AND4+1),a ;e.g. %11111100 + ld (AND8+1),a + + ld a,d ;e.g. x2 = 5, mask2 = %11111100, CPL = %00000011 + and 7 + inc a + ld b,a ;B = 1 + (x2 MOD 8) = 1,2,...,8 for x2 = 0,1,...,7 + 8*n + xor a +MASK2: + scf ;inject B 1s from the left + rra + djnz MASK2 + ld (AND2+1),a ;e.g. %00011111 + ld (AND7+1),a + cpl + ld (AND1+1),a ;e.g. %11100000 + ld (AND5+1),a + +; Compute Ncols and Display File address, and choose branch + ld a,c ;x1 + and %11111000 + rrca + rrca + rrca + ld b,a ;col1 + ld a,d ;x2 + and %11111000 + rrca + rrca + rrca ;col2 + sub b ;A = Ncols - 1 = col2 - col1 + ex af,af' ;save Ncols-1 and ZeroFlag + + ld c, d + ld b, h ; BC = y2x2 + ld a, 191 + call PIXEL_ADDR + res 6, h ; Starts from 0 + ld bc, (SCREEN_ADDR) + add hl, bc ; Now current offset + + ex af,af' ;load Ncols-1 and ZeroFlag + jr z,NEQ1 + +; N > 1 (there are 2 or more columns) + dec a ;A = Ncols - 2 + ld d,a ;D = N-2 +LOOP1: + push hl +; Scroll column N + ld a,(hl) +AND1: + and %00000011 ;e.g. x2 = 5 + ld c,a ;get out-window part of byte + ld a,(hl) +AND2: + and %11111100 ;e.g. x2 = 5 + rla ;scroll in-window part of byte + rl b ;CF is stored as bit0 of B + or c ;put out-window part of byte + ld (hl),a + dec hl +; Scroll columns N-1,...,3,2 + ld a,d ;N-2 + and a ;A=0 iff N=2 + jr z,COLUMN1 + rr b ;CF is on stage + ld b,a +LOOP2: + rl (hl) + dec hl + djnz LOOP2 + rl b ;CF is stored as bit0 of B +; Scroll column 1 +COLUMN1: + ld a,(hl) +AND3: + and %11100000 ;e.g. x1 = 3 + ld c,a ;get out-window part of byte + ld a,(hl) + rr b ;CF is on stage + rla ;scroll in-window part of byte +AND4: + and %00011111 ;e.g. x1 = 3 + or c ;put out-window part of byte + ld (hl),a +; Scroll another line + pop hl + dec e + ret z + call SP.PixelDown + jp LOOP1 + +; N = 1 (there is only one column) +NEQ1: + ld b,(hl) + ld a,b +AND5: + and %00000011 ;e.g. x2 = 5 + ld c,a + ld a,b +AND6: + and %11100000 ;e.g. x1 = 3 + or c + ld c,a ;get out-window part of byte + ld a,b +AND7: + and %11111100 ;e.g. x2 = 5 + rla ;scroll in-window part of byte +AND8: + and %00011111 ;e.g. x1 = 3 + or c ;put out-window part of byte + ld (hl),a +; Scroll another line + dec e + ret z + call SP.PixelDown + jp NEQ1 + ENDP + + pop namespace + end asm +end sub + + +' ---------------------------------------------------------------- +' sub ScrollUp +' pixel by pixel up scroll +' scrolls 1 pixel up the window defined by (x1, y1, x2, y2) +' ---------------------------------------------------------------- +sub fastcall ScrollUp(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) + asm + push namespace core + + PROC + LOCAL LOOP1, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + LOCAL EMPTYLINE + +; Read parameters, return if they are bad + ; a = x1 + pop hl ; RET address + pop bc ; b = y1 + pop de ; d = x2 + ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now + + ld c, a ; BC = y1x1 + ld a, d + sub c + ret c ; x1 > x2 + + ld a, h + sub b + ret c ; y1 > y2 + +; Compute height and masks + push ix + inc a + ld ixL,a;ixL = y2 - y1 + 1 + + ld a,c ;e.g. x1 = 3, mask1 = %11100000, CPL = %00011111 + and 7 + inc a + ld b,a ;B = 1 + (x1 MOD 8) = 1,2,...,8 for x1 = 0,1,...,7 + 8*n + xor a +MASK1: + rra + scf ;inject B-1 1s from the left + djnz MASK1 + ld (AND1+1),a ;e.g. %11100000 + ld (AND5+1),a + cpl + ld (AND2+1),a ;e.g. %00011111 + ld (AND7+1),a + + ld a,d ;e.g. x2 = 5, mask2 = %11111100, CPL = %00000011 + and 7 + inc a + ld b,a ;B = 1 + (x2 MOD 8) = 1,2,...,8 for x2 = 0,1,...,7 + 8*n + xor a +MASK2: + scf ;inject B 1s from the left + rra + djnz MASK2 + ld (AND4+1),a ;e.g. %11111100 + ld (AND8+1),a + cpl + ld (AND3+1),a ;e.g. %00000011 + ld (AND6+1),a + +; Compute Ncols and Display File address, and choose branch + ld a,c ;x1 + and %11111000 + rrca + rrca + rrca + ld b,a ;col1 + ld a,d ;x2 + and %11111000 + rrca + rrca + rrca ;col2 + sub b ;A = Ncols - 1 = col2 - col1 + ex af,af' ;save Ncols-1 and ZeroFlag + + ld b, h ; BC = y2x1 + ld a, 191 + call PIXEL_ADDR + res 6, h ; Starts from 0 + ld bc, (SCREEN_ADDR) + add hl, bc ; Now current offset + + ex af,af' ;load Ncols-1 and ZeroFlag + jr z,NEQ1 + +; N > 1 (there are 2 or more columns) + dec a ;A = Ncols - 2 + ld ixH,a ;save Ncols-2 + ld b,0 +LOOP1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE ;HL at empty line + call nz,SP.PixelDown ;HL at line below + inc ixL ;restore iterative variable for a second check + push hl +; Scroll column 1 + ld a,(hl) +AND2: + and %00011111 ;e.g. x1 = 3 + ld c,a ;get in-window part of byte + ld a,(de) +AND1: + and %11100000 ;e.g. x1 = 3 + or c ;put in-window part of byte + ld (de),a + inc hl + inc de +; Scroll columns 2,3,...,N-1 + ld a,ixH ;load Ncols-2 + and a + jr z,COLUMNN + ld c,a + ldir +; Scroll column N +COLUMNN: + ld a,(hl) +AND4: + and %11111100 ;e.g. x2 = 5 + ld c,a ;get in-window part of byte + ld a,(de) +AND3: + and %00000011 ;e.g. x2 = 5 + or c ;put in-window part of byte + ld (de),a +; Scroll another line + pop hl + dec ixL + jp nz,LOOP1 + pop ix + ret + +; N = 1 (there is only one column) +NEQ1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE ;HL at empty line + call nz,SP.PixelDown ;HL at line below + inc ixL ;restore iterative variable for a second check + + ld a,(hl) +AND7: + and %00011111 ;e.g. x1 = 3 +AND8: + and %11111100 ;e.g. x2 = 5 + ld c,a ;get in-window part of byte + ld a,(de) +AND5: + and %11100000 ;e.g. x1 = 3 + ld b,a + ld a,(de) +AND6: + and %00000011 ;e.g. x2 = 5 + or b + or c ;put in-window part of byte + ld (de),a +; Scroll another line + dec ixL + jp nz,NEQ1 + pop ix + ret + + defs 32,0 ;empty line with 32 zero-bytes +EMPTYLINE: + ld hl,EMPTYLINE-32 + ENDP + + pop namespace + end asm +end sub + + +' ---------------------------------------------------------------- +' sub ScrollDown +' pixel by pixel down scroll +' scrolls 1 pixel down the window defined by (x1, y1, x2, y2) +' ---------------------------------------------------------------- +sub fastcall ScrollDown(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) + asm + push namespace core + + PROC + LOCAL LOOP1, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + LOCAL EMPTYLINE + +; Read parameters, return if they are bad + ; a = x1 + pop hl ; RET address + pop bc ; b = y1 + pop de ; d = x2 + ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now + + ld c, a ; BC = y1x1 + ld a, d + sub c + ret c ; x1 > x2 + + ld a, h + sub b + ret c ; y1 > y2 + +; Compute height and masks + push ix + inc a + ld ixL,a;ixL = y2 - y1 + 1 + ld h,b ;y1 + + ld a,c ;e.g. x1 = 3, mask1 = %11100000, CPL = %00011111 + and 7 + inc a + ld b,a ;B = 1 + (x1 MOD 8) = 1,2,...,8 for x1 = 0,1,...,7 + 8*n + xor a +MASK1: + rra + scf ;inject B-1 1s from the left + djnz MASK1 + ld (AND1+1),a ;e.g. %11100000 + ld (AND5+1),a + cpl + ld (AND2+1),a ;e.g. %00011111 + ld (AND7+1),a + + ld a,d ;e.g. x2 = 5, mask2 = %11111100, CPL = %00000011 + and 7 + inc a + ld b,a ;B = 1 + (x2 MOD 8) = 1,2,...,8 for x2 = 0,1,...,7 + 8*n + xor a +MASK2: + scf ;inject B 1s from the left + rra + djnz MASK2 + ld (AND4+1),a ;e.g. %11111100 + ld (AND8+1),a + cpl + ld (AND3+1),a ;e.g. %00000011 + ld (AND6+1),a + +; Compute Ncols and Display File address, and choose branch + ld a,c ;x1 + and %11111000 + rrca + rrca + rrca + ld b,a ;col1 + ld a,d ;x2 + and %11111000 + rrca + rrca + rrca ;col2 + sub b ;A = Ncols - 1 = col2 - col1 + ex af,af' ;save Ncols-1 and ZeroFlag + + ld b, h ; BC = y1x1 + ld a, 191 + call PIXEL_ADDR + res 6, h ; Starts from 0 + ld bc, (SCREEN_ADDR) + add hl, bc ; Now current offset + + ex af,af' ;load Ncols-1 and ZeroFlag + jr z,NEQ1 + +; N > 1 (there are 2 or more columns) + dec a ;A = Ncols - 2 + ld ixH,a ;save Ncols-2 + ld b,0 +LOOP1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE ;HL at empty line + call nz,SP.PixelUp ;HL at line above + inc ixL ;restore iterative variable for a second check + push hl +; Scroll column 1 + ld a,(hl) +AND2: + and %00011111 ;e.g. x1 = 3 + ld c,a ;get in-window part of byte + ld a,(de) +AND1: + and %11100000 ;e.g. x1 = 3 + or c ;put in-window part of byte + ld (de),a + inc hl + inc de +; Scroll columns 2,3,...,N-1 + ld a,ixH ;load Ncols-2 + and a + jr z,COLUMNN + ld c,a + ldir +; Scroll column N +COLUMNN: + ld a,(hl) +AND4: + and %11111100 ;e.g. x2 = 5 + ld c,a ;get in-window part of byte + ld a,(de) +AND3: + and %00000011 ;e.g. x2 = 5 + or c ;put in-window part of byte + ld (de),a +; Scroll another line + pop hl + dec ixL + jp nz,LOOP1 + pop ix + ret + +; N = 1 (there is only one column) +NEQ1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE ;HL at empty line + call nz,SP.PixelUp ;HL at line below + inc ixL ;restore iterative variable for a second check + + ld a,(hl) +AND7: + and %00011111 ;e.g. x1 = 3 +AND8: + and %11111100 ;e.g. x2 = 5 + ld c,a ;get in-window part of byte + ld a,(de) +AND5: + and %11100000 ;e.g. x1 = 3 + ld b,a + ld a,(de) +AND6: + and %00000011 ;e.g. x2 = 5 + or b + or c ;put in-window part of byte + ld (de),a +; Scroll another line + dec ixL + jp nz,NEQ1 + pop ix + ret + + defs 32,0 ;empty line with 32 zero-bytes +EMPTYLINE: + ld hl,EMPTYLINE-32 + ENDP + + pop namespace + end asm +end sub + + +' ---------------------------------------------------------------- +' sub ScrollRightAligned +' pixel by pixel right scroll. +' scrolls 1 pixel right the window defined by (x1, y1, x2, y2) +' This scroll is aligned to columns. +' x1 and x2 are divided by 8 and rounded down (floor) to get the column number (0..31) +' ---------------------------------------------------------------- +sub fastcall ScrollRightAligned(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) + asm + push namespace core + + PROC + LOCAL LOOP1 + LOCAL LOOP2 + + ; a = x1 + pop hl ; RET address + pop bc ; b = y1 + pop de ; d = x2 + ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now + + ld c, a ; BC = y1x1 + ld a, d + sub c + ret c ; x1 > x2 + + srl a + srl a + srl a + inc a + ld e, a ; e = (x2 - x1) / 8 + 1 + + ld a, h + sub b + ret c ; y1 > y2 + + inc a + ld d, a ; d = y2 - y1 + 1 + + ld b, h ; BC = y2x1 + ld a, 191 + call PIXEL_ADDR + res 6, h ; Starts from 0 + ld bc, (SCREEN_ADDR) + add hl, bc ; Now current offset + +LOOP1: + push hl + ld b, e ; C cols + or a ; clear carry flag +LOOP2: + rr (hl) + inc hl + djnz LOOP2 + pop hl + + dec d + ret z + call SP.PixelDown + jp LOOP1 + ENDP + + pop namespace + end asm +end sub + + +' ---------------------------------------------------------------- +' sub ScrolLeftAligned +' pixel by pixel left scroll +' scrolls 1 pixel left the window defined by (x1, y1, x2, y2) +' This scroll is aligned to columns. +' x1 and x2 are divided by 8 and rounded down (floor) to get the column number (0..31) +' ---------------------------------------------------------------- +sub fastcall ScrollLeftAligned(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) + asm + push namespace core + + PROC + LOCAL LOOP1 + LOCAL LOOP2 + + ; a = x1 + pop hl ; RET address + pop bc ; b = y1 + pop de ; d = x2 + ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now + + ld c, a ; BC = y1x1 + ld a, d + sub c + ret c ; x1 > x2 + + srl a + srl a + srl a + inc a + ld e, a ; e = (x2 - x1) / 8 + 1 + + ld a, h + sub b + ret c ; y1 > y2 + + ld c, d + inc a + ld d, a ; d = y2 - y1 + 1 + + ld b, h ; BC = y2x1 + ld a, 191 + call PIXEL_ADDR + res 6, h ; Starts from 0 + ld bc, (SCREEN_ADDR) + add hl, bc ; Now current offset + +LOOP1: + push hl + ld b, e ; C cols + or a ; clear carry flag +LOOP2: + rl (hl) + dec hl + djnz LOOP2 + pop hl + + dec d + ret z + call SP.PixelDown + jp LOOP1 + ENDP + + pop namespace + end asm +end sub + + +' ---------------------------------------------------------------- +' sub ScrolUpAligned +' pixel by pixel up scroll +' scrolls 1 pixel up the window defined by (x1, y1, x2, y2) +' This scroll is aligned to columns. +' x1 and x2 are divided by 8 and rounded down (floor) to get the column number (0..31) +' ---------------------------------------------------------------- +sub fastcall ScrollUpAligned(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) + asm + push namespace core + + PROC + LOCAL LOOP1 + + ; a = x1 + pop hl ; RET address + pop bc ; b = y1 + pop de ; d = x2 + ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now + + ld c, a ; BC = y1x1 + ld a, d + sub c + ret c ; x1 > x2 + + srl a + srl a + srl a + inc a + ld e, a ; e = (x2 - x1) / 8 + 1 + ex af, af' ; save it for later + + ld a, h + sub b + ret c ; y1 > y2 + + inc a + ld d, a ; d = y2 - y1 + 1 + + ld b, h ; BC = y2x1 + ld a, 191 + call PIXEL_ADDR + res 6, h ; Starts from 0 + ld bc, (SCREEN_ADDR) + add hl, bc ; Now current offset + + ld a, d ; Num. of scan lines + ld b, 0 + exx + ld b, a ; Scan lines counter + ex af, af' ; Recovers cols + ld c, a + jp LOOP_START + +LOOP1: + exx + ld d, h + ld e, l + ld c, a ; C cols + call SP.PixelDown + push hl + ldir + pop hl + exx + ld a, c ; Recovers C Cols + LOCAL LOOP_START +LOOP_START: + djnz LOOP1 + + ; Clears bottom line + exx + ld (hl), 0 + ld d, h + ld e, l + inc de + ld c, a + dec c + ret z + ldir + ENDP + + pop namespace + end asm +end sub + + +' ---------------------------------------------------------------- +' sub ScrolDownAligned +' pixel by pixel down scroll +' scrolls 1 pixel down the window defined by (x1, y1, x2, y2) +' This scroll is aligned to columns. +' x1 and x2 are divided by 8 and rounded down (floor) to get the column number (0..31) +' ---------------------------------------------------------------- +sub fastcall ScrollDownAligned(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) + asm + push namespace core + + PROC + LOCAL LOOP1 + + ; a = x1 + pop hl ; RET address + pop bc ; b = y1 + pop de ; d = x2 + ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now + + ld c, a ; BC = y1x1 + ld a, d + sub c + ret c ; x1 > x2 + + srl a + srl a + srl a + inc a + ld e, a ; e = (x2 - x1) / 8 + 1 + ex af, af' ; save it for later + + ld a, h + sub b + ret c ; y1 > y2 + + inc a + ld d, a ; d = y2 - y1 + 1 + + ld a, 191 + call PIXEL_ADDR + res 6, h ; Starts from 0 + ld bc, (SCREEN_ADDR) + add hl, bc ; Now current offset + + ld a, d ; Num. of scan lines + ld b, 0 + exx + ld b, a ; Scan lines counter + ex af, af' ; Recovers cols + ld c, a + jp LOOP_START + +LOOP1: + exx + ld d, h + ld e, l + ld c, a ; C cols + call SP.PixelUp + push hl + ldir + pop hl + exx + ld a, c ; Recovers C Cols + LOCAL LOOP_START +LOOP_START: + djnz LOOP1 + + ; Clears top line + exx + ld (hl), 0 + ld d, h + ld e, l + inc de + ld c, a + dec c + ret z + ldir + + ENDP + + pop namespace + end asm +end sub + + +#pragma pop(case_insensitive) + +REM the following is required, because it defines SCREEN_ADDR and SCREEN_ATTR_ADDR +#require "sysvars.asm" +#require "SP/PixelDown.asm" +#require "SP/PixelUp.asm" +#require "pixel_addr.asm" + +#endif diff --git a/src/lib/arch/zxnext/runtime/SP/CharLeft.asm b/src/lib/arch/zxnext/runtime/SP/CharLeft.asm index 641754bfa..0d19132f1 100644 --- a/src/lib/arch/zxnext/runtime/SP/CharLeft.asm +++ b/src/lib/arch/zxnext/runtime/SP/CharLeft.asm @@ -1,59 +1,8 @@ +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; CharLeft -; Alvin Albrecht 2002 -; - -;INCLUDE "SPconfig.def" -;XLIB SPCharLeft - -; Char Left -; -; Adjusts screen address HL to move one character to the left -; on the display. Start of line wraps to the previous row. -; -; enter: HL = valid screen address -; Carry reset -; exit : Carry = moved off screen -; HL = moves one character left, with line wrap -; used : AF, HL - -;IF !DISP_HIRES - - push namespace core - -SP.CharLeft: - ld a,l - dec l - or a - ret nz - ld a,h - sub $08 - ld h,a - cp $40 - ret - - pop namespace - - -;ELSE - -;.SPCharLeft -; ld a,h -; xor $20 -; ld h,a -; cp $58 -; ccf -; ret nc -; ld a,l -; dec l -; or a -; ret nz -; ld a,h -; sub $08 -; ld h,a -; and $18 -; cp $18 -; ccf -; ret +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -; ENDIF \ No newline at end of file +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/SP/CharRight.asm b/src/lib/arch/zxnext/runtime/SP/CharRight.asm index 1a47d84cb..52ce45186 100644 --- a/src/lib/arch/zxnext/runtime/SP/CharRight.asm +++ b/src/lib/arch/zxnext/runtime/SP/CharRight.asm @@ -1,54 +1,8 @@ +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; CharRight -; Alvin Albrecht 2002 -; - -;INCLUDE "SPconfig.def" -;XLIB SPCharRight - -; Char Right -; -; Adjusts screen address HL to move one character to the right -; on the display. End of line wraps to the next row. -; -; enter: HL = valid screen address -; Carry reset -; exit : Carry = moved off screen -; HL = moves one character right, with line wrap -; used : AF, HL - -;IF !DISP_HIRES - - push namespace core - -SP.CharRight: - inc l - ret nz - ld a,8 - add a,h - ld h,a - cp $58 - ccf - ret - - pop namespace - - -;ELSE - -;.SPCharRight -; ld a,h -; xor $20 -; ld h,a -; cp $58 -; ret nc -; inc l -; ret nz -; ld a,8 -; add a,h -; ld h,a -; cp $58 -; ccf -; ret +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -; ENDIF \ No newline at end of file +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/SP/GetScrnAddr.asm b/src/lib/arch/zxnext/runtime/SP/GetScrnAddr.asm index 21f9a4afc..faca1b505 100644 --- a/src/lib/arch/zxnext/runtime/SP/GetScrnAddr.asm +++ b/src/lib/arch/zxnext/runtime/SP/GetScrnAddr.asm @@ -1,62 +1,8 @@ +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; GetScrnAddr -; Alvin Albrecht 2002 -; - -; Get Screen Address -; -; Computes the screen address given a valid pixel coordinate. -; (0,0) is located at the top left corner of the screen. -; -; enter: h = y coord -; l = x coord -; In hi-res mode, Carry is most significant bit of x coord (0..511 pixels) -; exit : de = screen address, b = pixel mask -; uses : af, b, de, hl - - push namespace core - PROC - LOCAL rotloop, norotate - -SPGetScrnAddr: - ld a,h - and $07 - ld d,a - ld a,h - rra - rra - rra - and $18 - or d - ld d,a - - ld a,l - and $07 - ld b,a - ld a,$80 - jr z, norotate - -rotloop: - rra - djnz rotloop - -norotate: - ld b,a - srl l - srl l - srl l - ld a,h - rla - rla - and $e0 - or l - ld e,a - ld hl, (SCREEN_ADDR) - add hl, de - ex de, hl - ret - - ENDP - pop namespace +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -#include once +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/SP/PixelDown.asm b/src/lib/arch/zxnext/runtime/SP/PixelDown.asm index 3b5aaf62e..e644ae996 100644 --- a/src/lib/arch/zxnext/runtime/SP/PixelDown.asm +++ b/src/lib/arch/zxnext/runtime/SP/PixelDown.asm @@ -1,57 +1,8 @@ +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; PixelDown -; Alvin Albrecht 2002 -; - -; Pixel Down -; -; Adjusts screen address HL to move one pixel down in the display. -; (0,0) is located at the top left corner of the screen. -; -; enter: HL = valid screen address -; exit : Carry = moved off screen -; Carry'= moved off current cell (needs ATTR update) -; HL = moves one pixel down -; used : AF, HL - - push namespace core - -SP.PixelDown: - PROC - LOCAL leave - - push de - ld de, (SCREEN_ADDR) - or a - sbc hl, de - inc h - ld a,h - and $07 - jr nz, leave - scf ; Sets carry on F', which flags ATTR must be updated - ex af, af' - ld a,h - sub $08 - ld h,a - ld a,l - add a,$20 - ld l,a - jr nc, leave - ld a,h - add a,$08 - ld h,a - cp $19 ; carry = 0 => Out of screen - jr c, leave ; returns if out of screen - ccf - pop de - ret - -leave: - add hl, de ; This always sets Carry = 0 - pop de - ret - - ENDP - pop namespace +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -#include once \ No newline at end of file +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/SP/PixelLeft.asm b/src/lib/arch/zxnext/runtime/SP/PixelLeft.asm index 20be4e294..39f9fb9f3 100644 --- a/src/lib/arch/zxnext/runtime/SP/PixelLeft.asm +++ b/src/lib/arch/zxnext/runtime/SP/PixelLeft.asm @@ -1,52 +1,8 @@ +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; PixelLeft -; Jose Rodriguez 2012 -; - -; PixelLeft -; -; Adjusts screen address HL and Pixel bit A to move one pixel to the left -; on the display. Start of line set Carry (Out of Screen) -; -; enter: HL = valid screen address -; A = Bit Set -; exit : Carry = moved off screen -; Carry' Set if moved off current ATTR CELL -; HL = moves one character left, if needed -; A = Bit Set with new pixel pos. -; used : AF, HL - - - push namespace core - -SP.PixelLeft: - PROC - - LOCAL leave - - push de - ld de, (SCREEN_ADDR) - or a - sbc hl, de ; This always sets Carry = 0 - - rlca ; Sets new pixel bit 1 to the right - jr nc, leave - ex af, af' ; Signal in C' we've moved off current ATTR cell - ld a,l - dec a - ld l,a - cp 32 ; Carry if in screen - ccf - ld a, 1 - -leave: ; Sets screen offset back again - push af - add hl, de - pop af - pop de - ret - - ENDP - pop namespace +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -#include once +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/SP/PixelRight.asm b/src/lib/arch/zxnext/runtime/SP/PixelRight.asm index 5c0688e93..8d7e8eda4 100644 --- a/src/lib/arch/zxnext/runtime/SP/PixelRight.asm +++ b/src/lib/arch/zxnext/runtime/SP/PixelRight.asm @@ -1,53 +1,8 @@ +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; PixelRight -; Jose Rodriguez 2012 -; - - -; PixelRight -; -; Adjusts screen address HL and Pixel bit A to move one pixel to the left -; on the display. Start of line set Carry (Out of Screen) -; -; enter: HL = valid screen address -; A = Bit Set -; exit : Carry = moved off screen -; Carry' Set if moved off current ATTR CELL -; HL = moves one character left, if needed -; A = Bit Set with new pixel pos. -; used : AF, HL - - - push namespace core - -SP.PixelRight: - PROC - - LOCAL leave - - push de - ld de, (SCREEN_ADDR) - or a - sbc hl, de ; This always sets Carry = 0 - - rrca ; Sets new pixel bit 1 to the right - jr nc, leave - ex af, af' ; Signal in C' we've moved off current ATTR cell - ld a, l - inc a - ld l, a - cp 32 ; Carry if IN screen - ccf - ld a, 80h - -leave: ; Sets screen offset back again - push af - add hl, de - pop af - pop de - ret - - ENDP - pop namespace +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -#include once +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/SP/PixelUp.asm b/src/lib/arch/zxnext/runtime/SP/PixelUp.asm index 1657f9571..cd99f7998 100644 --- a/src/lib/arch/zxnext/runtime/SP/PixelUp.asm +++ b/src/lib/arch/zxnext/runtime/SP/PixelUp.asm @@ -1,56 +1,8 @@ +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; PixelUp -; Alvin Albrecht 2002 -; - -; Pixel Up -; -; Adjusts screen address HL to move one pixel up in the display. -; (0,0) is located at the top left corner of the screen. -; -; enter: HL = valid screen address -; exit : Carry = moved off screen -; HL = moves one pixel up -; used : AF, HL - - push namespace core - -SP.PixelUp: - PROC - - LOCAL leave - - push de - ld de, (SCREEN_ADDR) - or a - sbc hl, de - - ld a,h - dec h - and $07 - jr nz, leave - scf ; sets C' to 1 (ATTR update needed) - ex af, af' - ld a,$08 - add a,h - ld h,a - ld a,l - sub $20 - ld l,a - jr nc, leave - ld a,h - sub $08 - ld h,a - -leave: - push af - add hl, de - pop af - pop de - ret - - ENDP - - pop namespace +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -#include once +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/abs16.asm b/src/lib/arch/zxnext/runtime/abs16.asm index 08a7699ff..c0ea9bac3 100644 --- a/src/lib/arch/zxnext/runtime/abs16.asm +++ b/src/lib/arch/zxnext/runtime/abs16.asm @@ -1,5 +1,8 @@ -; 16 bit signed integer abs value -; HL = value - -#include once +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/abs32.asm b/src/lib/arch/zxnext/runtime/abs32.asm index 0ef7ebab3..1c24bcb97 100644 --- a/src/lib/arch/zxnext/runtime/abs32.asm +++ b/src/lib/arch/zxnext/runtime/abs32.asm @@ -1,5 +1,8 @@ -; 16 bit signed integer abs value -; HL = value - -#include once +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/abs8.asm b/src/lib/arch/zxnext/runtime/abs8.asm index 499c8efa0..21c135129 100644 --- a/src/lib/arch/zxnext/runtime/abs8.asm +++ b/src/lib/arch/zxnext/runtime/abs8.asm @@ -1,12 +1,8 @@ -; Returns absolute value for 8 bit signed integer +; ---------------------------------------------------------------- +; This file is released under the MIT License ; - push namespace core - -__ABS8: - or a - ret p - neg - ret - - pop namespace +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/absf.asm b/src/lib/arch/zxnext/runtime/absf.asm index 084e1d55f..a882ec4ab 100644 --- a/src/lib/arch/zxnext/runtime/absf.asm +++ b/src/lib/arch/zxnext/runtime/absf.asm @@ -1,8 +1,8 @@ -; ABS value for a floating point A EDCB - push namespace core +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -__ABSF: - res 7, e ; Sets sign to positive. Thast all! ;-) - ret - - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/arith/_mul32.asm b/src/lib/arch/zxnext/runtime/arith/_mul32.asm index 60aa191a5..48b0800f6 100644 --- a/src/lib/arch/zxnext/runtime/arith/_mul32.asm +++ b/src/lib/arch/zxnext/runtime/arith/_mul32.asm @@ -1,70 +1,8 @@ - -; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 -; Used with permission. -; Multiplies 32x32 bit integer (DEHL x D'E'H'L') -; 64bit result is returned in H'L'H L B'C'A C - - - push namespace core - -__MUL32_64START: - push hl - exx - ld b, h - ld c, l ; BC = Low Part (A) - pop hl ; HL = Load Part (B) - ex de, hl ; DE = Low Part (B), HL = HightPart(A) (must be in B'C') - push hl - - exx - pop bc ; B'C' = HightPart(A) - exx ; A = B'C'BC , B = D'E'DE - - ; multiply routine 32 * 32bit = 64bit - ; h'l'hlb'c'ac = b'c'bc * d'e'de - ; needs register a, changes flags - ; - ; this routine was with tiny differences in the - ; sinclair zx81 rom for the mantissa multiply - -__LMUL: - xor a ; reset carry flag - ld h, a ; result bits 32..47 = 0 - ld l, a - exx - ld h, a ; result bits 48..63 = 0 - ld l, a - exx - ld a,b ; mpr is b'c'ac - ld b,33 ; initialize loop counter - jp __LMULSTART - -__LMULLOOP: - jr nc,__LMULNOADD ; JP is 2 cycles faster than JR. Since it's inside a LOOP - ; it can save up to 33 * 2 = 66 cycles - ; But JR if 3 cycles faster if JUMP not taken! - add hl,de ; result += mpd - exx - adc hl,de - exx - -__LMULNOADD: - exx - rr h ; right shift upper - rr l ; 32bit of result - exx - rr h - rr l - -__LMULSTART: - exx - rr b ; right shift mpr/ - rr c ; lower 32bit of result - exx - rra ; equivalent to rr a - rr c - djnz __LMULLOOP - - ret ; result in h'l'hlb'c'ac - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/arith/addf.asm b/src/lib/arch/zxnext/runtime/arith/addf.asm index cc6b03c04..176227d05 100644 --- a/src/lib/arch/zxnext/runtime/arith/addf.asm +++ b/src/lib/arch/zxnext/runtime/arith/addf.asm @@ -1,24 +1,8 @@ -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) -; All of them uses A EDCB registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order AF DE BC (F not used). +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - push namespace core - -__ADDF: ; Addition - call __FPSTACK_PUSH2 - - ; ------------- ROM ADD - rst 28h - defb 0fh ; ADD - defb 38h; ; END CALC - - jp __FPSTACK_POP +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/arith/div16.asm b/src/lib/arch/zxnext/runtime/arith/div16.asm index 1eed19848..e11169336 100644 --- a/src/lib/arch/zxnext/runtime/arith/div16.asm +++ b/src/lib/arch/zxnext/runtime/arith/div16.asm @@ -1,104 +1,8 @@ -; 16 bit division and modulo functions -; for both signed and unsigned values - -#include once - - push namespace core - -__DIVU16: ; 16 bit unsigned division - ; HL = Dividend, Stack Top = Divisor - - ; -- OBSOLETE ; Now uses FASTCALL convention - ; ex de, hl - ; pop hl ; Return address - ; ex (sp), hl ; CALLEE Convention - -__DIVU16_FAST: - ld a, h - ld c, l - ld hl, 0 - ld b, 16 - -__DIV16LOOP: - sll c - rla - adc hl,hl - sbc hl,de - jr nc, __DIV16NOADD - add hl,de - dec c - -__DIV16NOADD: - djnz __DIV16LOOP - - ex de, hl - ld h, a - ld l, c - - ret ; HL = quotient, DE = Mudulus - - - -__MODU16: ; 16 bit modulus - ; HL = Dividend, Stack Top = Divisor - - ;ex de, hl - ;pop hl - ;ex (sp), hl ; CALLEE Convention - - call __DIVU16_FAST - ex de, hl ; hl = reminder (modulus) - ; de = quotient - - ret - - -__DIVI16: ; 16 bit signed division - ; --- The following is OBSOLETE --- - ; ex de, hl - ; pop hl - ; ex (sp), hl ; CALLEE Convention - -__DIVI16_FAST: - ld a, d - xor h - ex af, af' ; BIT 7 of a contains result - - bit 7, d ; DE is negative? - jr z, __DIVI16A - - ld a, e ; DE = -DE - cpl - ld e, a - ld a, d - cpl - ld d, a - inc de - -__DIVI16A: - bit 7, h ; HL is negative? - call nz, __NEGHL - -__DIVI16B: - call __DIVU16_FAST - ex af, af' - - or a - ret p ; return if positive - jp __NEGHL - - -__MODI16: ; 16 bit modulus - ; HL = Dividend, Stack Top = Divisor - - ;ex de, hl - ;pop hl - ;ex (sp), hl ; CALLEE Convention - - call __DIVI16_FAST - ex de, hl ; hl = reminder (modulus) - ; de = quotient - - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/arith/div32.asm b/src/lib/arch/zxnext/runtime/arith/div32.asm index 968a6879a..95758a3b3 100644 --- a/src/lib/arch/zxnext/runtime/arith/div32.asm +++ b/src/lib/arch/zxnext/runtime/arith/div32.asm @@ -1,144 +1,8 @@ -#include once - - ; --------------------------------------------------------- - push namespace core - -__DIVU32: ; 32 bit unsigned division - ; DEHL = Dividend, Stack Top = Divisor - ; OPERANDS P = Dividend, Q = Divisor => OPERATION => P / Q - ; - ; Changes A, BC DE HL B'C' D'E' H'L' - ; --------------------------------------------------------- - exx - pop hl ; return address - pop de ; low part - ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend - -__DIVU32START: ; Performs D'E'H'L' / HLDE - ; Now switch to DIVIDEND = B'C'BC / DIVISOR = D'E'DE (A / B) - push de ; push Lowpart(Q) - ex de, hl ; DE = HL - ld hl, 0 - exx - ld b, h - ld c, l - pop hl - push de - ex de, hl - ld hl, 0 ; H'L'HL = 0 - exx - pop bc ; Pop HightPart(B) => B = B'C'BC - exx - - ld a, 32 ; Loop count - -__DIV32LOOP: - sll c ; B'C'BC << 1 ; Output most left bit to carry - rl b - exx - rl c - rl b - exx - - adc hl, hl - exx - adc hl, hl - exx - - sbc hl,de - exx - sbc hl,de - exx - jp nc, __DIV32NOADD ; use JP inside a loop for being faster - - add hl, de - exx - adc hl, de - exx - dec bc - -__DIV32NOADD: - dec a - jp nz, __DIV32LOOP ; use JP inside a loop for being faster - ; At this point, quotient is stored in B'C'BC and the reminder in H'L'HL - - push hl - exx - pop de - ex de, hl ; D'E'H'L' = 32 bits modulus - push bc - exx - pop de ; DE = B'C' - ld h, b - ld l, c ; DEHL = quotient D'E'H'L' = Modulus - - ret ; DEHL = quotient, D'E'H'L' = Modulus - - - -__MODU32: ; 32 bit modulus for 32bit unsigned division - ; DEHL = Dividend, Stack Top = Divisor (DE, HL) - - exx - pop hl ; return address - pop de ; low part - ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend - - call __DIVU32START ; At return, modulus is at D'E'H'L' - -__MODU32START: - - exx - push de - push hl - - exx - pop hl - pop de - - ret - - -__DIVI32: ; 32 bit signed division - ; DEHL = Dividend, Stack Top = Divisor - ; A = Dividend, B = Divisor => A / B - exx - pop hl ; return address - pop de ; low part - ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend - -__DIVI32START: - exx - ld a, d ; Save sign - ex af, af' - bit 7, d ; Negative? - call nz, __NEG32 ; Negates DEHL - - exx ; Now works with H'L'D'E' - ex af, af' - xor h - ex af, af' ; Stores sign of the result for later - - bit 7, h ; Negative? - ex de, hl ; HLDE = DEHL - call nz, __NEG32 - ex de, hl - - call __DIVU32START - ex af, af' ; Recovers sign - and 128 ; positive? - ret z - - jp __NEG32 ; Negates DEHL and returns from there - - -__MODI32: ; 32bits signed division modulus - exx - pop hl ; return address - pop de ; low part - ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend - - call __DIVI32START - jp __MODU32START - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/arith/div8.asm b/src/lib/arch/zxnext/runtime/arith/div8.asm index 465a7c2d3..5488bfb63 100644 --- a/src/lib/arch/zxnext/runtime/arith/div8.asm +++ b/src/lib/arch/zxnext/runtime/arith/div8.asm @@ -1,90 +1,8 @@ - ; -------------------------------- - push namespace core - -__DIVU8: ; 8 bit unsigned integer division - ; Divides (Top of stack, High Byte) / A - pop hl ; -------------------------------- - ex (sp), hl ; CALLEE - -__DIVU8_FAST: ; Does A / H - ld l, h - ld h, a ; At this point do H / L - - ld b, 8 - xor a ; A = 0, Carry Flag = 0 - -__DIV8LOOP: - sla h - rla - cp l - jr c, __DIV8NOSUB - sub l - inc h - -__DIV8NOSUB: - djnz __DIV8LOOP - - ld l, a ; save remainder - ld a, h ; - - ret ; a = Quotient, - - - ; -------------------------------- -__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A - pop hl ; -------------------------------- - ex (sp), hl - -__DIVI8_FAST: - ld e, a ; store operands for later - ld c, h - - or a ; negative? - jp p, __DIV8A - neg ; Make it positive - -__DIV8A: - ex af, af' - ld a, h - or a - jp p, __DIV8B - neg - ld h, a ; make it positive - -__DIV8B: - ex af, af' - - call __DIVU8_FAST - - ld a, c - xor l ; bit 7 of A = 1 if result is negative - - ld a, h ; Quotient - ret p ; return if positive - - neg - ret - - -__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) - pop hl - ex (sp), hl ; CALLEE - -__MODU8_FAST: ; __FASTCALL__ entry - call __DIVU8_FAST - ld a, l ; Remainder - - ret ; a = Modulus - - -__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) - pop hl - ex (sp), hl ; CALLEE - -__MODI8_FAST: ; __FASTCALL__ entry - call __DIVI8_FAST - ld a, l ; remainder - - ret ; a = Modulus - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/arith/divf.asm b/src/lib/arch/zxnext/runtime/arith/divf.asm index 5269f3f8c..56194aa25 100644 --- a/src/lib/arch/zxnext/runtime/arith/divf.asm +++ b/src/lib/arch/zxnext/runtime/arith/divf.asm @@ -1,61 +1,8 @@ -#include once -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) - -; All of them uses C EDHL registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order BC DE HL (B not used). +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - push namespace core - -__DIVF: ; Division - PROC - LOCAL __DIVBYZERO - LOCAL TMP, ERR_SP - -TMP EQU 23629 ;(DEST) -ERR_SP EQU 23613 - - call __FPSTACK_PUSH2 - - ld hl, (ERR_SP) - ld (TMP), hl - ld hl, __DIVBYZERO - push hl - ld (ERR_SP), sp - - ; ------------- ROM DIV - rst 28h - defb 01h ; EXCHANGE - defb 05h ; DIV - defb 38h; ; END CALC - - pop hl - ld hl, (TMP) - ld (ERR_SP), hl - - jp __FPSTACK_POP - -__DIVBYZERO: - ld hl, (TMP) - ld (ERR_SP), hl - - ld a, ERROR_NumberTooBig - ld (ERR_NR), a - - ; Returns 0 on DIV BY ZERO error - xor a - ld b, a - ld c, a - ld d, a - ld e, a - ret - - ENDP +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/arith/divf16.asm b/src/lib/arch/zxnext/runtime/arith/divf16.asm index 2961c62fb..7b4144547 100644 --- a/src/lib/arch/zxnext/runtime/arith/divf16.asm +++ b/src/lib/arch/zxnext/runtime/arith/divf16.asm @@ -1,86 +1,8 @@ -#include once - - - push namespace core - -__DIVF16: ; 16.16 Fixed point Division (signed) - - ; DE.HL = Dividend, Stack Top = Divisor - ; A = Dividend, B = Divisor => A / B - exx - pop hl ; return address - pop de ; low part - ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend - ex de, hl ; D'E'.H'L' Dividend - -__DIVF16START: ; FAST Entry: DEHL => Dividend, D'E'H'L' => Divisor - ld a, d ; Save sign - ex af, af' - bit 7, d ; Negative? - call nz, __NEG32 ; Negates DEHL - - exx ; Now works with D'E'.H'L' - ex af, af' - xor d - ex af, af' ; Stores sign of the result for later - - bit 7, d ; Negative? - call nz, __NEG32 - exx ; Now we have DE.HL => Dividend - - ld b, 16 - -__SHIFTALOOP: ; Tries to shift Dividend to the left - bit 7, d - jp nz, __SHIFTB - add hl, hl - ex de, hl - adc hl, hl - ex de, hl - djnz __SHIFTALOOP - jp __DOF16_DIVRDY - -__SHIFTB: ; Cannot shift Dividend more to the left, try to shift Divisor to the right - ld a, b - exx - ld b, a - ; Divisor is in DEHL -__SHIFTBLOOP: - bit 1, l - jp nz, __DOF16_DIVIDE - sra d - rr e - rr h - rr l - djnz __SHIFTBLOOP - -__DOF16_DIVIDE: - ld a, b - exx - ld b, a - -__DOF16_DIVRDY: - exx - ex de, hl - push bc - call __DIVU32START - pop bc - - xor a - or b - jp z, __ENDF16DIV - -__SHIFTCLOOP: - add hl, hl ; Shift DECIMAL PART << 1 - ex de, hl - adc hl, hl ; Shift INTEGER PART << 1 Plus Carry - ex de, hl - djnz __SHIFTCLOOP - -__ENDF16DIV: ; Put the sign on the result - ex af, af' ; Recovers sign - and 128 ; positive? - ret z - jp __NEG32 ; Negates DEHL and returns from there - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/arith/modf.asm b/src/lib/arch/zxnext/runtime/arith/modf.asm index d37965eb7..0998f7245 100644 --- a/src/lib/arch/zxnext/runtime/arith/modf.asm +++ b/src/lib/arch/zxnext/runtime/arith/modf.asm @@ -1,27 +1,8 @@ -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) - -; All of them uses A EDCB registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order A DE BC. +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - push namespace core - -__MODF: ; MODULO - call __FPSTACK_PUSH2 ; Enters B, A - - ; ------------- ROM DIV - rst 28h - defb 01h ; EXCHANGE - defb 32h ; MOD - defb 02h ; Discard (POP) - defb 38h; ; END CALC - - jp __FPSTACK_POP +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/arith/modf16.asm b/src/lib/arch/zxnext/runtime/arith/modf16.asm index edba61785..d97738174 100644 --- a/src/lib/arch/zxnext/runtime/arith/modf16.asm +++ b/src/lib/arch/zxnext/runtime/arith/modf16.asm @@ -1,40 +1,8 @@ -; Computes A % B for fixed values - -#include once -#include once - - push namespace core - -__MODF16: - ; 16.16 Fixed point Division (signed) - ; DE.HL = Divisor, Stack Top = Divider - ; A = Dividend, B = Divisor => A % B - -PROC - LOCAL TEMP - -TEMP EQU 23698 ; MEMBOT - - pop bc ; ret addr - ld (TEMP), bc ; stores it on MEMBOT temporarily - ld (TEMP + 2), hl ; stores HP of divider - ld (TEMP + 4), de ; stores DE of divider - - call __DIVF16 - rlc d ; Sign into carry - sbc a, a ; a register = -1 sgn(DE), or 0 - ld d, a - ld e, a ; DE = 0 if it was positive or 0; -1 if it was negative - - ld bc, (TEMP + 4) ; Pushes original divider into the stack - push bc - ld bc, (TEMP + 2) - push bc - - ld bc, (TEMP) ; recovers return address - push bc - jp __MULF16 ; multiplies and return from there - -ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/arith/mul16.asm b/src/lib/arch/zxnext/runtime/arith/mul16.asm index 091b0057f..7c4379561 100644 --- a/src/lib/arch/zxnext/runtime/arith/mul16.asm +++ b/src/lib/arch/zxnext/runtime/arith/mul16.asm @@ -1,6 +1,6 @@ push namespace core -__MUL16: ; Mutiplies HL with the last value stored into de stack +__MUL16: ; Multiplies HL with the last value stored into de stack ; Works for both signed and unsigned PROC diff --git a/src/lib/arch/zxnext/runtime/arith/mul32.asm b/src/lib/arch/zxnext/runtime/arith/mul32.asm index 641bc1efb..f11551544 100644 --- a/src/lib/arch/zxnext/runtime/arith/mul32.asm +++ b/src/lib/arch/zxnext/runtime/arith/mul32.asm @@ -1,26 +1,8 @@ -#include once - - push namespace core - -__MUL32: - ; multiplies 32 bit un/signed integer. - ; First operand stored in DEHL, and 2nd onto stack - ; Lowest part of 2nd operand on top of the stack - ; returns the result in DE.HL - exx - pop hl ; Return ADDRESS - pop de ; Low part - ex (sp), hl ; CALLEE -> HL = High part - ex de, hl - call __MUL32_64START - -__TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) - exx - push bc - exx - pop de - ld h, a - ld l, c - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/arith/mulf.asm b/src/lib/arch/zxnext/runtime/arith/mulf.asm index ea43970bf..4c10d267f 100644 --- a/src/lib/arch/zxnext/runtime/arith/mulf.asm +++ b/src/lib/arch/zxnext/runtime/arith/mulf.asm @@ -1,24 +1,8 @@ -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) -; All of them uses A EDCB registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order A DE BC. +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - push namespace core - -__MULF: ; Multiplication - call __FPSTACK_PUSH2 - - ; ------------- ROM MUL - rst 28h - defb 04h ; - defb 38h; ; END CALC - - jp __FPSTACK_POP +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/arith/mulf16.asm b/src/lib/arch/zxnext/runtime/arith/mulf16.asm index 9c960669c..477afc3ee 100644 --- a/src/lib/arch/zxnext/runtime/arith/mulf16.asm +++ b/src/lib/arch/zxnext/runtime/arith/mulf16.asm @@ -1,46 +1,8 @@ -#include once -#include once - - push namespace core - -__MULF16: ; - ld a, d ; load sgn into a - ex af, af' ; saves it - call __ABS32 ; convert to positive - - exx - pop hl ; Return address - pop de ; Low part - ex (sp), hl ; CALLEE caller convention; Now HL = Hight part, (SP) = Return address - ex de, hl ; D'E' = High part (B), H'L' = Low part (B) (must be in DE) - - ex af, af' - xor d ; A register contains resulting sgn - ex af, af' - call __ABS32 ; convert to positive - - call __MUL32_64START - -; rounding (was not included in zx81) -__ROUND_FIX: ; rounds a 64bit (32.32) fixed point number to 16.16 - ; result returned in dehl - ; input in h'l'hlb'c'ac - sla a ; result bit 47 to carry - exx - ld hl,0 ; ld does not change carry - adc hl,bc ; hl = hl + 0 + carry - push hl - - exx - ld bc,0 - adc hl,bc ; hl = hl + 0 + carry - ex de, hl - pop hl ; rounded result in de.hl - - ex af, af' ; recovers result sign - or a - jp m, __NEG32 ; if negative, negates it - - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/arith/sub32.asm b/src/lib/arch/zxnext/runtime/arith/sub32.asm index 18e36076d..26e971142 100644 --- a/src/lib/arch/zxnext/runtime/arith/sub32.asm +++ b/src/lib/arch/zxnext/runtime/arith/sub32.asm @@ -1,31 +1,8 @@ -; SUB32 -; Perform TOP of the stack - DEHL -; Pops operand out of the stack (CALLEE) -; and returns result in DEHL. Carry an Z are set correctly - - push namespace core - -__SUB32: - exx - pop bc ; saves return address in BC' - exx - - or a ; clears carry flag - ld b, h ; Operands come reversed => BC <- HL, HL = HL - BC - ld c, l - pop hl - sbc hl, bc - ex de, hl - - ld b, h ; High part (DE) now in HL. Repeat operation - ld c, l - pop hl - sbc hl, bc - ex de, hl ; DEHL now has de 32 bit result - - exx - push bc ; puts return address back - exx - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/arith/subf.asm b/src/lib/arch/zxnext/runtime/arith/subf.asm index e59d87180..19f7d42e2 100644 --- a/src/lib/arch/zxnext/runtime/arith/subf.asm +++ b/src/lib/arch/zxnext/runtime/arith/subf.asm @@ -1,27 +1,8 @@ -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) - -; All of them uses A EDCB registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order A DE BC. +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - - push namespace core - -__SUBF: ; Subtraction - call __FPSTACK_PUSH2 ; ENTERS B, A - - ; ------------- ROM SUB - rst 28h - defb 01h ; EXCHANGE - defb 03h ; SUB - defb 38h; ; END CALC - - jp __FPSTACK_POP +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/array/arrayalloc.asm b/src/lib/arch/zxnext/runtime/array/arrayalloc.asm index 705efe67b..609dc3184 100644 --- a/src/lib/arch/zxnext/runtime/array/arrayalloc.asm +++ b/src/lib/arch/zxnext/runtime/array/arrayalloc.asm @@ -1,143 +1,8 @@ - -#include once - - -; --------------------------------------------------------------------- -; __ALLOC_LOCAL_ARRAY -; Allocates an array element area in the heap, and clears it filling it -; with 0 bytes -; -; Parameters -; HL = Offset to be added to IX => HL = IX + HL -; BC = Length of the element area = n.elements * size(element) -; DE = PTR to the index table -; -; Returns: -; HL = (IX + HL) + 4 -; --------------------------------------------------------------------- - - push namespace core - -__ALLOC_LOCAL_ARRAY: - push de - push ix - pop de - add hl, de ; hl = ix + hl - pop de - ld (hl), e - inc hl - ld (hl), d - inc hl - push hl - call __MEM_CALLOC - pop de - ex de, hl - ld (hl), e - inc hl - ld (hl), d - ret - - -; --------------------------------------------------------------------- -; __ALLOC_INITIALIZED_LOCAL_ARRAY -; Allocates an array element area in the heap, and clears it filling it -; with data whose pointer (PTR) is in the stack -; -; Parameters -; HL = Offset to be added to IX => HL = IX + HL -; BC = Length of the element area = n.elements * size(element) -; DE = PTR to the index table -; [SP + 2] = PTR to the element area -; -; Returns: -; HL = (IX + HL) + 4 -; --------------------------------------------------------------------- - -__ALLOC_INITIALIZED_LOCAL_ARRAY: - push bc - call __ALLOC_LOCAL_ARRAY - pop bc - ;; Swaps [SP], [SP + 2] - exx - pop hl ; HL <- RET address - ex (sp), hl ; HL <- Data table, [SP] <- RET address - push hl ; [SP] <- Data table - exx - ex (sp), hl ; HL = Data table, (SP) = (IX + HL + 4) - start of array address lbound - ; HL = data table - ; BC = length - ; DE = new data area - ldir - pop hl ; HL = addr of LBound area if used - ret - - -#ifdef __ZXB_USE_LOCAL_ARRAY_WITH_BOUNDS__ - -; --------------------------------------------------------------------- -; __ALLOC_LOCAL_ARRAY_WITH_BOUNDS -; Allocates an array element area in the heap, and clears it filling it -; with 0 bytes. Then sets LBOUND and UBOUND ptrs -; -; Parameters -; HL = Offset to be added to IX => HL = IX + HL -; BC = Length of the element area = n.elements * size(element) -; DE = PTR to the index table -; [SP + 2] PTR to the lbound element area -; [SP + 4] PTR to the ubound element area +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Returns: -; HL = (IX + HL) + 8 -; --------------------------------------------------------------------- -__ALLOC_LOCAL_ARRAY_WITH_BOUNDS: - call __ALLOC_LOCAL_ARRAY - -__ALLOC_LOCAL_ARRAY_WITH_BOUNDS2: - pop bc ;; ret address - pop de ;; lbound - inc hl - ld (hl), e - inc hl - ld (hl), d - pop de ;; PTR to ubound table - push bc ;; puts ret address back - ld a, d - or e - ret z ;; if PTR for UBound is 0, it's not used - inc hl - ld (hl), e - inc hl - ld (hl), d - ret - - -; --------------------------------------------------------------------- -; __ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS -; Allocates an array element area in the heap, and clears it filling it -; with 0 bytes -; -; Parameters -; HL = Offset to be added to IX => HL = IX + HL -; BC = Length of the element area = n.elements * size(element) -; DE = PTR to the index table -; TOP of the stack = PTR to the element area -; [SP + 2] = PTR to the element area -; [SP + 4] = PTR to the lbound element area -; [SP + 6] = PTR to the ubound element area -; -; Returns: -; HL = (IX + HL) + 8 -; --------------------------------------------------------------------- -__ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS: - ;; Swaps [SP] and [SP + 2] - exx - pop hl ;; Ret address - ex (sp), hl ;; HL <- PTR to Element area, (sp) = Ret address - push hl ;; [SP] = PTR to element area, [SP + 2] = Ret address - exx - call __ALLOC_INITIALIZED_LOCAL_ARRAY - jp __ALLOC_LOCAL_ARRAY_WITH_BOUNDS2 - -#endif +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/array/arraybound.asm b/src/lib/arch/zxnext/runtime/array/arraybound.asm index 6cb71eafa..8a41bba74 100644 --- a/src/lib/arch/zxnext/runtime/array/arraybound.asm +++ b/src/lib/arch/zxnext/runtime/array/arraybound.asm @@ -1,91 +1,8 @@ -; --------------------------------------------------------- -; Copyleft (k)2011 by Jose Rodriguez (a.k.a. Boriel) -; http://www.boriel.com +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; ZX BASIC Compiler http://www.zxbasic.net -; This code is released under the BSD License -; --------------------------------------------------------- +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -; Implements both LBOUND(array, N) and UBOUND(array, N) function -; Parameters: -; HL = PTR to array -; [stack - 2] -> N (dimension) - - push namespace core - - PROC - LOCAL __BOUND - LOCAL __DIM_NOT_EXIST - LOCAL __CONT - -__LBOUND: - ld a, 4 - jr __BOUND - -__UBOUND: - ld a, 6 - -__BOUND: - ex de, hl ; DE <-- Array ptr - pop hl ; HL <-- Ret address - ex (sp), hl ; CALLEE: HL <-- N, (SP) <-- Ret address - ex de, hl ; DE <-- N, HL <-- ARRAY_PTR - - push hl - ld c, (hl) - inc hl - ld h, (hl) - ld l, c ; HL = start of dimension table (first position contains number of dimensions - 1) - ld c, (hl) - inc hl - ld b, (hl) - inc bc ; Number of total dimensions of the array - pop hl ; Recovers ARRAY PTR - ex af, af' ; Saves A for later - ld a, d - or e - jr nz, __CONT ; N = 0 => Return number of dimensions - - ;; Return the number of dimensions of the array - ld h, b - ld l, c - ret - -__CONT: - dec de - ex af, af' ; Recovers A (contains PTR offset) - ex de, hl ; HL = N (dimension asked) - 1, DE = Array PTR - or a - sbc hl, bc ; if no Carry => the user asked for a dimension that does not exist. Return 0 - jr nc, __DIM_NOT_EXIST - - add hl, bc ; restores HL = (N - 1) - add hl, hl ; hl *= 2 - ex de, hl ; hl = ARRAY_PTR + 3, DE jsz = (N - 1) * 2 - ld b, 0 - ld c, a - add hl, bc ; HL = &BOUND_PTR - ld a, (hl) - inc hl - ld h, (hl) - ld l, a ; LD HL, (HL) => Origin of L/U Bound table - - ; for LBound only, HL = 0x0000 (NULL) if the array is all 0-based - or h - ret z ; Should never happen for UBound - - add hl, de ; hl += OFFSET __LBOUND._xxxx - ld e, (hl) ; de = (hl) - inc hl - ld d, (hl) - - ex de, hl ; hl = de => returns result in HL - ret - -__DIM_NOT_EXIST: -; The dimension requested by the user does not exists. Return 0 - ld hl, 0 - ret - ENDP - - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/array/arraystrfree.asm b/src/lib/arch/zxnext/runtime/array/arraystrfree.asm index c599ef6b7..8bcb32542 100644 --- a/src/lib/arch/zxnext/runtime/array/arraystrfree.asm +++ b/src/lib/arch/zxnext/runtime/array/arraystrfree.asm @@ -1,63 +1,8 @@ -; This routine is in charge of freeing an array of strings from memory -; HL = Pointer to start of array in memory -; Top of the stack = Number of elements of the array - -#include once - - push namespace core - -__ARRAYSTR_FREE: - PROC - - LOCAL __ARRAY_LOOP - - ex de, hl - pop hl ; (ret address) - ex (sp), hl ; Callee -> HL = Number of elements - - ex de, hl - -__ARRAYSTR_FREE_FAST: ; Fastcall entry: DE = Number of elements - ld a, h - or l - ret z ; ret if NULL - - ld b, d - ld c, e - -__ARRAY_LOOP: - ld e, (hl) - inc hl - ld d, (hl) - inc hl ; DE = (HL) = String Pointer - - push hl - push bc - ex de, hl - call __MEM_FREE ; Frees it from memory - pop bc - pop hl - - dec bc - ld a, b - or c - jp nz, __ARRAY_LOOP - - ret ; Frees it and return - - ENDP - - -__ARRAYSTR_FREE_MEM: ; like the above, buf also frees the array itself - ex de, hl - pop hl ; (ret address) - ex (sp), hl ; Callee -> HL = Number of elements - ex de, hl - - push hl ; Saves array pointer for later - call __ARRAYSTR_FREE_FAST - pop hl ; recovers array block pointer - - jp __MEM_FREE ; Frees it and returns from __MEM_FREE - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/array/strarraycpy.asm b/src/lib/arch/zxnext/runtime/array/strarraycpy.asm index aafc51426..69ba6b8f0 100644 --- a/src/lib/arch/zxnext/runtime/array/strarraycpy.asm +++ b/src/lib/arch/zxnext/runtime/array/strarraycpy.asm @@ -1,68 +1,8 @@ -; (K)opyleft - by Jose M. Rodriguez de la Rosa (a.k.a. Boriel) -; 2009 - This is Free OpenSource BSD code -; vim: et:ts=4:sw=4 - -; Copies a vector of strings from one place to another -; reallocating strings of the destiny vector to hold source strings. -; This is used in the following code: -; DIM a$(20) : DIM b$(20): a$ = b$ - -#include once -#include once - - push namespace core - -STR_ARRAYCOPY: -; Copies an array of string a$ = b$ -; Parameters in the stack: -; a$, b$, num. of elements; - pop hl ; ret address - pop bc ; num of elements - pop de ; source array + offset to the 1st elem. - ex (sp), hl ; Callee -> hl = destiny array + offset to the 1st elem. - -; FASTCALL ENTRY - -; HL = a$ + offset -; DE = b$ + offset -; BC = Number of elements - -__STR_ARRAYCOPY: - PROC - LOCAL LOOP - -LOOP: - ld a, b - or c - ret z ; Done! - - dec bc - push bc - push de - - ld a,(hl) - inc hl - ld c,(hl) - dec hl - push hl - - ld h, c - ld l, a - call __LOAD_DE_DE - call __STRASSIGN - - ex de, hl - pop hl - ld (hl), e - inc hl - ld (hl), d - inc hl - pop de - pop bc - inc de - inc de - jp LOOP - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/asc.asm b/src/lib/arch/zxnext/runtime/asc.asm index 91f98fc2c..71251f381 100644 --- a/src/lib/arch/zxnext/runtime/asc.asm +++ b/src/lib/arch/zxnext/runtime/asc.asm @@ -1,39 +1,8 @@ -; Returns the ascii code for the given str -#include once - - push namespace core - -__ASC: - PROC - LOCAL __ASC_END - - ex af, af' ; Saves free_mem flag - - ld a, h - or l - ret z ; NULL? return - - ld c, (hl) - inc hl - ld b, (hl) - - ld a, b - or c - jr z, __ASC_END ; No length? return - - inc hl - ld a, (hl) - dec hl - -__ASC_END: - dec hl - ex af, af' - or a - call nz, __MEM_FREE ; Free memory if needed - - ex af, af' ; Recover result - - ret - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/attr.asm b/src/lib/arch/zxnext/runtime/attr.asm index 7c228409b..7e2603c9f 100644 --- a/src/lib/arch/zxnext/runtime/attr.asm +++ b/src/lib/arch/zxnext/runtime/attr.asm @@ -1,65 +1,8 @@ -; Attribute routines -; vim:ts=4:et:sw: - -#include once -#include once -#include once -#include once - - push namespace core - -__ATTR_ADDR: - ; calc start address in DE (as (32 * d) + e) - ; Contributed by Santiago Romero at http://www.speccy.org - ld h, 0 ; 7 T-States - ld a, d ; 4 T-States - ld d, h - add a, a ; a * 2 ; 4 T-States - add a, a ; a * 4 ; 4 T-States - ld l, a ; HL = A * 4 ; 4 T-States - - add hl, hl ; HL = A * 8 ; 15 T-States - add hl, hl ; HL = A * 16 ; 15 T-States - add hl, hl ; HL = A * 32 ; 15 T-States - add hl, de - - ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address - add hl, de - ; Return current screen address in HL - ret - - -; Sets the attribute at a given screen coordinate (D, E). -; The attribute is taken from the ATTR_T memory variable -; Used by PRINT routines -SET_ATTR: - - ; Checks for valid coords - call __IN_SCREEN - ret nc - - call __ATTR_ADDR - -__SET_ATTR: - ; Internal __FASTCALL__ Entry used by printing routines - ; HL contains the address of the ATTR cell to set - PROC - -__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address - ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T - - ld a, d - and (hl) - ld c, a ; C = current screen color, masked - - ld a, d - cpl ; Negate mask - and e ; Mask current attributes - or c ; Mix them - ld (hl), a ; Store result in screen - - ret - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bitwise/band16.asm b/src/lib/arch/zxnext/runtime/bitwise/band16.asm index a7ff382ca..ed946504c 100644 --- a/src/lib/arch/zxnext/runtime/bitwise/band16.asm +++ b/src/lib/arch/zxnext/runtime/bitwise/band16.asm @@ -1,22 +1,8 @@ -; vim:ts=4:et: -; FASTCALL bitwise and16 version. -; result in hl -; __FASTCALL__ version (operands: A, H) -; Performs 16bit or 16bit and returns the boolean -; Input: HL, DE -; Output: HL <- HL AND DE - - push namespace core - -__BAND16: - ld a, h - and d - ld h, a - - ld a, l - and e - ld l, a - - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bitwise/band32.asm b/src/lib/arch/zxnext/runtime/bitwise/band32.asm index 311c37b67..9cb96c546 100644 --- a/src/lib/arch/zxnext/runtime/bitwise/band32.asm +++ b/src/lib/arch/zxnext/runtime/bitwise/band32.asm @@ -1,39 +1,8 @@ -; FASTCALL bitwise and 32 version. -; Performs 32bit and 32bit and returns the bitwise -; result in DE,HL -; First operand in DE,HL 2nd operand into the stack - - push namespace core - -__BAND32: - ld b, h - ld c, l ; BC <- HL - - pop hl ; Return address - ex (sp), hl ; HL <- Lower part of 2nd Operand - - ld a, b - and h - ld b, a - - ld a, c - and l - ld c, a ; BC <- BC & HL - - pop hl ; Return dddress - ex (sp), hl ; HL <- High part of 2nd Operand - - ld a, d - and h - ld d, a - - ld a, e - and l - ld e, a ; DE <- DE & HL - - ld h, b - ld l, c ; HL <- BC ; Always return DE,HL pair regs - - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bitwise/bnot16.asm b/src/lib/arch/zxnext/runtime/bitwise/bnot16.asm index b1b8ffe63..37081941e 100644 --- a/src/lib/arch/zxnext/runtime/bitwise/bnot16.asm +++ b/src/lib/arch/zxnext/runtime/bitwise/bnot16.asm @@ -1,22 +1,8 @@ -; vim:ts=4:et: -; FASTCALL bitwise or 16 version. -; result in HL -; __FASTCALL__ version (operands: A, H) -; Performs 16bit NEGATION -; Input: HL -; Output: HL <- NOT HL - - push namespace core - -__BNOT16: - ld a, h - cpl - ld h, a - - ld a, l - cpl - ld l, a - - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bitwise/bnot32.asm b/src/lib/arch/zxnext/runtime/bitwise/bnot32.asm index 243870646..89cd6d343 100644 --- a/src/lib/arch/zxnext/runtime/bitwise/bnot32.asm +++ b/src/lib/arch/zxnext/runtime/bitwise/bnot32.asm @@ -1,30 +1,8 @@ -; vim:ts=4:et: -; FASTCALL bitwise or 32 version. -; result in DE,HL -; __FASTCALL__ version (operands: A, H) -; Performs 32bit NEGATION (cpl) -; Input: DE,HL -; Output: DE,HL <- NOT DE,HL - - push namespace core - -__BNOT32: - ld a, l - cpl - ld l, a - - ld a, h - cpl - ld h, a - - ld a, e - cpl - ld e, a - - ld a, d - cpl - ld d, a - - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bitwise/bor16.asm b/src/lib/arch/zxnext/runtime/bitwise/bor16.asm index 03fd45db8..574bdc887 100644 --- a/src/lib/arch/zxnext/runtime/bitwise/bor16.asm +++ b/src/lib/arch/zxnext/runtime/bitwise/bor16.asm @@ -1,22 +1,8 @@ -; vim:ts=4:et: -; FASTCALL bitwise or 16 version. -; result in HL -; __FASTCALL__ version (operands: A, H) -; Performs 16bit or 16bit and returns the boolean -; Input: HL, DE -; Output: HL <- HL OR DE - - push namespace core - -__BOR16: - ld a, h - or d - ld h, a - - ld a, l - or e - ld l, a - - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bitwise/bor32.asm b/src/lib/arch/zxnext/runtime/bitwise/bor32.asm index 470578b2f..46ee69a99 100644 --- a/src/lib/arch/zxnext/runtime/bitwise/bor32.asm +++ b/src/lib/arch/zxnext/runtime/bitwise/bor32.asm @@ -1,39 +1,8 @@ -; FASTCALL bitwise or 32 version. -; Performs 32bit or 32bit and returns the bitwise -; result DE,HL -; First operand in DE,HL 2nd operand into the stack - - push namespace core - -__BOR32: - ld b, h - ld c, l ; BC <- HL - - pop hl ; Return address - ex (sp), hl ; HL <- Lower part of 2nd Operand - - ld a, b - or h - ld b, a - - ld a, c - or l - ld c, a ; BC <- BC & HL - - pop hl ; Return dddress - ex (sp), hl ; HL <- High part of 2nd Operand - - ld a, d - or h - ld d, a - - ld a, e - or l - ld e, a ; DE <- DE & HL - - ld h, b - ld l, c ; HL <- BC ; Always return DE,HL pair regs - - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bitwise/bxor16.asm b/src/lib/arch/zxnext/runtime/bitwise/bxor16.asm index 297d853e4..c4ece6616 100644 --- a/src/lib/arch/zxnext/runtime/bitwise/bxor16.asm +++ b/src/lib/arch/zxnext/runtime/bitwise/bxor16.asm @@ -1,22 +1,8 @@ -; vim:ts=4:et: -; FASTCALL bitwise xor 16 version. -; result in Accumulator (0 False, not 0 True) -; __FASTCALL__ version (operands: A, H) -; Performs 16bit xor 16bit and returns the boolean -; Input: HL, DE -; Output: HL <- HL XOR DE - - push namespace core - -__BXOR16: - ld a, h - xor d - ld h, a - - ld a, l - xor e - ld l, a - - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bitwise/bxor32.asm b/src/lib/arch/zxnext/runtime/bitwise/bxor32.asm index d0100f704..52d2b2fa8 100644 --- a/src/lib/arch/zxnext/runtime/bitwise/bxor32.asm +++ b/src/lib/arch/zxnext/runtime/bitwise/bxor32.asm @@ -1,39 +1,8 @@ -; FASTCALL bitwise xor 32 version. -; Performs 32bit xor 32bit and returns the bitwise -; result DE,HL -; First operand in DE,HL 2nd operand into the stack - - push namespace core - -__BXOR32: - ld b, h - ld c, l ; BC <- HL - - pop hl ; Return address - ex (sp), hl ; HL <- Lower part of 2nd Operand - - ld a, b - xor h - ld b, a - - ld a, c - xor l - ld c, a ; BC <- BC & HL - - pop hl ; Return address - ex (sp), hl ; HL <- High part of 2nd Operand - - ld a, d - xor h - ld d, a - - ld a, e - xor l - ld e, a ; DE <- DE & HL - - ld h, b - ld l, c ; HL <- BC ; Always return DE,HL pair regs - - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bitwise/shl32.asm b/src/lib/arch/zxnext/runtime/bitwise/shl32.asm index 253884079..1769af86b 100644 --- a/src/lib/arch/zxnext/runtime/bitwise/shl32.asm +++ b/src/lib/arch/zxnext/runtime/bitwise/shl32.asm @@ -1,12 +1,8 @@ - - push namespace core - -__SHL32: ; Left Logical Shift 32 bits - - sla l - rl h - rl e - rl d - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bitwise/shra32.asm b/src/lib/arch/zxnext/runtime/bitwise/shra32.asm index 7f7257a1f..5ede8796a 100644 --- a/src/lib/arch/zxnext/runtime/bitwise/shra32.asm +++ b/src/lib/arch/zxnext/runtime/bitwise/shra32.asm @@ -1,12 +1,8 @@ - - push namespace core - -__SHRA32: ; Right Arithmetical Shift 32 bits - - sra d - rr e - rr h - rr l - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bitwise/shrl32.asm b/src/lib/arch/zxnext/runtime/bitwise/shrl32.asm index 1de12b689..54fabad65 100644 --- a/src/lib/arch/zxnext/runtime/bitwise/shrl32.asm +++ b/src/lib/arch/zxnext/runtime/bitwise/shrl32.asm @@ -1,12 +1,8 @@ - - push namespace core - -__SHRL32: ; Right Logical Shift 32 bits - - srl d - rr e - rr h - rr l - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bold.asm b/src/lib/arch/zxnext/runtime/bold.asm index 4bd46a82e..010169c6c 100644 --- a/src/lib/arch/zxnext/runtime/bold.asm +++ b/src/lib/arch/zxnext/runtime/bold.asm @@ -1,34 +1,8 @@ -; Sets BOLD flag in P_FLAG permanently -; Parameter: BOLD flag in bit 0 of A register -#include once - - push namespace core - -BOLD: - PROC - - and 1 - rlca - rlca - rlca - ld hl, FLAGS2 - res 3, (HL) - or (hl) - ld (hl), a - ret - -; Sets BOLD flag in P_FLAG temporarily -BOLD_TMP: - and 1 - rlca - rlca - ld hl, FLAGS2 - res 2, (hl) - or (hl) - ld (hl), a - ret - - ENDP - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bool/and16.asm b/src/lib/arch/zxnext/runtime/bool/and16.asm index 829a0bb9b..9a5b3339f 100644 --- a/src/lib/arch/zxnext/runtime/bool/and16.asm +++ b/src/lib/arch/zxnext/runtime/bool/and16.asm @@ -1,17 +1,8 @@ -; FASTCALL boolean and 16 version. -; result in Accumulator (0 False, not 0 True) -; __FASTCALL__ version (operands: DE, HL) -; Performs 16bit and 16bit and returns the boolean - - push namespace core - -__AND16: - ld a, h - or l - ret z - - ld a, d - or e - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bool/and32.asm b/src/lib/arch/zxnext/runtime/bool/and32.asm index 9723308a1..5ff61e206 100644 --- a/src/lib/arch/zxnext/runtime/bool/and32.asm +++ b/src/lib/arch/zxnext/runtime/bool/and32.asm @@ -1,31 +1,8 @@ -; FASTCALL boolean and 32 version. -; Performs 32bit and 32bit and returns the boolean -; result in Accumulator (0 False, not 0 True) -; First operand in DE,HL 2nd operand into the stack - - push namespace core - -__AND32: - ld a, l - or h - or e - or d - - pop hl - pop de - ex (sp), hl - ret z - - ld a, d - or e - or h - or l - -#ifdef NORMALIZE_BOOLEAN - ret z - ld a, 1 -#endif - - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bool/andf.asm b/src/lib/arch/zxnext/runtime/bool/andf.asm index fabbc00e6..5bb0c6a0c 100644 --- a/src/lib/arch/zxnext/runtime/bool/andf.asm +++ b/src/lib/arch/zxnext/runtime/bool/andf.asm @@ -1,28 +1,8 @@ -#include once -#include once -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) - -; All of them uses C EDHL registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order BC DE HL (B not used). +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - push namespace core - -__ANDF: ; A & B - call __FPSTACK_PUSH2 - - ; ------------- ROM NO-&-NO - rst 28h - defb 08h ; - defb 38h; ; END CALC - - call __FPSTACK_POP - jp __FTOU8 ; Convert to 8 bits +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bool/not32.asm b/src/lib/arch/zxnext/runtime/bool/not32.asm index e354698ed..8493e7560 100644 --- a/src/lib/arch/zxnext/runtime/bool/not32.asm +++ b/src/lib/arch/zxnext/runtime/bool/not32.asm @@ -1,16 +1,8 @@ -; ------------------------------------------------------------- -; 32 bit logical NOT -; ------------------------------------------------------------- +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - push namespace core - -__NOT32: ; A = ¬A - ld a, d - or e - or h - or l - sub 1 ; Gives CARRY only if 0 - sbc a, a; Gives 0 if not carry, FF otherwise - ret - - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bool/notf.asm b/src/lib/arch/zxnext/runtime/bool/notf.asm index bced6ef9f..0838ee0ab 100644 --- a/src/lib/arch/zxnext/runtime/bool/notf.asm +++ b/src/lib/arch/zxnext/runtime/bool/notf.asm @@ -1,28 +1,8 @@ -#include once -#include once -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) - -; All of them uses C EDHL registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order BC DE HL (B not used). +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - push namespace core - -__NOTF: ; A = ¬A - call __FPSTACK_PUSH - - ; ------------- ROM NOT - rst 28h - defb 30h ; - defb 38h; ; END CALC - - call __FPSTACK_POP - jp __FTOU8 ; Convert to 8 bits +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bool/or32.asm b/src/lib/arch/zxnext/runtime/bool/or32.asm index e1cb1689b..b83f5ba9e 100644 --- a/src/lib/arch/zxnext/runtime/bool/or32.asm +++ b/src/lib/arch/zxnext/runtime/bool/or32.asm @@ -1,28 +1,8 @@ - push namespace core - -__OR32: ; Performs logical operation A AND B - ; between DEHL and TOP of the stack. - ; Returns A = 0 (False) or A = FF (True) - - ld a, h - or l - or d - or e - - pop hl ; Return address - pop de - ex (sp), hl - - or d - or e - or h - or l - -#ifdef NORMALIZE_BOOLEAN - ; Ensure it returns 0 or 1 - ret z - ld a, 1 -#endif - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bool/orf.asm b/src/lib/arch/zxnext/runtime/bool/orf.asm index 7a89a2bf6..8aef1bc4a 100644 --- a/src/lib/arch/zxnext/runtime/bool/orf.asm +++ b/src/lib/arch/zxnext/runtime/bool/orf.asm @@ -1,28 +1,8 @@ -#include once -#include once -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) - -; All of them uses A EDCb registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order A DE BC. +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - push namespace core - -__ORF: ; A | B - call __FPSTACK_PUSH2 - - ; ------------- ROM NO-OR-NO - rst 28h - defb 07h ; - defb 38h; ; END CALC - - call __FPSTACK_POP - jp __FTOU8 ; Convert to 32 bits +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bool/xor16.asm b/src/lib/arch/zxnext/runtime/bool/xor16.asm index 85b1b3288..21978e818 100644 --- a/src/lib/arch/zxnext/runtime/bool/xor16.asm +++ b/src/lib/arch/zxnext/runtime/bool/xor16.asm @@ -1,3 +1,8 @@ +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -; XOR16 implemented in XOR8.ASM file -#include once +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bool/xor32.asm b/src/lib/arch/zxnext/runtime/bool/xor32.asm index 4be3918d5..c8dbed260 100644 --- a/src/lib/arch/zxnext/runtime/bool/xor32.asm +++ b/src/lib/arch/zxnext/runtime/bool/xor32.asm @@ -1,27 +1,8 @@ -; FASTCALL boolean xor 8 version. -; result in Accumulator (0 False, not 0 True) -; __FASTCALL__ version (operands: A, H) -; Performs 32bit xor 32bit and returns the boolean - -#include once - - push namespace core - -__XOR32: - ld a, h - or l - or d - or e - ld c, a - - pop hl ; RET address - pop de - ex (sp), hl - ld a, h - or l - or d - or e - ld h, c - jp __XOR8 - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bool/xor8.asm b/src/lib/arch/zxnext/runtime/bool/xor8.asm index a40d24729..c9285f95c 100644 --- a/src/lib/arch/zxnext/runtime/bool/xor8.asm +++ b/src/lib/arch/zxnext/runtime/bool/xor8.asm @@ -1,28 +1,8 @@ -; vim:ts=4:et: -; FASTCALL boolean xor 8 version. -; result in Accumulator (0 False, not 0 True) -; __FASTCALL__ version (operands: A, H) -; Performs 8bit xor 8bit and returns the boolean - - push namespace core - -__XOR16: - ld a, h - or l - ld h, a - - ld a, d - or e - -__XOR8: - sub 1 - sbc a, a - ld l, a ; l = 00h or FFh - - ld a, h - sub 1 - sbc a, a ; a = 00h or FFh - xor l - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bool/xorf.asm b/src/lib/arch/zxnext/runtime/bool/xorf.asm index 0c76f6f83..95d051682 100644 --- a/src/lib/arch/zxnext/runtime/bool/xorf.asm +++ b/src/lib/arch/zxnext/runtime/bool/xorf.asm @@ -1,38 +1,8 @@ -#include once -#include once -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) - -; All of them uses C EDHL registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order BC DE HL (B not used). +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - push namespace core - -__XORF: ; A XOR B - call __FPSTACK_PUSH2 - - ; A XOR B == A ^ ¬B v ¬A ^ B - rst 28h - defb 0C0h ; STORE 0 - defb 02h ; DELETE - defb 31h ; DUP - defb 30h ; NOT A - defb 0E0h ; Recall 0 - defb 08h ; AND - defb 01h ; SWAP - defb 0E0h ; Recall 0 - defb 30h ; NOT B - defb 08h ; AND - defb 07h ; OR - defb 38h ; END CALC - - call __FPSTACK_POP - jp __FTOU8 ; Convert to 8 bits +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/border.asm b/src/lib/arch/zxnext/runtime/border.asm index 19c923194..3e46fd9f5 100644 --- a/src/lib/arch/zxnext/runtime/border.asm +++ b/src/lib/arch/zxnext/runtime/border.asm @@ -1,11 +1,8 @@ -; __FASTCALL__ Routine to change de border -; Parameter (color) specified in A register - - push namespace core - -BORDER EQU 229Bh - - pop namespace - - -; Nothing to do! (Directly from the ZX Spectrum ROM) +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/break.asm b/src/lib/arch/zxnext/runtime/break.asm index 9579232d5..f2f9ca23e 100644 --- a/src/lib/arch/zxnext/runtime/break.asm +++ b/src/lib/arch/zxnext/runtime/break.asm @@ -1,35 +1,8 @@ -#include once - - -; Check if BREAK is pressed -; Return if not. Else Raises -; L BREAK Into Program Error -; HL contains the line number we want to appear in the error msg. - - push namespace core - -CHECK_BREAK: - PROC - LOCAL PPC, TS_BRK, NO_BREAK - - push af - call TS_BRK - jr c, NO_BREAK - - ld (PPC), hl ; line num - ld a, ERROR_BreakIntoProgram - jp __ERROR ; this stops the program and exits to BASIC - -NO_BREAK: - pop af - pop hl ; ret address - ex (sp), hl ; puts it back into the stack and recovers initial HL - ret - -PPC EQU 23621 -TS_BRK EQU 8020 - - ENDP - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/bright.asm b/src/lib/arch/zxnext/runtime/bright.asm index 13feb8fd4..75f8531c7 100644 --- a/src/lib/arch/zxnext/runtime/bright.asm +++ b/src/lib/arch/zxnext/runtime/bright.asm @@ -1,46 +1,8 @@ -; Sets bright flag in ATTR_P permanently -; Parameter: Paper color in A register - -#include once - - push namespace core - -BRIGHT: - ld hl, ATTR_P - - PROC - LOCAL IS_TR - LOCAL IS_ZERO - -__SET_BRIGHT: - ; Another entry. This will set the bright flag at location pointer by DE - cp 8 - jr z, IS_TR - - ; # Convert to 0/1 - or a - jr z, IS_ZERO - ld a, 0x40 - -IS_ZERO: - ld b, a ; Saves the color - ld a, (hl) - and 0BFh ; Clears previous value - or b - ld (hl), a - inc hl - res 6, (hl) ;Reset bit 6 to disable transparency - ret - -IS_TR: ; transparent - inc hl ; Points DE to MASK_T or MASK_P - set 6, (hl) ;Set bit 6 to enable transparency - ret - -; Sets the BRIGHT flag passed in A register in the ATTR_T variable -BRIGHT_TMP: - ld hl, ATTR_T - jr __SET_BRIGHT - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/chr.asm b/src/lib/arch/zxnext/runtime/chr.asm index 494285dc8..b2c6ecf77 100644 --- a/src/lib/arch/zxnext/runtime/chr.asm +++ b/src/lib/arch/zxnext/runtime/chr.asm @@ -1,78 +1,8 @@ -; CHR$(x, y, x) returns the string CHR$(x) + CHR$(y) + CHR$(z) +; ---------------------------------------------------------------- +; This file is released under the MIT License ; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -#include once - - push namespace core - -CHR: ; Returns HL = Pointer to STRING (NULL if no memory) - ; Requires alloc.asm for dynamic memory heap. - ; Parameters: HL = Number of bytes to insert (already push onto the stack) - ; STACK => parameters (16 bit, only the High byte is considered) - ; Used registers A, A', BC, DE, HL, H'L' - - PROC - - LOCAL __POPOUT - LOCAL TMP - -TMP EQU 23629 ; (DEST System variable) - - ld a, h - or l - ret z ; If Number of parameters is ZERO, return NULL STRING - - ld b, h - ld c, l - - pop hl ; Return address - ld (TMP), hl - - push bc - inc bc - inc bc ; BC = BC + 2 => (2 bytes for the length number) - call __MEM_ALLOC - pop bc - - ld d, h - ld e, l ; Saves HL in DE - - ld a, h - or l - jr z, __POPOUT ; No Memory, return - - ld (hl), c - inc hl - ld (hl), b - inc hl - -__POPOUT: ; Removes out of the stack every byte and return - ; If Zero Flag is set, don't store bytes in memory - ex af, af' ; Save Zero Flag - - ld a, b - or c - jr z, __CHR_END - - dec bc - pop af ; Next byte - - ex af, af' ; Recovers Zero flag - jr z, __POPOUT - - ex af, af' ; Saves Zero flag - ld (hl), a - inc hl - ex af, af' ; Recovers Zero Flag - - jp __POPOUT - -__CHR_END: - ld hl, (TMP) - push hl ; Restores return addr - ex de, hl ; Recovers original HL ptr - ret - - ENDP - - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/circle.asm b/src/lib/arch/zxnext/runtime/circle.asm index 874b58ebe..3ab80a796 100644 --- a/src/lib/arch/zxnext/runtime/circle.asm +++ b/src/lib/arch/zxnext/runtime/circle.asm @@ -1,227 +1,8 @@ -; Bresenham's like circle algorithm -; best known as Middle Point Circle drawing algorithm - -#include once -#include once -#include once - -; Draws a circle at X, Y of radius R -; X, Y on the Stack, R in accumulator (Byte) - - push namespace core - - PROC - LOCAL __CIRCLE_ERROR - LOCAL __CIRCLE_LOOP - LOCAL __CIRCLE_NEXT - -__CIRCLE_ERROR: - jp __OUT_OF_SCREEN_ERR -CIRCLE: - ;; Entry point - pop hl ; Return Address - pop de ; D = Y - ex (sp), hl ; __CALLEE__ convention - ld e, h ; E = X - ld h, a ; H = R - -#ifdef SCREEN_Y_OFFSET - ld a, SCREEN_Y_OFFSET - add a, d - ld d, a -#endif - -#ifdef SCREEN_X_OFFSET - ld a, SCREEN_X_OFFSET - add a, e - ld e, a -#endif - - ld a, h - add a, d - sub 192 - jr nc, __CIRCLE_ERROR - - ld a, d - sub h - jr c, __CIRCLE_ERROR - - ld a, e - sub h - jr c, __CIRCLE_ERROR - - ld a, h - add a, e - jr c, __CIRCLE_ERROR - - -; __FASTCALL__ Entry: D, E = Y, X point of the center -; A = Radious -__CIRCLE: - push de - ld a, h - exx - pop de ; D'E' = x0, y0 - ld h, a ; H' = r - - ld c, e - ld a, h - add a, d - ld b, a - call __CIRCLE_PLOT ; PLOT (x0, y0 + r) - - ld b, d - ld a, h - add a, e - ld c, a - call __CIRCLE_PLOT ; PLOT (x0 + r, y0) - - ld c, e - ld a, d - sub h - ld b, a - call __CIRCLE_PLOT ; PLOT (x0, y0 - r) - - ld b, d - ld a, e - sub h - ld c, a - call __CIRCLE_PLOT ; PLOT (x0 - r, y0) - - exx - ld b, 0 ; B = x = 0 - ld c, h ; C = y = Radius - ld hl, 1 - or a - sbc hl, bc ; HL = f = 1 - radius - - ex de, hl - ld hl, 0 - or a - sbc hl, bc ; HL = -radius - add hl, hl ; HL = -2 * radius - ex de, hl ; DE = -2 * radius = ddF_y, HL = f - - xor a ; A = ddF_x = 0 - ex af, af' ; Saves it - -__CIRCLE_LOOP: - ld a, b - inc a - cp c - ret nc ; Returns when x >= y - - bit 7, h ; HL >= 0? : if (f >= 0)... - jp nz, __CIRCLE_NEXT - - dec c ; y-- - inc de - inc de ; ddF_y += 2 - - add hl, de ; f += ddF_y - -__CIRCLE_NEXT: - inc b ; x++ - ex af, af' - add a, 2 ; 1 Cycle faster than inc a, inc a - - inc hl ; f++ - push af - add a, l - ld l, a - ld a, h - adc a, 0 ; f = f + ddF_x - ld h, a - pop af - ex af, af' - - push bc - exx - pop hl ; H'L' = Y, X - - ld a, d - add a, h - ld b, a ; B = y0 + y - ld a, e - add a, l - ld c, a ; C = x0 + x - call __CIRCLE_PLOT ; plot(x0 + x, y0 + y) - - ld a, d - add a, h - ld b, a ; B = y0 + y - ld a, e - sub l - ld c, a ; C = x0 - x - call __CIRCLE_PLOT ; plot(x0 - x, y0 + y) - - ld a, d - sub h - ld b, a ; B = y0 - y - ld a, e - add a, l - ld c, a ; C = x0 + x - call __CIRCLE_PLOT ; plot(x0 + x, y0 - y) - - ld a, d - sub h - ld b, a ; B = y0 - y - ld a, e - sub l - ld c, a ; C = x0 - x - call __CIRCLE_PLOT ; plot(x0 - x, y0 - y) - - ld a, l - cp h - jr z, 1f - - ld a, d - add a, l - ld b, a ; B = y0 + x - ld a, e - add a, h - ld c, a ; C = x0 + y - call __CIRCLE_PLOT ; plot(x0 + y, y0 + x) - - ld a, d - add a, l - ld b, a ; B = y0 + x - ld a, e - sub h - ld c, a ; C = x0 - y - call __CIRCLE_PLOT ; plot(x0 - y, y0 + x) - - ld a, d - sub l - ld b, a ; B = y0 - x - ld a, e - add a, h - ld c, a ; C = x0 + y - call __CIRCLE_PLOT ; plot(x0 + y, y0 - x) - - ld a, d - sub l - ld b, a ; B = y0 - x - ld a, e - sub h - ld c, a ; C = x0 + y - call __CIRCLE_PLOT ; plot(x0 - y, y0 - x) - -1: - exx - jp __CIRCLE_LOOP - - - -__CIRCLE_PLOT: - ; Plots a point of the circle, preserving HL and DE - push hl - push de - call __PLOT - pop de - pop hl - ret - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/cls.asm b/src/lib/arch/zxnext/runtime/cls.asm index 21aab73c8..24a28bc23 100644 --- a/src/lib/arch/zxnext/runtime/cls.asm +++ b/src/lib/arch/zxnext/runtime/cls.asm @@ -1,38 +1,8 @@ -;; Clears the user screen (24 rows) - -#include once - - push namespace core - -CLS: - PROC - - ld hl, 0 - ld (COORDS), hl - ld hl, SCR_SIZE - ld (S_POSN), hl - ld hl, (SCREEN_ADDR) - ld (DFCC), hl - ld (hl), 0 - ld d, h - ld e, l - inc de - ld bc, 6143 - ldir - - ; Now clear attributes - - ld hl, (SCREEN_ATTR_ADDR) - ld (DFCCL), hl - ld d, h - ld e, l - inc de - ld a, (ATTR_P) - ld (hl), a - ld bc, 767 - ldir - ret - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/cmp/eq16.asm b/src/lib/arch/zxnext/runtime/cmp/eq16.asm index 85bbdf440..e68d92931 100644 --- a/src/lib/arch/zxnext/runtime/cmp/eq16.asm +++ b/src/lib/arch/zxnext/runtime/cmp/eq16.asm @@ -1,11 +1,8 @@ - push namespace core +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -__EQ16: ; Test if 16bit values HL == DE - ; Returns result in A: 0 = False, FF = True - xor a ; Reset carry flag - sbc hl, de - ret nz - inc a - ret - - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/cmp/eq32.asm b/src/lib/arch/zxnext/runtime/cmp/eq32.asm index fb51892bd..a345f1dc9 100644 --- a/src/lib/arch/zxnext/runtime/cmp/eq32.asm +++ b/src/lib/arch/zxnext/runtime/cmp/eq32.asm @@ -1,29 +1,8 @@ - push namespace core - -__EQ32: ; Test if 32bit value HLDE equals top of the stack - ; Returns result in A: 0 = False, FF = True - exx - pop bc ; Return address - exx - - xor a ; Reset carry flag - pop bc - sbc hl, bc ; Low part - ex de, hl - pop bc - sbc hl, bc ; High part - - exx - push bc ; CALLEE - exx - - ld a, h - or l - or d - or e ; a = 0 and Z flag set only if HLDE = 0 - ld a, 1 - ret z - xor a - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/cmp/eqf.asm b/src/lib/arch/zxnext/runtime/cmp/eqf.asm index a2dd56e3e..29d2e58d7 100644 --- a/src/lib/arch/zxnext/runtime/cmp/eqf.asm +++ b/src/lib/arch/zxnext/runtime/cmp/eqf.asm @@ -1,30 +1,8 @@ -#include once -#include once -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) - -; All of them uses C EDHL registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order BC DE HL (B not used). +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - - push namespace core - -__EQF: ; A = B - call __FPSTACK_PUSH2 - - ; ------------- ROM NOS-EQL - ld b, 0Eh ; For comparison operators, OP must be in B also - rst 28h - defb 0Eh - defb 38h; ; END CALC - - call __FPSTACK_POP - jp __FTOU8 ; Convert to 8 bits +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/cmp/gef.asm b/src/lib/arch/zxnext/runtime/cmp/gef.asm index af52e8dc0..3d5743351 100644 --- a/src/lib/arch/zxnext/runtime/cmp/gef.asm +++ b/src/lib/arch/zxnext/runtime/cmp/gef.asm @@ -1,30 +1,8 @@ -#include once -#include once -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) - -; All of them uses A EDCB registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order A DE BC. +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - - push namespace core - -__GEF: ; A >= B - call __FPSTACK_PUSH2 ; Enters B, A - - ; ------------- ROM NO-LESS - ld b, 09h ; B =< A - rst 28h - defb 09h - defb 38h ; END CALC - - call __FPSTACK_POP - jp __FTOU8 ; Convert to 8 bits +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/cmp/gtf.asm b/src/lib/arch/zxnext/runtime/cmp/gtf.asm index 637887cff..6c91c3e51 100644 --- a/src/lib/arch/zxnext/runtime/cmp/gtf.asm +++ b/src/lib/arch/zxnext/runtime/cmp/gtf.asm @@ -1,29 +1,8 @@ -#include once -#include once -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) - -; All of them uses A EDCB registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order A DE BC. +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - push namespace core - -__GTF: ; A > B - call __FPSTACK_PUSH2 ; ENTERS B, A - - ; ------------- ROM NOS-GRTR - ld b, 0Dh ; B < A - rst 28h - defb 0Dh ; B < A - defb 38h; ; END CALC - - call __FPSTACK_POP - jp __FTOU8; Convert to 8 bits +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/cmp/lef.asm b/src/lib/arch/zxnext/runtime/cmp/lef.asm index e5a67b6a8..c245a6158 100644 --- a/src/lib/arch/zxnext/runtime/cmp/lef.asm +++ b/src/lib/arch/zxnext/runtime/cmp/lef.asm @@ -1,29 +1,8 @@ -#include once -#include once -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) - -; All of them uses A EDCB registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order A DE BC. +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - push namespace core - -__LEF: ; A <= B - call __FPSTACK_PUSH2 ; B, A - - ; ------------- ROM NO-L-EQL - ld b, 0Ah ; B => A - rst 28h - defb 0Ah ; B => A - defb 38h; ; END CALC - - call __FPSTACK_POP - jp __FTOU8 ; Convert to 8 bits +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/cmp/lei16.asm b/src/lib/arch/zxnext/runtime/cmp/lei16.asm index 60d5cb65a..6f07c8a1f 100644 --- a/src/lib/arch/zxnext/runtime/cmp/lei16.asm +++ b/src/lib/arch/zxnext/runtime/cmp/lei16.asm @@ -1,20 +1,8 @@ - push namespace core +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -__LEI16: - PROC - LOCAL checkParity - or a - sbc hl, de - ld a, 1 - ret z - jp po, checkParity - ld a, h - xor 0x80 -checkParity: - ld a, 0 ; False - ret p - inc a ; True - ret - ENDP - - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/cmp/lei32.asm b/src/lib/arch/zxnext/runtime/cmp/lei32.asm index c79c094b6..209e3dee8 100644 --- a/src/lib/arch/zxnext/runtime/cmp/lei32.asm +++ b/src/lib/arch/zxnext/runtime/cmp/lei32.asm @@ -1,38 +1,8 @@ - -#include once - - push namespace core - -__LEI32: ; Test 32 bit values Top of the stack <= HL,DE - PROC - LOCAL checkParity - exx - pop de ; Preserves return address - exx - - call __SUB32 - - exx - push de ; Puts return address back - exx - - ex af, af' - ld a, h - or l - or e - or d - ld a, 1 - ret z - - ex af, af' - jp po, checkParity - ld a, d - xor 0x80 -checkParity: - ld a, 0 ; False - ret p - inc a ; True - ret - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/cmp/lei8.asm b/src/lib/arch/zxnext/runtime/cmp/lei8.asm index 6480a963b..599e68763 100644 --- a/src/lib/arch/zxnext/runtime/cmp/lei8.asm +++ b/src/lib/arch/zxnext/runtime/cmp/lei8.asm @@ -1,25 +1,8 @@ - push namespace core - -__LEI8: ; Signed <= comparison for 8bit int - ; A <= H (registers) - PROC - LOCAL checkParity - sub h - jr nz, __LTI - inc a - ret - -__LTI8: ; Test 8 bit values A < H - sub h - -__LTI: ; Generic signed comparison - jp po, checkParity - xor 0x80 -checkParity: - ld a, 0 ; False - ret p - inc a ; True - ret - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/cmp/ltf.asm b/src/lib/arch/zxnext/runtime/cmp/ltf.asm index f6be6d29e..bbfa4e933 100644 --- a/src/lib/arch/zxnext/runtime/cmp/ltf.asm +++ b/src/lib/arch/zxnext/runtime/cmp/ltf.asm @@ -1,29 +1,8 @@ -#include once -#include once -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) - -; All of them uses A EDCB registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order A HL BC. +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - push namespace core - -__LTF: ; A < B - call __FPSTACK_PUSH2 ; Enters B, A - - ; ------------- ROM NOS-LESS - ld b, 0Ch ; A > B (Operands stack-reversed) - rst 28h - defb 0Ch; ; A > B - defb 38h; ; END CALC - - call __FPSTACK_POP - jp __FTOU8 ; Convert to 8 bits +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/cmp/lti16.asm b/src/lib/arch/zxnext/runtime/cmp/lti16.asm index b59f8fba6..e9012b683 100644 --- a/src/lib/arch/zxnext/runtime/cmp/lti16.asm +++ b/src/lib/arch/zxnext/runtime/cmp/lti16.asm @@ -1,21 +1,8 @@ -#include once +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - push namespace core - -__LTI16: ; Test 8 bit values HL < DE - ; Returns result in A: 0 = False, !0 = True - PROC - LOCAL checkParity - or a - sbc hl, de - jp po, checkParity - ld a, h - xor 0x80 -checkParity: - ld a, 0 ; False - ret p - inc a ; True - ret - ENDP - - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/cmp/lti32.asm b/src/lib/arch/zxnext/runtime/cmp/lti32.asm index 2f3e100a4..07ace187a 100644 --- a/src/lib/arch/zxnext/runtime/cmp/lti32.asm +++ b/src/lib/arch/zxnext/runtime/cmp/lti32.asm @@ -1,29 +1,8 @@ - -#include once - - push namespace core - -__LTI32: ; Test 32 bit values in Top of the stack < HLDE - PROC - LOCAL checkParity - exx - pop de ; Preserves return address - exx - - call __SUB32 - - exx - push de ; Restores return address - exx - - jp po, checkParity - ld a, d - xor 0x80 -checkParity: - ld a, 0 ; False - ret p - inc a ; True - ret - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/cmp/lti8.asm b/src/lib/arch/zxnext/runtime/cmp/lti8.asm index 9a8ac8ced..29b117e71 100644 --- a/src/lib/arch/zxnext/runtime/cmp/lti8.asm +++ b/src/lib/arch/zxnext/runtime/cmp/lti8.asm @@ -1 +1,8 @@ -#include once +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/cmp/nef.asm b/src/lib/arch/zxnext/runtime/cmp/nef.asm index 94cea1b02..6990e1ffc 100644 --- a/src/lib/arch/zxnext/runtime/cmp/nef.asm +++ b/src/lib/arch/zxnext/runtime/cmp/nef.asm @@ -1,29 +1,8 @@ -#include once -#include once -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) - -; All of them uses A EDCB registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order A DE BC. +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - push namespace core - -__NEF: ; A <> B - call __FPSTACK_PUSH2 ; Enters B, A - - ; ------------- ROM NOS-NEQ - ld b, 0Bh - rst 28h - defb 0Bh - defb 38h ; END CALC - - call __FPSTACK_POP - jp __FTOU8 ; Convert to 8 bits +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/copy_attr.asm b/src/lib/arch/zxnext/runtime/copy_attr.asm index 15015ec18..9a31de189 100644 --- a/src/lib/arch/zxnext/runtime/copy_attr.asm +++ b/src/lib/arch/zxnext/runtime/copy_attr.asm @@ -1,80 +1,8 @@ -#ifdef ___PRINT_IS_USED___ -#include once -#endif - -#include once - - push namespace core - -COPY_ATTR: - ; Just copies current permanent attribs into temporal attribs - ; and sets print mode - PROC - - LOCAL INVERSE1 - LOCAL __REFRESH_TMP - -INVERSE1 EQU 02Fh - - ld hl, (ATTR_P) - ld (ATTR_T), hl - - ld hl, FLAGS2 - call __REFRESH_TMP - - ld hl, P_FLAG - call __REFRESH_TMP - - -__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) - -#ifdef ___PRINT_IS_USED___ - LOCAL TABLE - LOCAL CONT2 - - rra ; Over bit to carry - ld a, (FLAGS2) - rla ; Over bit in bit 1, Over2 bit in bit 2 - and 3 ; Only bit 0 and 1 (OVER flag) - - ld c, a - ld b, 0 - - ld hl, TABLE - add hl, bc - ld a, (hl) - ld (PRINT_MODE), a - - ld hl, (P_FLAG) - xor a ; NOP -> INVERSE0 - bit 2, l - jr z, CONT2 - ld a, INVERSE1 ; CPL -> INVERSE1 - -CONT2: - ld (INVERSE_MODE), a - ret - -TABLE: - nop ; NORMAL MODE - xor (hl) ; OVER 1 MODE - and (hl) ; OVER 2 MODE - or (hl) ; OVER 3 MODE - -#else - ret -#endif - -__REFRESH_TMP: - ld a, (hl) - and 0b10101010 - ld c, a - rra - or c - ld (hl), a - ret - - ENDP - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/draw.asm b/src/lib/arch/zxnext/runtime/draw.asm index aea5b49a1..8880ffd1c 100644 --- a/src/lib/arch/zxnext/runtime/draw.asm +++ b/src/lib/arch/zxnext/runtime/draw.asm @@ -1,336 +1,8 @@ -; DRAW using bresenhams algorithm and screen positioning -; Copyleft (k) 2010 by J. Rodriguez (a.k.a. Boriel) http://www.boriel.com -; vim:ts=4:et:sw=4: - -; Y parameter in A -; X parameter in high byte on top of the stack - -#include once -#include once - -#include once -#include once - -#include once -#include once -#include once -#include once - -;; DRAW PROCEDURE - push namespace core - - PROC - - LOCAL __DRAW1 - LOCAL __DRAW2 - LOCAL __DRAW3 - LOCAL __DRAW4, __DRAW4_LOOP - LOCAL __DRAW5 - LOCAL __DRAW6, __DRAW6_LOOP - LOCAL __DRAW_ERROR - LOCAL DX1, DX2, DY1, DY2 - LOCAL __INCX, __INCY, __DECX, __DECY - LOCAL P_FLAG -P_FLAG EQU 23697 - -__DRAW_ERROR: - jp __OUT_OF_SCREEN_ERR - -DRAW: - ;; ENTRY POINT - - LOCAL COORDS - LOCAL __DRAW_SETUP1, __DRAW_START, __PLOTOVER, __PLOTINVERSE - - ex de, hl ; DE = Y OFFSET - pop hl ; return addr - ex (sp), hl ; CALLEE => HL = X OFFSET - ld bc, (COORDS) - - ld a, c - add a, l - ld l, a - ld a, h - adc a, 0 ; HL = HL + C - ld h, a - jr nz, __DRAW_ERROR ; if a <> 0 => Out of Screen - - ld a, b - add a, e - ld e, a - ld a, d - adc a, 0 ; DE = DE + B - ld d, a - jr nz, __DRAW_ERROR ; if a <> 0 => Out of Screen - - ld a, 191 - sub e - jr c, __DRAW_ERROR ; Out of screen - - ld h, e ; now H,L = y2, x2 - -__DRAW: - ; __FASTCALL__ Entry. Plots from (COORDS) to coord H, L - push hl - ex de, hl ; D,E = y2, x2; - - ld a, (P_FLAG) - ld c, a - bit 2, a ; Test for INVERSE1 - jr z, __DRAW_SETUP1 - ld a, 2Fh ; CPL - ld (__PLOTINVERSE), a - ld a, 0A6h ; and (hl) - jp __DRAW_START - -__DRAW_SETUP1: - xor a ; nop - ld (__PLOTINVERSE), a - ld a, 0B6h ; or (hl) - bit 0, c ; Test for OVER - jr z, __DRAW_START - ld a, 0AEh ; xor (hl) - -__DRAW_START: - ld (__PLOTOVER), a ; "Pokes" last operation - exx - ld bc, (COORDS) ; B'C' = y1, x1 - ld d, b ; Saves B' in D' - ld a, 191 - LOCAL __PIXEL_ADDR -__PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR - res 6, h ; Starts from 0 offset - - ;; Now gets pixel mask in A register - ld b, a - inc b - xor a - scf - LOCAL __PIXEL_MASK -__PIXEL_MASK: - rra - djnz __PIXEL_MASK - - ld b, d ; Restores B' from D' - ld de, (SCREEN_ADDR) - add hl, de - pop de ; D'E' = y2, x2 - exx ; At this point: D'E' = y2,x2 coords - ; B'C' = y1, y1 coords - ; H'L' = Screen Address of pixel - - ex af, af' ; Saves A reg for later - ; A' = Pixel mask - - ld bc, (COORDS) ; B,C = y1, x1 - - ld a, e - sub c ; dx = X2 - X1 - ld c, a ; Saves dx in c - - ld a, 0Ch ; INC C opcode - ld hl, __INCX ; xi = 1 - jr nc, __DRAW1 - - ld a, c - neg ; dx = X1 - X2 - ld c, a - ld a, 0Dh ; DEC C opcode - ld hl, __DECX ; xi = -1 - -__DRAW1: - ld (DX1), a - ld (DX1 + 2), hl ; Updates DX1 call address - ld (DX2), a - ld (DX2 + 2), hl ; Updates DX2 call address - - ld a, d - sub b ; dy = Y2 - Y1 - ld b, a ; Saves dy in b - - ld a, 4 ; INC B opcode - ld hl, __INCY ; y1 = 1 - jr nc, __DRAW2 - - ld a, b - neg - ld b, a ; dy = Y2 - Y1 - ld a, 5 ; DEC B opcode - ld hl, __DECY ; y1 = -1 - -__DRAW2: - ld (DY1), a - ld (DY1 + 2), hl ; Updates DX1 call address - ld (DY2), a - ld (DY2 + 2), hl ; Updates DX2 call address - - ld a, b - sub c ; dy - dx - jr c, __DRAW_DX_GT_DY ; DX > DY - - ; At this point DY >= DX - ; -------------------------- - ; HL = error = dY / 2 - ld h, 0 - ld l, b - srl l - - ; DE = -dX - xor a - sub c - ld e, a - sbc a, a - ld d, a - - ; BC = DY - ld c, b - ld b, h - - exx - scf ; Sets Carry to signal update ATTR - ex af, af' ; Brings back pixel mask - ld e, a ; Saves it in free E register - jp __DRAW4_LOOP - -__DRAW3: ; While c != e => while y != y2 - exx - add hl, de ; error -= dX - bit 7, h ; - exx ; recover coordinates - jr z, __DRAW4 ; if error < 0 - - exx - add hl, bc ; error += dY - exx - - ld a, e -DX1: ; x += xi - inc c - call __INCX ; This address will be dynamically updated - ld e, a - -__DRAW4: - -DY1: ; y += yi - inc b - call __INCY ; This address will be dynamically updated - ld a, e ; Restores A reg. - call __FASTPLOT - -__DRAW4_LOOP: - ld a, b - cp d - jp nz, __DRAW3 - ld (COORDS), bc - ret - -__DRAW_DX_GT_DY: ; DX > DY - ; -------------------------- - ; HL = error = dX / 2 - ld h, 0 - ld l, c - srl l ; HL = error = DX / 2 - - ; DE = -dY - xor a - sub b - ld e, a - sbc a, a - ld d, a - - ; BC = dX - ld b, h - - exx - ld d, e - scf ; Sets Carry to signal update ATTR - ex af, af' ; Brings back pixel mask - ld e, a ; Saves it in free E register - jp __DRAW6_LOOP - -__DRAW5: ; While loop - exx - add hl, de ; error -= dY - bit 7, h ; if (error < 0) - exx ; Restore coords - jr z, __DRAW6 ; - exx - add hl, bc ; error += dX - exx - -DY2: ; y += yi - inc b - call __INCY ; This address will be dynamically updated - -__DRAW6: - ld a, e -DX2: ; x += xi - inc c - call __INCX ; This address will be dynamically updated - ld e, a - call __FASTPLOT - -__DRAW6_LOOP: - ld a, c ; Current X coord - cp d - jp nz, __DRAW5 - ld (COORDS), bc - ret - -COORDS EQU 5C7Dh - -__DRAW_END: - exx - ret - - ;; Given a A mask and an HL screen position - ;; return the next left position - ;; Also updates BC coords -__DECX EQU SP.PixelLeft - - ;; Like the above, but to the RIGHT - ;; Also updates BC coords -__INCX EQU SP.PixelRight - - ;; Given an HL screen position, calculates - ;; the above position - ;; Also updates BC coords -__INCY EQU SP.PixelUp - - ;; Given an HL screen position, calculates - ;; the above position - ;; Also updates BC coords -__DECY EQU SP.PixelDown - - ;; Puts the A register MASK in (HL) -__FASTPLOT: -__PLOTINVERSE: - nop ; Replace with CPL if INVERSE 1 -__PLOTOVER: - or (hl) ; Replace with XOR (hl) if OVER 1 AND INVERSE 0 - ; Replace with AND (hl) if INVERSE 1 - - ld (hl), a - ex af, af' ; Recovers flag. If Carry set => update ATTR - ld a, e ; Recovers A reg - ret nc - - push hl - push de - push bc - call SET_PIXEL_ADDR_ATTR - pop bc - pop de - pop hl - - LOCAL __FASTPLOTEND -__FASTPLOTEND: - or a ; Resets carry flag - ex af, af' ; Recovers A reg - ld a, e - ret - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/draw3.asm b/src/lib/arch/zxnext/runtime/draw3.asm index 956acffb8..eb67eff3d 100644 --- a/src/lib/arch/zxnext/runtime/draw3.asm +++ b/src/lib/arch/zxnext/runtime/draw3.asm @@ -1,511 +1,8 @@ -; ----------------------------------------------------------- -; vim: et:ts=4:sw=4:ruler: +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; DRAW an arc using ZX ROM algorithm. -; DRAW x, y, r => r = Arc in radians +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -; r parameter in A ED BC register -; X, and Y parameter in high byte on top of the stack - -#include once -#include once -#include once -#include once -#include once - -; Ripped from the ZX Spectrum ROM - - push namespace core - -DRAW3: - PROC - LOCAL STACK_TO_BC - LOCAL STACK_TO_A - LOCAL L2477 - LOCAL L2420 - LOCAL L2439 - LOCAL L245F - LOCAL L23C1 - LOCAL L2D28 - LOCAL SUM_C, SUM_B - -L2D28 EQU 02D28h -STACK_TO_BC EQU 2307h -STACK_TO_A EQU 2314h - - exx - ex af, af' ;; Preserves ARC - pop hl - pop de - ex (sp), hl ;; CALLEE - push de - call __FPSTACK_I16 ;; X Offset - pop hl - call __FPSTACK_I16 ;; Y Offset - exx - ex af, af' - call __FPSTACK_PUSH ;; R Arc - -; Now enter the calculator and store the complete rotation angle in mem-5 - - RST 28H ;; FP-CALC x, y, A. - DEFB $C5 ;;st-mem-5 x, y, A. - -; Test the angle for the special case of 360 degrees. - - DEFB $A2 ;;stk-half x, y, A, 1/2. - DEFB $04 ;;multiply x, y, A/2. - DEFB $1F ;;sin x, y, sin(A/2). - DEFB $31 ;;duplicate x, y, sin(A/2),sin(A/2) - DEFB $30 ;;not x, y, sin(A/2), (0/1). - DEFB $30 ;;not x, y, sin(A/2), (1/0). - DEFB $00 ;;jump-true x, y, sin(A/2). - - DEFB $06 ;;forward to L23A3, DR-SIN-NZ - ;;if sin(r/2) is not zero. - -; The third parameter is 2*PI (or a multiple of 2*PI) so a 360 degrees turn -; would just be a straight line. Eliminating this case here prevents -; division by zero at later stage. - - DEFB $02 ;;delete x, y. - DEFB $38 ;;end-calc x, y. - JP L2477 - -; --- - -; An arc can be drawn. - -;; DR-SIN-NZ - DEFB $C0 ;;st-mem-0 x, y, sin(A/2). store mem-0 - DEFB $02 ;;delete x, y. - -; The next step calculates (roughly) the diameter of the circle of which the -; arc will form part. This value does not have to be too accurate as it is -; only used to evaluate the number of straight lines and then discarded. -; After all for a circle, the radius is used. Consequently, a circle of -; radius 50 will have 24 straight lines but an arc of radius 50 will have 20 -; straight lines - when drawn in any direction. -; So that simple arithmetic can be used, the length of the chord can be -; calculated as X+Y rather than by Pythagoras Theorem and the sine of the -; nearest angle within reach is used. - - DEFB $C1 ;;st-mem-1 x, y. store mem-1 - DEFB $02 ;;delete x. - - DEFB $31 ;;duplicate x, x. - DEFB $2A ;;abs x, x (+ve). - DEFB $E1 ;;get-mem-1 x, X, y. - DEFB $01 ;;exchange x, y, X. - DEFB $E1 ;;get-mem-1 x, y, X, y. - DEFB $2A ;;abs x, y, X, Y (+ve). - DEFB $0F ;;addition x, y, X+Y. - DEFB $E0 ;;get-mem-0 x, y, X+Y, sin(A/2). - DEFB $05 ;;division x, y, X+Y/sin(A/2). - DEFB $2A ;;abs x, y, X+Y/sin(A/2) = D. - -; Bring back sin(A/2) from mem-0 which will shortly get trashed. -; Then bring D to the top of the stack again. - - DEFB $E0 ;;get-mem-0 x, y, D, sin(A/2). - DEFB $01 ;;exchange x, y, sin(A/2), D. - -; Note. that since the value at the top of the stack has arisen as a result -; of division then it can no longer be in integer form and the next re-stack -; is unnecessary. Only the Sinclair ZX80 had integer division. - - ;;DEFB $3D ;;re-stack (unnecessary) - - DEFB $38 ;;end-calc x, y, sin(A/2), D. - -; The next test avoids drawing 4 straight lines when the start and end pixels -; are adjacent (or the same) but is probably best dispensed with. - - LD A,(HL) ; fetch exponent byte of D. - CP $81 ; compare to 1 - JR NC,L23C1 ; forward, if > 1, to DR-PRMS - -; else delete the top two stack values and draw a simple straight line. - - RST 28H ;; FP-CALC - DEFB $02 ;;delete - DEFB $02 ;;delete - DEFB $38 ;;end-calc x, y. - - JP L2477 ; to LINE-DRAW - -; --- - -; The ARC will consist of multiple straight lines so call the CIRCLE-DRAW -; PARAMETERS ROUTINE to pre-calculate sine values from the angle (in mem-5) -; and determine also the number of straight lines from that value and the -; 'diameter' which is at the top of the calculator stack. - -;; DR-PRMS -L23C1: CALL 247Dh ; routine CD-PRMS1 - - ; mem-0 ; (A)/No. of lines (=a) (step angle) - ; mem-1 ; sin(a/2) - ; mem-2 ; - - ; mem-3 ; cos(a) const - ; mem-4 ; sin(a) const - ; mem-5 ; Angle of rotation (A) in - ; B ; Count of straight lines - max 252. - - PUSH BC ; Save the line count on the machine stack. - -; Remove the now redundant diameter value D. - - RST 28H ;; FP-CALC x, y, sin(A/2), D. - DEFB $02 ;;delete x, y, sin(A/2). - -; Dividing the sine of the step angle by the sine of the total angle gives -; the length of the initial chord on a unary circle. This factor f is used -; to scale the coordinates of the first line which still points in the -; direction of the end point and may be larger. - - DEFB $E1 ;;get-mem-1 x, y, sin(A/2), sin(a/2) - DEFB $01 ;;exchange x, y, sin(a/2), sin(A/2) - DEFB $05 ;;division x, y, sin(a/2)/sin(A/2) - DEFB $C1 ;;st-mem-1 x, y. f. - DEFB $02 ;;delete x, y. - -; With the factor stored, scale the x coordinate first. - - DEFB $01 ;;exchange y, x. - DEFB $31 ;;duplicate y, x, x. - DEFB $E1 ;;get-mem-1 y, x, x, f. - DEFB $04 ;;multiply y, x, x*f (=xx) - DEFB $C2 ;;st-mem-2 y, x, xx. - DEFB $02 ;;delete y. x. - -; Now scale the y coordinate. - - DEFB $01 ;;exchange x, y. - DEFB $31 ;;duplicate x, y, y. - DEFB $E1 ;;get-mem-1 x, y, y, f - DEFB $04 ;;multiply x, y, y*f (=yy) - -; Note. 'sin' and 'cos' trash locations mem-0 to mem-2 so fetch mem-2 to the -; calculator stack for safe keeping. - - DEFB $E2 ;;get-mem-2 x, y, yy, xx. - -; Once we get the coordinates of the first straight line then the 'ROTATION -; FORMULA' used in the arc loop will take care of all other points, but we -; now use a variation of that formula to rotate the first arc through (A-a)/2 -; radians. -; -; xRotated = y * sin(angle) + x * cos(angle) -; yRotated = y * cos(angle) - x * sin(angle) -; - - DEFB $E5 ;;get-mem-5 x, y, yy, xx, A. - DEFB $E0 ;;get-mem-0 x, y, yy, xx, A, a. - DEFB $03 ;;subtract x, y, yy, xx, A-a. - DEFB $A2 ;;stk-half x, y, yy, xx, A-a, 1/2. - DEFB $04 ;;multiply x, y, yy, xx, (A-a)/2. (=angle) - DEFB $31 ;;duplicate x, y, yy, xx, angle, angle. - DEFB $1F ;;sin x, y, yy, xx, angle, sin(angle) - DEFB $C5 ;;st-mem-5 x, y, yy, xx, angle, sin(angle) - DEFB $02 ;;delete x, y, yy, xx, angle - - DEFB $20 ;;cos x, y, yy, xx, cos(angle). - -; Note. mem-0, mem-1 and mem-2 can be used again now... - - DEFB $C0 ;;st-mem-0 x, y, yy, xx, cos(angle). - DEFB $02 ;;delete x, y, yy, xx. - - DEFB $C2 ;;st-mem-2 x, y, yy, xx. - DEFB $02 ;;delete x, y, yy. - - DEFB $C1 ;;st-mem-1 x, y, yy. - DEFB $E5 ;;get-mem-5 x, y, yy, sin(angle) - DEFB $04 ;;multiply x, y, yy*sin(angle). - DEFB $E0 ;;get-mem-0 x, y, yy*sin(angle), cos(angle) - DEFB $E2 ;;get-mem-2 x, y, yy*sin(angle), cos(angle), xx. - DEFB $04 ;;multiply x, y, yy*sin(angle), xx*cos(angle). - DEFB $0F ;;addition x, y, xRotated. - DEFB $E1 ;;get-mem-1 x, y, xRotated, yy. - DEFB $01 ;;exchange x, y, yy, xRotated. - DEFB $C1 ;;st-mem-1 x, y, yy, xRotated. - DEFB $02 ;;delete x, y, yy. - - DEFB $E0 ;;get-mem-0 x, y, yy, cos(angle). - DEFB $04 ;;multiply x, y, yy*cos(angle). - DEFB $E2 ;;get-mem-2 x, y, yy*cos(angle), xx. - DEFB $E5 ;;get-mem-5 x, y, yy*cos(angle), xx, sin(angle). - DEFB $04 ;;multiply x, y, yy*cos(angle), xx*sin(angle). - DEFB $03 ;;subtract x, y, yRotated. - DEFB $C2 ;;st-mem-2 x, y, yRotated. - -; Now the initial x and y coordinates are made positive and summed to see -; if they measure up to anything significant. - - DEFB $2A ;;abs x, y, yRotated'. - DEFB $E1 ;;get-mem-1 x, y, yRotated', xRotated. - DEFB $2A ;;abs x, y, yRotated', xRotated'. - DEFB $0F ;;addition x, y, yRotated+xRotated. - DEFB $02 ;;delete x, y. - - DEFB $38 ;;end-calc x, y. - -; Although the test value has been deleted it is still above the calculator -; stack in memory and conveniently DE which points to the first free byte -; addresses the exponent of the test value. - - LD A,(DE) ; Fetch exponent of the length indicator. - CP $81 ; Compare to that for 1 - - POP BC ; Balance the machine stack - - JP C,L2477 ; forward, if the coordinates of first line - ; don't add up to more than 1, to LINE-DRAW - -; Continue when the arc will have a discernable shape. - - PUSH BC ; Restore line counter to the machine stack. - -; The parameters of the DRAW command were relative and they are now converted -; to absolute coordinates by adding to the coordinates of the last point -; plotted. The first two values on the stack are the terminal tx and ty -; coordinates. The x-coordinate is converted first but first the last point -; plotted is saved as it will initialize the moving ax, value. - - RST 28H ;; FP-CALC x, y. - DEFB $01 ;;exchange y, x. - DEFB $38 ;;end-calc y, x. - - LD A,(COORDS) ;; Fetch System Variable COORDS-x - CALL L2D28 ;; routine STACK-A - - RST 28H ;; FP-CALC y, x, last-x. - -; Store the last point plotted to initialize the moving ax value. - - DEFB $C0 ;;st-mem-0 y, x, last-x. - DEFB $0F ;;addition y, absolute x. - DEFB $01 ;;exchange tx, y. - DEFB $38 ;;end-calc tx, y. - - LD A,(COORDS + 1) ; Fetch System Variable COORDS-y - CALL L2D28 ; routine STACK-A - - RST 28H ;; FP-CALC tx, y, last-y. - -; Store the last point plotted to initialize the moving ay value. - - DEFB $C5 ;;st-mem-5 tx, y, last-y. - DEFB $0F ;;addition tx, ty. - -; Fetch the moving ax and ay to the calculator stack. - - DEFB $E0 ;;get-mem-0 tx, ty, ax. - DEFB $E5 ;;get-mem-5 tx, ty, ax, ay. - DEFB $38 ;;end-calc tx, ty, ax, ay. - - POP BC ; Restore the straight line count. - -; ----------------------------------- -; THE 'CIRCLE/DRAW CONVERGENCE POINT' -; ----------------------------------- -; The CIRCLE and ARC-DRAW commands converge here. -; -; Note. for both the CIRCLE and ARC commands the minimum initial line count -; is 4 (as set up by the CD_PARAMS routine) and so the zero flag will never -; be set and the loop is always entered. The first test is superfluous and -; the jump will always be made to ARC-START. - -;; DRW-STEPS -L2420: - DEC B ; decrement the arc count (4,8,12,16...). - - ;JR Z,L245F ; forward, if zero (not possible), to ARC-END - - JP L2439 ; forward to ARC-START - -; -------------- -; THE 'ARC LOOP' -; -------------- -; -; The arc drawing loop will draw up to 31 straight lines for a circle and up -; 251 straight lines for an arc between two points. In both cases the final -; closing straight line is drawn at ARC_END, but it otherwise loops back to -; here to calculate the next coordinate using the ROTATION FORMULA where (a) -; is the previously calculated, constant CENTRAL ANGLE of the arcs. -; -; Xrotated = x * cos(a) - y * sin(a) -; Yrotated = x * sin(a) + y * cos(a) -; -; The values cos(a) and sin(a) are pre-calculated and held in mem-3 and mem-4 -; for the duration of the routine. -; Memory location mem-1 holds the last relative x value (rx) and mem-2 holds -; the last relative y value (ry) used by DRAW. -; -; Note. that this is a very clever twist on what is after all a very clever, -; well-used formula. Normally the rotation formula is used with the x and y -; coordinates from the centre of the circle (or arc) and a supplied angle to -; produce two new x and y coordinates in an anticlockwise direction on the -; circumference of the circle. -; What is being used here, instead, is the relative X and Y parameters from -; the last point plotted that are required to get to the current point and -; the formula returns the next relative coordinates to use. - -;; ARC-LOOP -L2425: - RST 28H ;; FP-CALC - DEFB $E1 ;;get-mem-1 rx. - DEFB $31 ;;duplicate rx, rx. - DEFB $E3 ;;get-mem-3 cos(a) - DEFB $04 ;;multiply rx, rx*cos(a). - DEFB $E2 ;;get-mem-2 rx, rx*cos(a), ry. - DEFB $E4 ;;get-mem-4 rx, rx*cos(a), ry, sin(a). - DEFB $04 ;;multiply rx, rx*cos(a), ry*sin(a). - DEFB $03 ;;subtract rx, rx*cos(a) - ry*sin(a) - DEFB $C1 ;;st-mem-1 rx, new relative x rotated. - DEFB $02 ;;delete rx. - - DEFB $E4 ;;get-mem-4 rx, sin(a). - DEFB $04 ;;multiply rx*sin(a) - DEFB $E2 ;;get-mem-2 rx*sin(a), ry. - DEFB $E3 ;;get-mem-3 rx*sin(a), ry, cos(a). - DEFB $04 ;;multiply rx*sin(a), ry*cos(a). - DEFB $0F ;;addition rx*sin(a) + ry*cos(a). - DEFB $C2 ;;st-mem-2 new relative y rotated. - DEFB $02 ;;delete . - DEFB $38 ;;end-calc . - -; Note. the calculator stack actually holds tx, ty, ax, ay -; and the last absolute values of x and y -; are now brought into play. -; -; Magically, the two new rotated coordinates rx and ry are all that we would -; require to draw a circle or arc - on paper! -; The Spectrum DRAW routine draws to the rounded x and y coordinate and so -; repetitions of values like 3.49 would mean that the fractional parts -; would be lost until eventually the draw coordinates might differ from the -; floating point values used above by several pixels. -; For this reason the accurate offsets calculated above are added to the -; accurate, absolute coordinates maintained in ax and ay and these new -; coordinates have the integer coordinates of the last plot position -; ( from System Variable COORDS ) subtracted from them to give the relative -; coordinates required by the DRAW routine. - -; The mid entry point. - -;; ARC-START -L2439: - PUSH BC ; Preserve the arc counter on the machine stack. - -; Store the absolute ay in temporary variable mem-0 for the moment. - - RST 28H ;; FP-CALC ax, ay. - DEFB $C0 ;;st-mem-0 ax, ay. - DEFB $02 ;;delete ax. - -; Now add the fractional relative x coordinate to the fractional absolute -; x coordinate to obtain a new fractional x-coordinate. - - DEFB $E1 ;;get-mem-1 ax, xr. - DEFB $0F ;;addition ax+xr (= new ax). - DEFB $31 ;;duplicate ax, ax. - DEFB $38 ;;end-calc ax, ax. - - LD A,(COORDS) ; COORDS-x last x (integer ix 0-255) - CALL L2D28 ; routine STACK-A - - RST 28H ;; FP-CALC ax, ax, ix. - DEFB $03 ;;subtract ax, ax-ix = relative DRAW Dx. - -; Having calculated the x value for DRAW do the same for the y value. - - DEFB $E0 ;;get-mem-0 ax, Dx, ay. - DEFB $E2 ;;get-mem-2 ax, Dx, ay, ry. - DEFB $0F ;;addition ax, Dx, ay+ry (= new ay). - DEFB $C0 ;;st-mem-0 ax, Dx, ay. - DEFB $01 ;;exchange ax, ay, Dx, - DEFB $E0 ;;get-mem-0 ax, ay, Dx, ay. - DEFB $38 ;;end-calc ax, ay, Dx, ay. - - LD A,(COORDS + 1) ; COORDS-y last y (integer iy 0-175) - CALL L2D28 ; routine STACK-A - - RST 28H ;; FP-CALC ax, ay, Dx, ay, iy. - DEFB $03 ;;subtract ax, ay, Dx, ay-iy ( = Dy). - DEFB $38 ;;end-calc ax, ay, Dx, Dy. - - CALL L2477 ; Routine DRAW-LINE draws (Dx,Dy) relative to - ; the last pixel plotted leaving absolute x - ; and y on the calculator stack. - ; ax, ay. - - POP BC ; Restore the arc counter from the machine stack. - - DJNZ L2425 ; Decrement and loop while > 0 to ARC-LOOP - -; ------------- -; THE 'ARC END' -; ------------- - -; To recap the full calculator stack is tx, ty, ax, ay. - -; Just as one would do if drawing the curve on paper, the final line would -; be drawn by joining the last point plotted to the initial start point -; in the case of a CIRCLE or to the calculated end point in the case of -; an ARC. -; The moving absolute values of x and y are no longer required and they -; can be deleted to expose the closing coordinates. - -;; ARC-END -L245F: - RST 28H ;; FP-CALC tx, ty, ax, ay. - DEFB $02 ;;delete tx, ty, ax. - DEFB $02 ;;delete tx, ty. - DEFB $01 ;;exchange ty, tx. - DEFB $38 ;;end-calc ty, tx. - -; First calculate the relative x coordinate to the end-point. - - LD A,(COORDS) ; COORDS-x - CALL L2D28 ; routine STACK-A - - RST 28H ;; FP-CALC ty, tx, coords_x. - DEFB $03 ;;subtract ty, rx. - -; Next calculate the relative y coordinate to the end-point. - - DEFB $01 ;;exchange rx, ty. - DEFB $38 ;;end-calc rx, ty. - - LD A,(COORDS + 1) ; COORDS-y - CALL L2D28 ; routine STACK-A - - RST 28H ;; FP-CALC rx, ty, coords_y - DEFB $03 ;;subtract rx, ry. - DEFB $38 ;;end-calc rx, ry. -; Finally draw the last straight line. -L2477: - call STACK_TO_BC ;;Pops x, and y, and stores it in B, C - ld hl, (COORDS) ;;Calculates x2 and y2 in L, H - - rl e ;; Rotate left to carry - ld a, c - jr nc, SUM_C - neg -SUM_C: - add a, l - ld l, a ;; X2 - - rl d ;; Low sign to carry - ld a, b - jr nc, SUM_B - neg -SUM_B: - add a, h - ld h, a - jp __DRAW ;;forward to LINE-DRAW (Fastcalled) - - ENDP - - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/error.asm b/src/lib/arch/zxnext/runtime/error.asm index e603d5477..475ccaa81 100644 --- a/src/lib/arch/zxnext/runtime/error.asm +++ b/src/lib/arch/zxnext/runtime/error.asm @@ -1,44 +1,8 @@ -; Simple error control routines -; vim:ts=4:et: - - push namespace core - -ERR_NR EQU 23610 ; Error code system variable - - -; Error code definitions (as in ZX spectrum manual) - -; Set error code with: -; ld a, ERROR_CODE -; ld (ERR_NR), a - - -ERROR_Ok EQU -1 -ERROR_SubscriptWrong EQU 2 -ERROR_OutOfMemory EQU 3 -ERROR_OutOfScreen EQU 4 -ERROR_NumberTooBig EQU 5 -ERROR_InvalidArg EQU 9 -ERROR_IntOutOfRange EQU 10 -ERROR_NonsenseInBasic EQU 11 -ERROR_InvalidFileName EQU 14 -ERROR_InvalidColour EQU 19 -ERROR_BreakIntoProgram EQU 20 -ERROR_TapeLoadingErr EQU 26 - - -; Raises error using RST #8 -__ERROR: - ld (__ERROR_CODE), a - rst 8 -__ERROR_CODE: - nop - ret - -; Sets the error system variable, but keeps running. -; Usually this instruction if followed by the END intermediate instruction. -__STOP: - ld (ERR_NR), a - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/f16tofreg.asm b/src/lib/arch/zxnext/runtime/f16tofreg.asm index 97ab00997..1aa615330 100644 --- a/src/lib/arch/zxnext/runtime/f16tofreg.asm +++ b/src/lib/arch/zxnext/runtime/f16tofreg.asm @@ -1,52 +1,8 @@ -#include once -#include once - - push namespace core - -__F16TOFREG: ; Converts a 16.16 signed fixed point (stored in DEHL) - ; to a Floating Point Number returned in (C ED CB) - PROC - - LOCAL __F16TOFREG2 - - ld a, d - or a ; Test sign - - jp p, __F16TOFREG2 ; It was positive, proceed as 32bit unsigned - - call __NEG32 ; Convert it to positive - call __F16TOFREG2 ; Convert it to Floating point - - set 7, e ; Put the sign bit (negative) in the 31bit of mantissa - ret - - -__F16TOFREG2: ; Converts an unsigned 32 bit integer (DEHL) - ; to a Floating point number returned in C DE HL - - ld a, d - or e - or h - or l - ld b, h - ld c, l - ret z ; Return 00 0000 0000 if 0 - - push de - push hl - - exx - pop de ; Loads integer into B'C' D'E' - pop bc - exx - - ld l, 112 ; Exponent - ld bc, 0 ; DEBC = 0 - ld d, b - ld e, c - jp __U32TOFREG_LOOP ; Proceed as an integer - - ENDP - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/flash.asm b/src/lib/arch/zxnext/runtime/flash.asm index 2ba5a7e53..c4b28606c 100644 --- a/src/lib/arch/zxnext/runtime/flash.asm +++ b/src/lib/arch/zxnext/runtime/flash.asm @@ -1,47 +1,8 @@ -; Sets flash flag in ATTR_P permanently -; Parameter: Paper color in A register - -#include once - - push namespace core - -FLASH: - ld hl, ATTR_P - - PROC - LOCAL IS_TR - LOCAL IS_ZERO - -__SET_FLASH: - ; Another entry. This will set the flash flag at location pointer by DE - cp 8 - jr z, IS_TR - - ; # Convert to 0/1 - or a - jr z, IS_ZERO - ld a, 0x80 - -IS_ZERO: - ld b, a ; Saves the color - ld a, (hl) - and 07Fh ; Clears previous value - or b - ld (hl), a - inc hl - res 7, (hl) ;Reset bit 7 to disable transparency - ret - -IS_TR: ; transparent - inc hl ; Points DE to MASK_T or MASK_P - set 7, (hl) ;Set bit 7 to enable transparency - ret - -; Sets the FLASH flag passed in A register in the ATTR_T variable -FLASH_TMP: - ld hl, ATTR_T - jr __SET_FLASH - ENDP - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/ftof16reg.asm b/src/lib/arch/zxnext/runtime/ftof16reg.asm index b1b19f9fb..2a80b544e 100644 --- a/src/lib/arch/zxnext/runtime/ftof16reg.asm +++ b/src/lib/arch/zxnext/runtime/ftof16reg.asm @@ -1,46 +1,8 @@ -#include once - - push namespace core - -__FTOF16REG: ; Converts a Float to 16.16 (32 bit) fixed point decimal - ; Input FP number in A EDCB (A exponent, EDCB mantissa) - - ld l, a ; Saves exponent for later - or d - or e - or b - or c - ld h, e - ret z ; Return if ZERO - - push hl ; Stores it for later (Contains sign in H, exponent in L) - - push de - push bc - - exx - pop de ; Loads mantissa into C'B' E'D' - pop bc ; - - set 7, c ; Highest mantissa bit is always 1 - exx - - ld hl, 0 ; DEHL = 0 - ld d, h - ld e, l - - pop bc - - ld a, c ; Get exponent - sub 112 ; Exponent -= 128 + 16 - - push bc ; Saves sign in b again - - jp z, __FTOU32REG_END ; If it was <= 128, we are done (Integers must be > 128) - jp c, __FTOU32REG_END ; It was decimal (0.xxx). We are done (return 0) - - ld b, a ; Loop counter = exponent - 128 + 16 (we need to shift 16 bit more) - jp __FTOU32REG_LOOP ; proceed as an u32 integer - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/ftou32reg.asm b/src/lib/arch/zxnext/runtime/ftou32reg.asm index ab84e3072..304f702d4 100644 --- a/src/lib/arch/zxnext/runtime/ftou32reg.asm +++ b/src/lib/arch/zxnext/runtime/ftou32reg.asm @@ -1,94 +1,8 @@ -#include once - - push namespace core - -__FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS 32 bit signed) - ; Input FP number in A EDCB (A exponent, EDCB mantissa) - ; Output: DEHL 32 bit number (signed) - PROC - - LOCAL __IS_FLOAT - LOCAL __NEGATE - - or a - jr nz, __IS_FLOAT - ; Here if it is a ZX ROM Integer - - ld h, c - ld l, d - ld d, e - ret - -__IS_FLOAT: ; Jumps here if it is a true floating point number - ld h, e - push hl ; Stores it for later (Contains Sign in H) - - push de - push bc - - exx - pop de ; Loads mantissa into C'B' E'D' - pop bc ; - - set 7, c ; Highest mantissa bit is always 1 - exx - - ld hl, 0 ; DEHL = 0 - ld d, h - ld e, l - - ;ld a, c ; Get exponent - sub 128 ; Exponent -= 128 - jr z, __FTOU32REG_END ; If it was <= 128, we are done (Integers must be > 128) - jr c, __FTOU32REG_END ; It was decimal (0.xxx). We are done (return 0) - - ld b, a ; Loop counter = exponent - 128 - -__FTOU32REG_LOOP: - exx ; Shift C'B' E'D' << 1, output bit stays in Carry - sla d - rl e - rl b - rl c - - exx ; Shift DEHL << 1, inserting the carry on the right - rl l - rl h - rl e - rl d - - djnz __FTOU32REG_LOOP - -__FTOU32REG_END: - pop af ; Take the sign bit - or a ; Sets SGN bit to 1 if negative - jp m, __NEGATE ; Negates DEHL - - ret - -__NEGATE: - exx - ld a, d - or e - or b - or c - exx - jr z, __END - inc l - jr nz, __END - inc h - jr nz, __END - inc de -LOCAL __END -__END: - jp __NEG32 - ENDP - - -__FTOU8: ; Converts float in C ED LH to Unsigned byte in A - call __FTOU32REG - ld a, l - ret - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/iload32.asm b/src/lib/arch/zxnext/runtime/iload32.asm index a647bcdc9..bfd142743 100644 --- a/src/lib/arch/zxnext/runtime/iload32.asm +++ b/src/lib/arch/zxnext/runtime/iload32.asm @@ -1,21 +1,8 @@ -; __FASTCALL__ routine which -; loads a 32 bits integer into DE,HL -; stored at position pointed by POINTER HL -; DE,HL <-- (HL) - - push namespace core - -__ILOAD32: - ld e, (hl) - inc hl - ld d, (hl) - inc hl - ld a, (hl) - inc hl - ld h, (hl) - ld l, a - ex de, hl - ret - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/iloadf.asm b/src/lib/arch/zxnext/runtime/iloadf.asm index b425e3588..32ff8669b 100644 --- a/src/lib/arch/zxnext/runtime/iloadf.asm +++ b/src/lib/arch/zxnext/runtime/iloadf.asm @@ -1,32 +1,8 @@ -; __FASTCALL__ routine which -; loads a 40 bits floating point into A ED CB -; stored at position pointed by POINTER HL -;A DE, BC <-- ((HL)) - - push namespace core - -__ILOADF: - ld a, (hl) - inc hl - ld h, (hl) - ld l, a - -; __FASTCALL__ routine which -; loads a 40 bits floating point into A ED CB -; stored at position pointed by POINTER HL -;A DE, BC <-- (HL) - -__LOADF: ; Loads a 40 bits FP number from address pointed by HL - ld a, (hl) - inc hl - ld e, (hl) - inc hl - ld d, (hl) - inc hl - ld c, (hl) - inc hl - ld b, (hl) - ret - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/in_screen.asm b/src/lib/arch/zxnext/runtime/in_screen.asm index 724450c7a..ece104e67 100644 --- a/src/lib/arch/zxnext/runtime/in_screen.asm +++ b/src/lib/arch/zxnext/runtime/in_screen.asm @@ -1,30 +1,8 @@ -#include once -#include once - - push namespace core - -__IN_SCREEN: - ; Returns NO carry if current coords (D, E) - ; are OUT of the screen limits - - PROC - LOCAL __IN_SCREEN_ERR - - ld hl, SCR_SIZE - ld a, e - cp l - jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range - - ld a, d - cp h - ret c ; Return if carry (OK) - -__IN_SCREEN_ERR: -__OUT_OF_SCREEN_ERR: - ; Jumps here if out of screen - ld a, ERROR_OutOfScreen - jp __STOP ; Saves error code and exits - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/ink.asm b/src/lib/arch/zxnext/runtime/ink.asm index 7dcb64113..286ba4231 100644 --- a/src/lib/arch/zxnext/runtime/ink.asm +++ b/src/lib/arch/zxnext/runtime/ink.asm @@ -1,47 +1,8 @@ -; Sets ink color in ATTR_P permanently -; Parameter: Paper color in A register - -#include once - - push namespace core - -INK: - PROC - LOCAL __SET_INK - LOCAL __SET_INK2 - - ld de, ATTR_P - -__SET_INK: - cp 8 - jr nz, __SET_INK2 - - inc de ; Points DE to MASK_T or MASK_P - ld a, (de) - or 7 ; Set bits 0,1,2 to enable transparency - ld (de), a - ret - -__SET_INK2: - ; Another entry. This will set the ink color at location pointer by DE - and 7 ; # Gets color mod 8 - ld b, a ; Saves the color - ld a, (de) - and 0F8h ; Clears previous value - or b - ld (de), a - inc de ; Points DE to MASK_T or MASK_P - ld a, (de) - and 0F8h ; Reset bits 0,1,2 sign to disable transparency - ld (de), a ; Store new attr - ret - -; Sets the INK color passed in A register in the ATTR_T variable -INK_TMP: - ld de, ATTR_T - jp __SET_INK - - ENDP - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/inverse.asm b/src/lib/arch/zxnext/runtime/inverse.asm index 974a0c5bc..f9bbedcad 100644 --- a/src/lib/arch/zxnext/runtime/inverse.asm +++ b/src/lib/arch/zxnext/runtime/inverse.asm @@ -1,35 +1,8 @@ -; Sets INVERSE flag in P_FLAG permanently -; Parameter: INVERSE flag in bit 0 of A register - -#include once - - push namespace core - -INVERSE: - PROC - - and 1 ; # Convert to 0/1 - add a, a; # Shift left 3 bits for permanent - add a, a - add a, a - ld hl, P_FLAG - res 3, (hl) - or (hl) - ld (hl), a - ret - -; Sets INVERSE flag in P_FLAG temporarily -INVERSE_TMP: - and 1 - add a, a - add a, a; # Shift left 2 bits for temporary - ld hl, P_FLAG - res 2, (hl) - or (hl) - ld (hl), a - jp __SET_ATTR_MODE - - ENDP - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/io/keyboard/inkey.asm b/src/lib/arch/zxnext/runtime/io/keyboard/inkey.asm index 8ab774f8b..e09b4e8e9 100644 --- a/src/lib/arch/zxnext/runtime/io/keyboard/inkey.asm +++ b/src/lib/arch/zxnext/runtime/io/keyboard/inkey.asm @@ -1,63 +1,8 @@ -; INKEY Function -; Returns a string allocated in dynamic memory -; containing the string. -; An empty string otherwise. - -#include once - - push namespace core - -INKEY: - PROC - LOCAL __EMPTY_INKEY - LOCAL KEY_SCAN - LOCAL KEY_TEST - LOCAL KEY_CODE - - ld bc, 3 ; 1 char length string - call __MEM_ALLOC - - ld a, h - or l - ret z ; Return if NULL (No memory) - - push hl ; Saves memory pointer - - call KEY_SCAN - jp nz, __EMPTY_INKEY - - call KEY_TEST - jp nc, __EMPTY_INKEY - - dec d ; D is expected to be FLAGS so set bit 3 $FF - ; 'L' Mode so no keywords. - ld e, a ; main key to A - ; C is MODE 0 'KLC' from above still. - call KEY_CODE ; routine K-DECODE - pop hl - - ld (hl), 1 - inc hl - ld (hl), 0 - inc hl - ld (hl), a - dec hl - dec hl ; HL Points to string result - ret - -__EMPTY_INKEY: - pop hl - xor a - ld (hl), a - inc hl - ld (hl), a - dec hl - ret - -KEY_SCAN EQU 028Eh -KEY_TEST EQU 031Eh -KEY_CODE EQU 0333h - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/io/sound/beep.asm b/src/lib/arch/zxnext/runtime/io/sound/beep.asm index 71124c1ed..c58f37a15 100644 --- a/src/lib/arch/zxnext/runtime/io/sound/beep.asm +++ b/src/lib/arch/zxnext/runtime/io/sound/beep.asm @@ -1,24 +1,8 @@ -#include once - - push namespace core - -BEEP: ; The beep command, as in BASIC - ; Duration in C,ED,LH (float) - ; Pitch in top of the stack - - CALL __FPSTACK_PUSH - - pop hl ; RET address - pop af - pop de - pop bc ; Recovers PITCH from the stack - push hl ; CALLEE, now ret addr in top of the stack - - CALL __FPSTACK_PUSH ; Pitch onto the FP stack - - push ix ; BEEP routine modifies IX. We have to preserve it - call 03F8h - pop ix - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/io/sound/beeper.asm b/src/lib/arch/zxnext/runtime/io/sound/beeper.asm index b879ada72..78633e75a 100644 --- a/src/lib/arch/zxnext/runtime/io/sound/beeper.asm +++ b/src/lib/arch/zxnext/runtime/io/sound/beeper.asm @@ -1,20 +1,8 @@ -; vim:ts=4:et:sw=4: -; This is a fast beep routine, but needs parameters -; codified in a different way. -; See http://www.wearmouth.demon.co.uk/zx82.htm#L03F8 - -; Needs pitch on top of the stack -; HL = duration - - push namespace core - -__BEEPER: - ex de, hl - pop hl - ex (sp), hl ; CALLEE - push ix ; BEEPER changes IX - call 03B5h - pop ix - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/istore16.asm b/src/lib/arch/zxnext/runtime/istore16.asm index e2a918c24..8c1916c13 100644 --- a/src/lib/arch/zxnext/runtime/istore16.asm +++ b/src/lib/arch/zxnext/runtime/istore16.asm @@ -1,20 +1,8 @@ - push namespace core - -__PISTORE16: ; stores an integer in hl into address IX + BC; Destroys DE - ex de, hl - push ix - pop hl - add hl, bc - -__ISTORE16: ; Load address at hl, and stores E,D integer at that address - ld a, (hl) - inc hl - ld h, (hl) - ld l, a - ld (hl), e - inc hl - ld (hl), d - ret - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/italic.asm b/src/lib/arch/zxnext/runtime/italic.asm index 5d24dae13..c7398bc84 100644 --- a/src/lib/arch/zxnext/runtime/italic.asm +++ b/src/lib/arch/zxnext/runtime/italic.asm @@ -1,36 +1,8 @@ -; Sets ITALIC flag in P_FLAG permanently -; Parameter: ITALIC flag in bit 0 of A register -#include once - - push namespace core - -ITALIC: - PROC - - and 1 - rrca - rrca - rrca - ld hl, FLAGS2 - res 5, (HL) - or (hl) - ld (hl), a - ret - -; Sets ITALIC flag in P_FLAG temporarily -ITALIC_TMP: - and 1 - rrca - rrca - rrca - rrca - ld hl, FLAGS2 - res 4, (hl) - or (hl) - ld (hl), a - ret - - ENDP - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/lddede.asm b/src/lib/arch/zxnext/runtime/lddede.asm index 4718f2fcf..71a4b7394 100644 --- a/src/lib/arch/zxnext/runtime/lddede.asm +++ b/src/lib/arch/zxnext/runtime/lddede.asm @@ -1,17 +1,8 @@ -; Loads DE into DE -; Modifies C register -; There is a routine similar to this one -; at ROM address L2AEE - push namespace core - -__LOAD_DE_DE: - ex de, hl - ld c, (hl) - inc hl - ld h, (hl) - ld l, c - ex de, hl - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/letsubstr.asm b/src/lib/arch/zxnext/runtime/letsubstr.asm index 4a0a3e7f6..b56b9e6f6 100644 --- a/src/lib/arch/zxnext/runtime/letsubstr.asm +++ b/src/lib/arch/zxnext/runtime/letsubstr.asm @@ -1,157 +1,8 @@ -; Substring assigment eg. LET a$(p0 TO p1) = "xxxx" -; HL = Start of string -; TOP of the stack -> p1 (16 bit, unsigned) -; TOP -1 of the stack -> p0 register -; TOP -2 Flag (popped out in A register) -; A Register => 0 if HL is not freed from memory -; => Not 0 if HL must be freed from memory on exit -; TOP -3 B$ address - -#include once - - push namespace core - -__LETSUBSTR: - PROC - - LOCAL __CONT0 - LOCAL __CONT1 - LOCAL __CONT2 - LOCAL __FREE_STR - - exx - pop hl ; Return address - pop de ; p1 - pop bc ; p0 - exx - - pop af ; Flag - ex af, af' ; Save it for later - - pop de ; B$ - - exx - push hl ; push ret addr back - exx - push de ; B$ addr to be freed upon return (if A != 0) - - ld a, h - or l - jp z, __FREE_STR ; Return if null - - ld c, (hl) - inc hl - ld b, (hl) ; BC = Str length - inc hl ; HL = String start - push bc - - exx - ex de, hl - or a - sbc hl, bc ; HL = Length of string requested by user - inc hl ; len (a$(p0 TO p1)) = p1 - p0 + 1 - ex de, hl ; Saves it in DE - - pop hl ; HL = String length - exx - jp c, __FREE_STR ; Return if p0 > p1 - exx - - or a - sbc hl, bc ; P0 >= String length? - exx - - jp z, __FREE_STR ; Return if equal - jp c, __FREE_STR ; Return if greater - - exx - add hl, bc ; Add it back - - sbc hl, de ; Length of substring > string => Truncate it - add hl, de ; add it back - jr nc, __CONT0 ; Length of substring within a$ - - ld d, h - ld e, l ; Truncate length of substring to fit within the strlen - -__CONT0: ; At this point DE = Length of substring to copy - ; BC = start of char to copy - push de - - push bc - exx - pop bc - - add hl, bc ; Start address (within a$) so copy from b$ (in DE) - - push hl - exx - pop hl ; Start address (within a$) so copy from b$ (in DE) - - ld b, d ; Length of string - ld c, e - - ld (hl), ' ' - ld d, h - ld e, l - inc de - dec bc - ld a, b - or c - jr z, __CONT2 - - ; At this point HL = DE = Start of Write zone in a$ - ; BC = Number of chars to write - - ldir - -__CONT2: - - pop bc ; Recovers Length of string to copy - exx - ex de, hl ; HL = Source, DE = Target - - ld a, h - or l - jp z, __FREE_STR ; Return if B$ is NULL - - ld c, (hl) - inc hl - ld b, (hl) - inc hl - - ld a, b - or c - jp z, __FREE_STR ; Return if len(b$) = 0 - - ; Now if len(b$) < len(char to copy), copy only len(b$) chars - - push de - push hl - push bc - exx - pop hl ; LEN (b$) - or a - sbc hl, bc - add hl, bc - jr nc, __CONT1 - - ; If len(b$) < len(to copy) - ld b, h ; BC = len(to copy) - ld c, l - -__CONT1: - pop hl - pop de - ldir ; Copy b$ into a$(x to y) - -__FREE_STR: - pop hl - ex af, af' - or a ; If not 0, free - jp nz, __MEM_FREE - ret - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/load.asm b/src/lib/arch/zxnext/runtime/load.asm index ce6e97733..84a302f7a 100644 --- a/src/lib/arch/zxnext/runtime/load.asm +++ b/src/lib/arch/zxnext/runtime/load.asm @@ -1,335 +1,8 @@ -#include once - -#ifndef HIDE_LOAD_MSG -# include once -#endif - - push namespace core - -LOAD_CODE: -; This function will implement the LOAD CODE Routine -; Parameters in the stack are HL => String with LOAD name -; (only first 12 chars will be taken into account) -; DE = START address of CODE to save -; BC = Length of data in bytes -; A = 1 => LOAD 0 => Verify - - PROC - - LOCAL LOAD_CONT, LOAD_CONT2, LOAD_CONT3 - LOCAL LD_BYTES - LOCAL LOAD_HEADER - LOCAL LD_LOOK_H - LOCAL HEAD1 - LOCAL TMP_HEADER - LOCAL LD_NAME - LOCAL LD_CH_PR - LOCAL LOAD_END - LOCAL VR_CONTROL, VR_CONT_1, VR_CONT_2 - LOCAL MEM0 - LOCAL TMP_SP - -MEM0 EQU 5C92h ; Temporary memory buffer -HEAD1 EQU MEM0 + 8 ; Uses CALC Mem for temporary storage - ; Must skip first 8 bytes used by - ; PRINT routine -TMP_HEADER EQU HEAD1 + 17 ; Temporary HEADER2 pointer storage -TMP_SP EQU TMP_HEADER + 2 ; Temporary SP storage - -#ifdef __ENABLE_BREAK__ -LD_BYTES EQU 0556h ; ROM Routine LD-BYTES -#endif - -TMP_FLAG EQU 23655 ; Uses BREG as a Temporary FLAG - - pop hl ; Return address - pop af ; A = 1 => LOAD; A = 0 => VERIFY - pop bc ; data length in bytes - pop de ; address start - ex (sp), hl ; CALLE => now hl = String - -__LOAD_CODE: ; INLINE version - push ix ; saves IX - ld (TMP_FLAG), a ; Stores verify/load flag - - ; Prepares temporary 1st header descriptor - ld ix, HEAD1 - ld (ix + 0), 3 ; ZXBASIC ALWAYS uses CODE - ld (ix + 1), 0FFh ; Wildcard for empty string - - ld (ix + 11), c - ld (ix + 12), b ; Store length in bytes - ld (ix + 13), e - ld (ix + 14), d ; Store address in bytes - - push hl ; String ptr to be freed later - - ld a, h - or l - ld b, h - ld c, l - jr z, LOAD_HEADER ; NULL STRING => LOAD "" - - ld c, (hl) - inc hl - ld b, (hl) - inc hl - - ld a, b - or c - jr z, LOAD_CONT2 ; NULL STRING => LOAD "" - - ; Fill with blanks - push hl - push bc - ld hl, HEAD1 + 2 - ld de, HEAD1 + 3 - ld bc, 8 - ld (hl), ' ' - ldir - pop bc - pop hl - -LOAD_HEADER: - ex de, hl ; Saves HL in DE - ld hl, 10 - or a - sbc hl, bc ; Test BC > 10? - ex de, hl ; Retrieve HL - jr nc, LOAD_CONT ; Ok BC <= 10 - ld bc, 10 ; BC at most 10 chars - -LOAD_CONT: - ld de, HEAD1 + 1 - ldir ; Copy String block NAME in header - -LOAD_CONT2: - pop hl ; String ptr - call MEM_FREE - - ld hl, 0 - add hl, sp - ld (TMP_SP), hl - ld bc, -18 - add hl, bc - ld sp, hl - -LOAD_CONT3: - ld (TMP_HEADER), hl - push hl - pop ix - -;; LD-LOOK-H --- RIPPED FROM ROM at 0x767 -LD_LOOK_H: - push ix ; save IX - ld de, 17 ; seventeen bytes - xor a ; reset zero flag - scf ; set carry flag - - call LD_BYTES ; routine LD-BYTES loads a header from tape - ; to second descriptor. - pop ix ; restore IX - jr nc, LD_LOOK_H ; loop back to LD-LOOK-H until header found. - - ld c, 80h ; C has bit 7 set to indicate header type mismatch as - ; a default startpoint. - - ld a, (ix + 0) ; compare loaded type - cp 3 ; with expected bytes header - jr nz, LD_TYPE ; forward to LD-TYPE with mis-match. - - ld c, -10 ; set C to minus ten - will count characters - ; up to zero. -LD_TYPE: - cp 4 ; check if type in acceptable range 0 - 3. - jr nc, LD_LOOK_H ; back to LD-LOOK-H with 4 and over. - ; else A indicates type 0-3. -#ifndef HIDE_LOAD_MSG - call PRINT_TAPE_MESSAGES; Print tape msg -#endif - - ld hl, HEAD1 + 1 ; point HL to 1st descriptor. - ld de, (TMP_HEADER) ; point DE to 2nd descriptor. - ld b, 10 ; the count will be ten characters for the - ; filename. - - ld a, (hl) ; fetch first character and test for - inc a ; value 255. - jr nz, LD_NAME ; forward to LD-NAME if not the wildcard. - -; but if it is the wildcard, then add ten to C which is minus ten for a type -; match or -128 for a type mismatch. Although characters have to be counted -; bit 7 of C will not alter from state set here. - - ld a, c ; transfer $F6 or $80 to A - add a, b ; add 10 - ld c, a ; place result, zero or -118, in C. - -; At this point we have either a type mismatch, a wildcard match or ten -; characters to be counted. The characters must be shown on the screen. - -;; LD-NAME -LD_NAME: - inc de ; address next input character - ld a, (de) ; fetch character - cp (hl) ; compare to expected - inc hl ; address next expected character - jr nz, LD_CH_PR ; forward to LD-CH-PR with mismatch - - inc c ; increment matched character count - -;; LD-CH-PR -LD_CH_PR: -#ifndef HIDE_LOAD_MSG - call __PRINTCHAR ; PRINT-A prints character -#endif - djnz LD_NAME ; loop back to LD-NAME for ten characters. - - bit 7, c ; test if all matched - jr nz, LD_LOOK_H ; back to LD-LOOK-H if not - -; else print a terminal carriage return. - -#ifndef HIDE_LOAD_MSG - ld a, 0Dh ; prepare carriage return. - call __PRINTCHAR ; PRINT-A outputs it. -#endif - - ld a, (HEAD1) - cp 03 ; Only "bytes:" header is used un ZX BASIC - jr nz, LD_LOOK_H - - ; Ok, ready to check for bytes start and end - -VR_CONTROL: - ld e, (ix + 11) ; fetch length of new data - ld d, (ix + 12) ; to DE. - - ld hl, HEAD1 + 11 - ld a, (hl) ; fetch length of old data (orig. header) - inc hl - ld h, (hl) ; to HL - ld l, a - or h ; check length of old for zero. (Carry reset) - jr z, VR_CONT_1 ; forward to VR-CONT-1 if length unspecified - ; e.g. LOAD "x" CODE - sbc hl, de - jr nz, LOAD_ERROR ; Lengths don't match - -VR_CONT_1: - ld hl, HEAD1 + 13 ; fetch start of old data (orig. header) - ld a, (hl) - inc hl - ld h, (hl) - ld l, a - or h ; check start for zero (unspecified) - jr nz, VR_CONT_2 ; Jump if there was a start - - ld l, (ix + 13) ; otherwise use destination in header - ld h, (ix + 14) ; and load code at addr. saved from - -VR_CONT_2: - push hl - pop ix ; Transfer load addr to IX - - ld a, (TMP_FLAG) ; load verify/load flag - sra a ; shift bit 0 to Carry (1 => Load, 0 = Verify), A = 0 - dec a ; a = 0xFF (Data) - call LD_BYTES - jr c, LOAD_END ; if carry, load/verification was ok - -LOAD_ERROR: - ; Sets ERR_NR with Tape Loading, and returns - ld a, ERROR_TapeLoadingErr - ld (ERR_NR), a - -LOAD_END: - ld hl, (TMP_SP) - ld sp, hl ; Recovers stack - pop ix ; Recovers stack frame pointer - ret - -#ifndef __ENABLE_BREAK__ - LOCAL LD_BYTES_RET - LOCAL LD_BYTES_ROM - LOCAL LD_BYTES_NOINTER - -LD_BYTES_ROM EQU 0562h - -LD_BYTES: - - inc d - ex af, af' - dec d - ld a, r - push af - di - call 0562h - -LD_BYTES_RET: - ; Restores DI / EI state - ex af, af' - pop af - jp po, LD_BYTES_NOINTER - ei - -LD_BYTES_NOINTER: - ex af, af' - ret -#endif - ENDP - - -#ifndef HIDE_LOAD_MSG -PRINT_TAPE_MESSAGES: - - PROC - - LOCAL LOOK_NEXT_TAPE_MSG - LOCAL PRINT_TAPE_MSG - - ; Print tape messages according to A value - ; Each message starts with a carriage return and - ; ends with last char having its bit 7 set - - ; A = 0 => '\nProgram: ' - ; A = 1 => '\nNumber array: ' - ; A = 2 => '\nCharacter array: ' - ; A = 3 => '\nBytes: ' - - push bc - - ld hl, 09C0h ; address base of last 4 tape messages - ld b, a - inc b ; avoid 256-loop if b == 0 - ld a, 0Dh ; Msg start mark - - ; skip memory bytes looking for next tape msg entry - ; each msg ends when 0Dh is fond -LOOK_NEXT_TAPE_MSG: - inc hl ; Point to next char - cp (hl) ; Is it 0Dh? - jr nz, LOOK_NEXT_TAPE_MSG - ; Ok next message found - djnz LOOK_NEXT_TAPE_MSG ; Repeat if more msg to skip - -PRINT_TAPE_MSG: - ; Ok. This will print bytes after (HL) - ; until one of them has bit 7 set - ld a, (hl) - and 7Fh ; Clear bit 7 of A - call __PRINTCHAR - - ld a, (hl) - inc hl - add a, a ; Carry if A >= 128 - jr nc, PRINT_TAPE_MSG - - pop bc - ret - - ENDP - -#endif - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/loadstr.asm b/src/lib/arch/zxnext/runtime/loadstr.asm index ae597951f..c6fd13711 100644 --- a/src/lib/arch/zxnext/runtime/loadstr.asm +++ b/src/lib/arch/zxnext/runtime/loadstr.asm @@ -1,47 +1,8 @@ -#include once - -; Loads a string (ptr) from HL -; and duplicates it on dynamic memory again -; Finally, it returns result pointer in HL - - push namespace core - -__ILOADSTR: ; This is the indirect pointer entry HL = (HL) - ld a, h - or l - ret z - ld a, (hl) - inc hl - ld h, (hl) - ld l, a - -__LOADSTR: ; __FASTCALL__ entry - ld a, h - or l - ret z ; Return if NULL - - ld c, (hl) - inc hl - ld b, (hl) - dec hl ; BC = LEN(a$) - - inc bc - inc bc ; BC = LEN(a$) + 2 (two bytes for length) - - push hl - push bc - call __MEM_ALLOC - pop bc ; Recover length - pop de ; Recover origin - - ld a, h - or l - ret z ; Return if NULL (No memory) - - ex de, hl ; ldir takes HL as source, DE as destiny, so SWAP HL,DE - push de ; Saves destiny start - ldir ; Copies string (length number included) - pop hl ; Recovers destiny in hl as result - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/math/acos.asm b/src/lib/arch/zxnext/runtime/math/acos.asm index 36f7169ad..4d11d63a4 100644 --- a/src/lib/arch/zxnext/runtime/math/acos.asm +++ b/src/lib/arch/zxnext/runtime/math/acos.asm @@ -1,14 +1,8 @@ -#include once - - push namespace core - -ACOS: ; Computes ACOS using ROM FP-CALC - call __FPSTACK_PUSH - - rst 28h ; ROM CALC - defb 23h ; ACOS - defb 38h ; END CALC - - jp __FPSTACK_POP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/math/asin.asm b/src/lib/arch/zxnext/runtime/math/asin.asm index a41b236b7..dd5a4023a 100644 --- a/src/lib/arch/zxnext/runtime/math/asin.asm +++ b/src/lib/arch/zxnext/runtime/math/asin.asm @@ -1,14 +1,8 @@ -#include once - - push namespace core - -ASIN: ; Computes ASIN using ROM FP-CALC - call __FPSTACK_PUSH - - rst 28h ; ROM CALC - defb 22h ; ASIN - defb 38h ; END CALC - - jp __FPSTACK_POP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/math/atan.asm b/src/lib/arch/zxnext/runtime/math/atan.asm index c73b56939..d3f9a3daf 100644 --- a/src/lib/arch/zxnext/runtime/math/atan.asm +++ b/src/lib/arch/zxnext/runtime/math/atan.asm @@ -1,14 +1,8 @@ -#include once - - push namespace core - -ATAN: ; Computes ATAN using ROM FP-CALC - call __FPSTACK_PUSH - - rst 28h ; ROM CALC - defb 24h ; ATAN - defb 38h ; END CALC - - jp __FPSTACK_POP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/math/cos.asm b/src/lib/arch/zxnext/runtime/math/cos.asm index 4a62f1947..37d8d0248 100644 --- a/src/lib/arch/zxnext/runtime/math/cos.asm +++ b/src/lib/arch/zxnext/runtime/math/cos.asm @@ -1,14 +1,8 @@ -#include once - - push namespace core - -COS: ; Computes COS using ROM FP-CALC - call __FPSTACK_PUSH - - rst 28h ; ROM CALC - defb 20h ; COS - defb 38h ; END CALC - - jp __FPSTACK_POP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/math/exp.asm b/src/lib/arch/zxnext/runtime/math/exp.asm index 052b38984..7a75bc8d0 100644 --- a/src/lib/arch/zxnext/runtime/math/exp.asm +++ b/src/lib/arch/zxnext/runtime/math/exp.asm @@ -1,14 +1,8 @@ -#include once - - push namespace core - -EXP: ; Computes e^n using ROM FP-CALC - call __FPSTACK_PUSH - - rst 28h ; ROM CALC - defb 26h ; E^n - defb 38h ; END CALC - - jp __FPSTACK_POP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/math/logn.asm b/src/lib/arch/zxnext/runtime/math/logn.asm index 1f12fa2e9..6a2b33a54 100644 --- a/src/lib/arch/zxnext/runtime/math/logn.asm +++ b/src/lib/arch/zxnext/runtime/math/logn.asm @@ -1,14 +1,8 @@ -#include once - - push namespace core - -LN: ; Computes Ln(x) using ROM FP-CALC - call __FPSTACK_PUSH - - rst 28h ; ROM CALC - defb 25h - defb 38h ; END CALC - - jp __FPSTACK_POP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/math/pow.asm b/src/lib/arch/zxnext/runtime/math/pow.asm index f6119c66d..8ec3bd2ac 100644 --- a/src/lib/arch/zxnext/runtime/math/pow.asm +++ b/src/lib/arch/zxnext/runtime/math/pow.asm @@ -1,34 +1,8 @@ -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) - -; All of them uses A EDCB registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order A DE BC. -; -; Uses CALLEE convention +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Operands comes swapped: -; 1 st parameter is the BASE (A ED CB) -; 2 nd parameter (Top of the stack) is Exponent -; ------------------------------------------------------------- - - push namespace core - -__POW: ; Exponentiation - PROC - - call __FPSTACK_PUSH2 - - ; ------------- ROM POW - rst 28h - defb 01h ; Exchange => 1, Base - defb 06h ; POW - defb 38h; ; END CALC - - jp __FPSTACK_POP - - ENDP +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/math/sin.asm b/src/lib/arch/zxnext/runtime/math/sin.asm index 59ed05661..1e5d90661 100644 --- a/src/lib/arch/zxnext/runtime/math/sin.asm +++ b/src/lib/arch/zxnext/runtime/math/sin.asm @@ -1,14 +1,8 @@ -#include once - - push namespace core - -SIN: ; Computes SIN using ROM FP-CALC - call __FPSTACK_PUSH - - rst 28h ; ROM CALC - defb 1Fh - defb 38h ; END CALC - - jp __FPSTACK_POP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/math/sqrt.asm b/src/lib/arch/zxnext/runtime/math/sqrt.asm index 4ddbc1b54..84dd2bb7b 100644 --- a/src/lib/arch/zxnext/runtime/math/sqrt.asm +++ b/src/lib/arch/zxnext/runtime/math/sqrt.asm @@ -1,14 +1,8 @@ -#include once - - push namespace core - -SQRT: ; Computes SQRT(x) using ROM FP-CALC - call __FPSTACK_PUSH - - rst 28h ; ROM CALC - defb 28h ; SQRT - defb 38h ; END CALC - - jp __FPSTACK_POP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/math/tan.asm b/src/lib/arch/zxnext/runtime/math/tan.asm index 4da8c9414..68d4860f8 100644 --- a/src/lib/arch/zxnext/runtime/math/tan.asm +++ b/src/lib/arch/zxnext/runtime/math/tan.asm @@ -1,14 +1,8 @@ -#include once - - push namespace core - -TAN: ; Computes TAN using ROM FP-CALC - call __FPSTACK_PUSH - - rst 28h ; ROM CALC - defb 21h ; TAN - defb 38h ; END CALC - - jp __FPSTACK_POP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/mem/alloc.asm b/src/lib/arch/zxnext/runtime/mem/alloc.asm index 2769745ab..c7669bc01 100644 --- a/src/lib/arch/zxnext/runtime/mem/alloc.asm +++ b/src/lib/arch/zxnext/runtime/mem/alloc.asm @@ -9,181 +9,4 @@ ; ; Please read the MIT license on the internet -; ----- IMPLEMENTATION NOTES ------ -; The heap is implemented as a linked list of free blocks. - -; Each free block contains this info: -; -; +----------------+ <-- HEAP START -; | Size (2 bytes) | -; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ <-+ -; | Size (2 bytes) | -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ | -; | | | <-- If Size > 4, then this contains (size - 4) bytes -; | (0 if Size = 4)| | -; +----------------+ <-+ -; | Size (2 bytes) | -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ | -; | | | -; | (0 if Size = 4)| | -; +----------------+ | -; | <-- This zone is in use (Already allocated) -; +----------------+ <-+ -; | Size (2 bytes) | -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ | -; | | | -; | (0 if Size = 4)| | -; +----------------+ <-+ -; | Next (2 bytes) |--> NULL => END OF LIST -; | 0 = NULL | -; +----------------+ -; | | -; | (0 if Size = 4)| -; +----------------+ - - -; When a block is FREED, the previous and next pointers are examined to see -; if we can defragment the heap. If the block to be freed is just next to the -; previous, or to the next (or both) they will be converted into a single -; block (so defragmented). - - -; MEMORY MANAGER -; -; This library must be initialized calling __MEM_INIT with -; HL = BLOCK Start & DE = Length. - -; An init directive is useful for initialization routines. -; They will be added automatically if needed. - -#include once -#include once - - -; --------------------------------------------------------------------- -; MEM_ALLOC -; Allocates a block of memory in the heap. -; -; Parameters -; BC = Length of requested memory block -; -; Returns: -; HL = Pointer to the allocated block in memory. Returns 0 (NULL) -; if the block could not be allocated (out of memory) -; --------------------------------------------------------------------- - - push namespace core - -MEM_ALLOC: -__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) - PROC - - LOCAL __MEM_LOOP - LOCAL __MEM_DONE - LOCAL __MEM_SUBTRACT - LOCAL __MEM_START - LOCAL TEMP, TEMP0 - -TEMP EQU TEMP0 + 1 - - ld hl, 0 - ld (TEMP), hl - -__MEM_START: - ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start - inc bc - inc bc ; BC = BC + 2 ; block size needs 2 extra bytes for hidden pointer - -__MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE - ld a, h ; HL = NULL (No memory available?) - or l -#ifdef __MEMORY_CHECK__ - ld a, ERROR_OutOfMemory - jp z, __ERROR -#else - ret z ; NULL -#endif - ; HL = Pointer to Free block - ld e, (hl) - inc hl - ld d, (hl) - inc hl ; DE = Block Length - - push hl ; HL = *pointer to -> next block - ex de, hl - or a ; CF = 0 - sbc hl, bc ; FREE >= BC (Length) (HL = BlockLength - Length) - jp nc, __MEM_DONE - pop hl - ld (TEMP), hl - - ex de, hl - ld e, (hl) - inc hl - ld d, (hl) - ex de, hl - jp __MEM_LOOP - -__MEM_DONE: ; A free block has been found. - ; Check if at least 4 bytes remains free (HL >= 4) - push hl - exx ; exx to preserve bc - pop hl - ld bc, 4 - or a - sbc hl, bc - exx - jp nc, __MEM_SUBTRACT - ; At this point... - ; less than 4 bytes remains free. So we return this block entirely - ; We must link the previous block with the next to this one - ; (DE) => Pointer to next block - ; (TEMP) => &(previous->next) - pop hl ; Discard current block pointer - push de - ex de, hl ; DE = Previous block pointer; (HL) = Next block pointer - ld a, (hl) - inc hl - ld h, (hl) - ld l, a ; HL = (HL) - ex de, hl ; HL = Previous block pointer; DE = Next block pointer -TEMP0: - ld hl, 0 ; Pre-previous block pointer - - ld (hl), e - inc hl - ld (hl), d ; LINKED - pop hl ; Returning block. - - ret - -__MEM_SUBTRACT: - ; At this point we have to store HL value (Length - BC) into (DE - 2) - ex de, hl - dec hl - ld (hl), d - dec hl - ld (hl), e ; Store new block length - - add hl, de ; New length + DE => free-block start - pop de ; Remove previous HL off the stack - - ld (hl), c ; Store length on its 1st word - inc hl - ld (hl), b - inc hl ; Return hl - ret - - ENDP - - pop namespace - +#include once [arch:zx48k] \ No newline at end of file diff --git a/src/lib/arch/zxnext/runtime/mem/calloc.asm b/src/lib/arch/zxnext/runtime/mem/calloc.asm index fbda2cb17..428d3a41b 100644 --- a/src/lib/arch/zxnext/runtime/mem/calloc.asm +++ b/src/lib/arch/zxnext/runtime/mem/calloc.asm @@ -1,49 +1,8 @@ -; vim: ts=4:et:sw=4: -; Copyleft (K) by Jose M. Rodriguez de la Rosa -; (a.k.a. Boriel) -; http://www.boriel.com +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; This ASM library is licensed under the MIT license -; you can use it for any purpose (even for commercial -; closed source programs). -; -; Please read the MIT license on the internet - -#include once - - -; --------------------------------------------------------------------- -; MEM_CALLOC -; Allocates a block of memory in the heap, and clears it filling it -; with 0 bytes -; -; Parameters -; BC = Length of requested memory block -; -; Returns: -; HL = Pointer to the allocated block in memory. Returns 0 (NULL) -; if the block could not be allocated (out of memory) -; --------------------------------------------------------------------- - push namespace core - -__MEM_CALLOC: - push bc - call __MEM_ALLOC - pop bc - ld a, h - or l - ret z ; No memory - ld (hl), 0 - dec bc - ld a, b - or c - ret z ; Already filled (1 byte-length block) - ld d, h - ld e, l - inc de - push hl - ldir - pop hl - ret +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/mem/free.asm b/src/lib/arch/zxnext/runtime/mem/free.asm index 171cbfa98..db5840847 100644 --- a/src/lib/arch/zxnext/runtime/mem/free.asm +++ b/src/lib/arch/zxnext/runtime/mem/free.asm @@ -1,193 +1,8 @@ -; vim: ts=4:et:sw=4: -; Copyleft (K) by Jose M. Rodriguez de la Rosa -; (a.k.a. Boriel) -; http://www.boriel.com +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; This ASM library is licensed under the BSD license -; you can use it for any purpose (even for commercial -; closed source programs). -; -; Please read the BSD license on the internet - -; ----- IMPLEMENTATION NOTES ------ -; The heap is implemented as a linked list of free blocks. - -; Each free block contains this info: -; -; +----------------+ <-- HEAP START -; | Size (2 bytes) | -; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ <-+ -; | Size (2 bytes) | -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ | -; | | | <-- If Size > 4, then this contains (size - 4) bytes -; | (0 if Size = 4)| | -; +----------------+ <-+ -; | Size (2 bytes) | -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ | -; | | | -; | (0 if Size = 4)| | -; +----------------+ | -; | <-- This zone is in use (Already allocated) -; +----------------+ <-+ -; | Size (2 bytes) | -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ | -; | | | -; | (0 if Size = 4)| | -; +----------------+ <-+ -; | Next (2 bytes) |--> NULL => END OF LIST -; | 0 = NULL | -; +----------------+ -; | | -; | (0 if Size = 4)| -; +----------------+ - - -; When a block is FREED, the previous and next pointers are examined to see -; if we can defragment the heap. If the block to be breed is just next to the -; previous, or to the next (or both) they will be converted into a single -; block (so defragmented). - - -; MEMORY MANAGER -; -; This library must be initialized calling __MEM_INIT with -; HL = BLOCK Start & DE = Length. - -; An init directive is useful for initialization routines. -; They will be added automatically if needed. - -#include once - -; --------------------------------------------------------------------- -; MEM_FREE -; Frees a block of memory -; -; Parameters: -; HL = Pointer to the block to be freed. If HL is NULL (0) nothing -; is done -; --------------------------------------------------------------------- - - push namespace core - -MEM_FREE: -__MEM_FREE: ; Frees the block pointed by HL - ; HL DE BC & AF modified - PROC - - LOCAL __MEM_LOOP2 - LOCAL __MEM_LINK_PREV - LOCAL __MEM_JOIN_TEST - LOCAL __MEM_BLOCK_JOIN - - ld a, h - or l - ret z ; Return if NULL pointer - - dec hl - dec hl - ld b, h - ld c, l ; BC = Block pointer - - ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start - -__MEM_LOOP2: - inc hl - inc hl ; Next block ptr - - ld e, (hl) - inc hl - ld d, (hl) ; Block next ptr - ex de, hl ; DE = &(block->next); HL = block->next - - ld a, h ; HL == NULL? - or l - jp z, __MEM_LINK_PREV; if so, link with previous - - or a ; Clear carry flag - sbc hl, bc ; Carry if BC > HL => This block if before - add hl, bc ; Restores HL, preserving Carry flag - jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block - -;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next - -__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL - ex de, hl - push hl - dec hl - - ld (hl), c - inc hl - ld (hl), b ; (DE) <- BC - - ld h, b ; HL <- BC (Free block ptr) - ld l, c - inc hl ; Skip block length (2 bytes) - inc hl - ld (hl), e ; Block->next = DE - inc hl - ld (hl), d - ; --- LINKED ; HL = &(BC->next) + 2 - - call __MEM_JOIN_TEST - pop hl - -__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them - ; hl = Ptr to current block + 2 - ld d, (hl) - dec hl - ld e, (hl) - dec hl - ld b, (hl) ; Loads block length into BC - dec hl - ld c, (hl) ; - - push hl ; Saves it for later - add hl, bc ; Adds its length. If HL == DE now, it must be joined - or a - sbc hl, de ; If Z, then HL == DE => We must join - pop hl - ret nz - -__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC - push hl ; Saves it for later - ex de, hl - - ld e, (hl) ; DE -> block->next->length - inc hl - ld d, (hl) - inc hl - - ex de, hl ; DE = &(block->next) - add hl, bc ; HL = Total Length - - ld b, h - ld c, l ; BC = Total Length - - ex de, hl - ld e, (hl) - inc hl - ld d, (hl) ; DE = block->next - - pop hl ; Recovers Pointer to block - ld (hl), c - inc hl - ld (hl), b ; Length Saved - inc hl - ld (hl), e - inc hl - ld (hl), d ; Next saved - ret - - ENDP - - pop namespace +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/mem/heapinit.asm b/src/lib/arch/zxnext/runtime/mem/heapinit.asm index 7c09a1129..00e039d77 100644 --- a/src/lib/arch/zxnext/runtime/mem/heapinit.asm +++ b/src/lib/arch/zxnext/runtime/mem/heapinit.asm @@ -1,129 +1,8 @@ -; vim: ts=4:et:sw=4: -; Copyleft (K) by Jose M. Rodriguez de la Rosa -; (a.k.a. Boriel) -; http://www.boriel.com +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; This ASM library is licensed under the BSD license -; you can use it for any purpose (even for commercial -; closed source programs). -; -; Please read the BSD license on the internet - -; ----- IMPLEMENTATION NOTES ------ -; The heap is implemented as a linked list of free blocks. - -; Each free block contains this info: -; -; +----------------+ <-- HEAP START -; | Size (2 bytes) | -; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ <-+ -; | Size (2 bytes) | -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ | -; | | | <-- If Size > 4, then this contains (size - 4) bytes -; | (0 if Size = 4)| | -; +----------------+ <-+ -; | Size (2 bytes) | -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ | -; | | | -; | (0 if Size = 4)| | -; +----------------+ | -; | <-- This zone is in use (Already allocated) -; +----------------+ <-+ -; | Size (2 bytes) | -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ | -; | | | -; | (0 if Size = 4)| | -; +----------------+ <-+ -; | Next (2 bytes) |--> NULL => END OF LIST -; | 0 = NULL | -; +----------------+ -; | | -; | (0 if Size = 4)| -; +----------------+ - - -; When a block is FREED, the previous and next pointers are examined to see -; if we can defragment the heap. If the block to be breed is just next to the -; previous, or to the next (or both) they will be converted into a single -; block (so defragmented). - - -; MEMORY MANAGER -; -; This library must be initialized calling __MEM_INIT with -; HL = BLOCK Start & DE = Length. - -; An init directive is useful for initialization routines. -; They will be added automatically if needed. - -#init ".core.__MEM_INIT" - - -; --------------------------------------------------------------------- -; __MEM_INIT must be called to initalize this library with the -; standard parameters -; --------------------------------------------------------------------- - push namespace core - -__MEM_INIT: ; Initializes the library using (RAMTOP) as start, and - ld hl, ZXBASIC_MEM_HEAP ; Change this with other address of heap start - ld de, ZXBASIC_HEAP_SIZE ; Change this with your size - -; --------------------------------------------------------------------- -; __MEM_INIT2 initalizes this library -; Parameters: -; HL : Memory address of 1st byte of the memory heap -; DE : Length in bytes of the Memory Heap -; --------------------------------------------------------------------- -__MEM_INIT2: - ; HL as TOP - PROC - - dec de - dec de - dec de - dec de ; DE = length - 4; HL = start - ; This is done, because we require 4 bytes for the empty dummy-header block - - xor a - ld (hl), a - inc hl - ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 - inc hl - - ld b, h - ld c, l - inc bc - inc bc ; BC = starts of next block - - ld (hl), c - inc hl - ld (hl), b - inc hl ; Pointer to next block - - ld (hl), e - inc hl - ld (hl), d - inc hl ; Block size (should be length - 4 at start); This block contains all the available memory - - ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) - inc hl - ld (hl), a - - ld a, 201 - ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again - ret - - ENDP - - pop namespace +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/mem/memcopy.asm b/src/lib/arch/zxnext/runtime/mem/memcopy.asm index ab4816ea0..1cf2732b8 100644 --- a/src/lib/arch/zxnext/runtime/mem/memcopy.asm +++ b/src/lib/arch/zxnext/runtime/mem/memcopy.asm @@ -2,52 +2,7 @@ ; This file is released under the MIT License ; ; Copyleft (k) 2008 -; by Jose Rodriguez-Rosa (a.k.a. Boriel) -; -; Use this file as a template to develop your own library file +; by Jose Rodriguez-Rosa (a.k.a. Boriel) ; ---------------------------------------------------------------- -; Emulates both memmove and memcpy C routines -; Block will be safely copied if they overlap - -; HL => Start of source block -; DE => Start of destiny block -; BC => Block length - - push namespace core - -__MEMCPY: - - PROC - LOCAL __MEMCPY2 - - push hl - add hl, bc ; addr of last source block byte + 1 - or a - sbc hl, de ; checks if DE > HL + BC - pop hl ; recovers HL. If carry => DE > HL + BC (no overlap) - jr c, __MEMCPY2 - - ; Now checks if DE <= HL - - sbc hl, de ; Even if overlap, if DE < HL then we can LDIR safely - add hl, de - jr nc, __MEMCPY2 - - dec bc - add hl, bc - ex de, hl - add hl, bc - ex de, hl - inc bc ; HL and DE point to the last byte position - - lddr ; Copies from end to beginning - ret - -__MEMCPY2: - ldir - ret - - ENDP - - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/mem/realloc.asm b/src/lib/arch/zxnext/runtime/mem/realloc.asm index 01e832fdb..23786a98d 100644 --- a/src/lib/arch/zxnext/runtime/mem/realloc.asm +++ b/src/lib/arch/zxnext/runtime/mem/realloc.asm @@ -1,160 +1,8 @@ -; vim: ts=4:et:sw=4: -; Copyleft (K) by Jose M. Rodriguez de la Rosa -; (a.k.a. Boriel) -; http://www.boriel.com +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; This ASM library is licensed under the BSD license -; you can use it for any purpose (even for commercial -; closed source programs). -; -; Please read the BSD license on the internet - -; ----- IMPLEMENTATION NOTES ------ -; The heap is implemented as a linked list of free blocks. - -; Each free block contains this info: -; -; +----------------+ <-- HEAP START -; | Size (2 bytes) | -; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ <-+ -; | Size (2 bytes) | -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ | -; | | | <-- If Size > 4, then this contains (size - 4) bytes -; | (0 if Size = 4)| | -; +----------------+ <-+ -; | Size (2 bytes) | -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ | -; | | | -; | (0 if Size = 4)| | -; +----------------+ | -; | <-- This zone is in use (Already allocated) -; +----------------+ <-+ -; | Size (2 bytes) | -; +----------------+ -; | Next (2 bytes) |---+ -; +----------------+ | -; | | | -; | (0 if Size = 4)| | -; +----------------+ <-+ -; | Next (2 bytes) |--> NULL => END OF LIST -; | 0 = NULL | -; +----------------+ -; | | -; | (0 if Size = 4)| -; +----------------+ - - -; When a block is FREED, the previous and next pointers are examined to see -; if we can defragment the heap. If the block to be breed is just next to the -; previous, or to the next (or both) they will be converted into a single -; block (so defragmented). - - -; MEMORY MANAGER -; -; This library must be initialized calling __MEM_INIT with -; HL = BLOCK Start & DE = Length. - -; An init directive is useful for initialization routines. -; They will be added automatically if needed. - - -#include once -#include once -#include once - - -; --------------------------------------------------------------------- -; MEM_REALLOC -; Reallocates a block of memory in the heap. -; -; Parameters -; HL = Pointer to the original block -; BC = New Length of requested memory block -; -; Returns: -; HL = Pointer to the allocated block in memory. Returns 0 (NULL) -; if the block could not be allocated (out of memory) -; -; Notes: -; If BC = 0, the block is freed, otherwise -; the content of the original block is copied to the new one, and -; the new size is adjusted. If BC < original length, the content -; will be truncated. Otherwise, extra block content might contain -; memory garbage. -; -; --------------------------------------------------------------------- - push namespace core - -__REALLOC: ; Reallocates block pointed by HL, with new length BC - PROC - - LOCAL __REALLOC_END - - ld a, h - or l - jp z, __MEM_ALLOC ; If HL == NULL, just do a malloc - - ld e, (hl) - inc hl - ld d, (hl) ; DE = First 2 bytes of HL block - - push hl - exx - pop de - inc de ; DE' <- HL + 2 - exx ; DE' <- HL (Saves current pointer into DE') - - dec hl ; HL = Block start - - push de - push bc - call __MEM_FREE ; Frees current block - pop bc - push bc - call __MEM_ALLOC ; Gets a new block of length BC - pop bc - pop de - - ld a, h - or l - ret z ; Return if HL == NULL (No memory) - - ld (hl), e - inc hl - ld (hl), d - inc hl ; Recovers first 2 bytes in HL - - dec bc - dec bc ; BC = BC - 2 (Two bytes copied) - - ld a, b - or c - jp z, __REALLOC_END ; Ret if nothing to copy (BC == 0) - - exx - push de - exx - pop de ; DE <- DE' ; Start of remaining block - - push hl ; Saves current Block + 2 start - ex de, hl ; Exchanges them: DE is destiny block - ldir ; Copies BC Bytes - pop hl ; Recovers Block + 2 start - -__REALLOC_END: - - dec hl ; Set HL - dec hl ; To begin of block - ret - - ENDP +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/neg16.asm b/src/lib/arch/zxnext/runtime/neg16.asm index b21f8ea92..5acf5a4f4 100644 --- a/src/lib/arch/zxnext/runtime/neg16.asm +++ b/src/lib/arch/zxnext/runtime/neg16.asm @@ -1,19 +1,8 @@ -; Negates HL value (16 bit) - push namespace core - -__ABS16: - bit 7, h - ret z - -__NEGHL: - ld a, l ; HL = -HL - cpl - ld l, a - ld a, h - cpl - ld h, a - inc hl - ret - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/neg32.asm b/src/lib/arch/zxnext/runtime/neg32.asm index ce7932ee4..216cad001 100644 --- a/src/lib/arch/zxnext/runtime/neg32.asm +++ b/src/lib/arch/zxnext/runtime/neg32.asm @@ -1,34 +1,8 @@ - push namespace core - -__ABS32: - bit 7, d - ret z - -__NEG32: ; Negates DEHL (Two's complement) - ld a, l - cpl - ld l, a - - ld a, h - cpl - ld h, a - - ld a, e - cpl - ld e, a - - ld a, d - cpl - ld d, a - - inc l - ret nz - - inc h - ret nz - - inc de - ret - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/negf.asm b/src/lib/arch/zxnext/runtime/negf.asm index b74adb6aa..9420c7020 100644 --- a/src/lib/arch/zxnext/runtime/negf.asm +++ b/src/lib/arch/zxnext/runtime/negf.asm @@ -1,29 +1,8 @@ -#include once -#include once -#include once - -; ------------------------------------------------------------- -; Floating point library using the FP ROM Calculator (ZX 48K) - -; All of them uses C EDHL registers as 1st paramter. -; For binary operators, the 2n operator must be pushed into the -; stack, in the order BC DE HL (B not used). +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Uses CALLEE convention -; ------------------------------------------------------------- - - push namespace core - -__NEGF: ; A = -A - call __FPSTACK_PUSH - - ; ------------- ROM NEGATE - rst 28h - defb 1Bh ; NEGF - defb 38h; ; END CALC - - jp __FPSTACK_POP - - pop namespace - +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/ongoto.asm b/src/lib/arch/zxnext/runtime/ongoto.asm index bf71c9c8f..aaf260f8d 100644 --- a/src/lib/arch/zxnext/runtime/ongoto.asm +++ b/src/lib/arch/zxnext/runtime/ongoto.asm @@ -1,35 +1,8 @@ -; ------------------------------------------------------ -; Implements ON .. GOTO -; ------------------------------------------------------ - - push namespace core - -__ON_GOSUB: - pop hl - ex (sp), hl ; hl = beginning of table - call __ON_GOTO_START - ret - -__ON_GOTO: - pop hl - ex (sp), hl ; hl = beginning of table - -__ON_GOTO_START: - ; hl = address of jump table - ; a = index (0..255) - cp (hl) ; length of last post - ret nc ; a >= length of last position (out of range) - inc hl - pop de ; removes ret addr from the stack - ld d, 0 - add a, a - ld e, a - rl d - add hl, de - ld a, (hl) - inc hl - ld h, (hl) - ld l, a - jp (hl) - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/over.asm b/src/lib/arch/zxnext/runtime/over.asm index afe15fc55..7bdb74273 100644 --- a/src/lib/arch/zxnext/runtime/over.asm +++ b/src/lib/arch/zxnext/runtime/over.asm @@ -1,49 +1,8 @@ -; Sets OVER flag in P_FLAG permanently -; Parameter: OVER flag in bit 0 of A register -#include once -#include once - - push namespace core - -OVER: - PROC - - ld c, a ; saves it for later - and 2 - ld hl, FLAGS2 - res 1, (HL) - or (hl) - ld (hl), a - - ld a, c ; Recovers previous value - and 1 ; # Convert to 0/1 - add a, a; # Shift left 1 bit for permanent - - ld hl, P_FLAG - res 1, (hl) - or (hl) - ld (hl), a - ret - -; Sets OVER flag in P_FLAG temporarily -OVER_TMP: - ld c, a ; saves it for later - and 2 ; gets bit 1; clears carry - rra - ld hl, FLAGS2 - res 0, (hl) - or (hl) - ld (hl), a - - ld a, c ; Recovers previous value - and 1 - ld hl, P_FLAG - res 0, (hl) - or (hl) - ld (hl), a - jp __SET_ATTR_MODE - - ENDP - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/paper.asm b/src/lib/arch/zxnext/runtime/paper.asm index 55c9600ba..c8b6cce98 100644 --- a/src/lib/arch/zxnext/runtime/paper.asm +++ b/src/lib/arch/zxnext/runtime/paper.asm @@ -1,50 +1,8 @@ -; Sets paper color in ATTR_P permanently -; Parameter: Paper color in A register - -#include once - - push namespace core - -PAPER: - PROC - LOCAL __SET_PAPER - LOCAL __SET_PAPER2 - - ld de, ATTR_P - -__SET_PAPER: - cp 8 - jr nz, __SET_PAPER2 - inc de - ld a, (de) - or 038h - ld (de), a - ret - - ; Another entry. This will set the paper color at location pointer by DE -__SET_PAPER2: - and 7 ; # Remove - rlca - rlca - rlca ; a *= 8 - - ld b, a ; Saves the color - ld a, (de) - and 0C7h ; Clears previous value - or b - ld (de), a - inc de ; Points to MASK_T or MASK_P accordingly - ld a, (de) - and 0C7h ; Resets bits 3,4,5 - ld (de), a - ret - - -; Sets the PAPER color passed in A register in the ATTR_T variable -PAPER_TMP: - ld de, ATTR_T - jp __SET_PAPER - ENDP - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/pause.asm b/src/lib/arch/zxnext/runtime/pause.asm index b07fb3976..a7624dcb3 100644 --- a/src/lib/arch/zxnext/runtime/pause.asm +++ b/src/lib/arch/zxnext/runtime/pause.asm @@ -1,10 +1,8 @@ -; The PAUSE statement (Calling the ROM) +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - push namespace core - -__PAUSE: - ld b, h - ld c, l - jp 1F3Dh ; PAUSE_1 - - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/pistore32.asm b/src/lib/arch/zxnext/runtime/pistore32.asm index 49418d113..58463ff90 100644 --- a/src/lib/arch/zxnext/runtime/pistore32.asm +++ b/src/lib/arch/zxnext/runtime/pistore32.asm @@ -1,3 +1,8 @@ -#include once +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -; The content of this file has been moved to "store32.asm" +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/ploadf.asm b/src/lib/arch/zxnext/runtime/ploadf.asm index cba7b7b0a..cf46d75dd 100644 --- a/src/lib/arch/zxnext/runtime/ploadf.asm +++ b/src/lib/arch/zxnext/runtime/ploadf.asm @@ -1,17 +1,8 @@ -; Parameter / Local var load -; A => Offset -; IX = Stack Frame -; RESULT: HL => IX + DE - -#include once - - push namespace core - -__PLOADF: - push ix - pop hl - add hl, de - jp __LOADF - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/plot.asm b/src/lib/arch/zxnext/runtime/plot.asm index 869e85e80..4fa0674f8 100644 --- a/src/lib/arch/zxnext/runtime/plot.asm +++ b/src/lib/arch/zxnext/runtime/plot.asm @@ -1,92 +1,8 @@ -; MIXED __FASTCAL__ / __CALLE__ PLOT Function -; Plots a point into the screen calling the ZX ROM PLOT routine - -; Y in A (accumulator) -; X in top of the stack - -#include once -#include once -#include once -#include once - - push namespace core - -PLOT: - PROC - - LOCAL PLOT_SUB - LOCAL PIXEL_ADDR - LOCAL COORDS - LOCAL __PLOT_ERR - LOCAL P_FLAG - LOCAL __PLOT_OVER1 - -P_FLAG EQU 23697 - - pop hl - ex (sp), hl ; Callee - - ld b, a - ld c, h - -#ifdef SCREEN_Y_OFFSET - ld a, SCREEN_Y_OFFSET - add a, b - ld b, a -#endif - -#ifdef SCREEN_X_OFFSET - ld a, SCREEN_X_OFFSET - add a, c - ld c, a -#endif - - ld a, 191 - cp b - jr c, __PLOT_ERR ; jr is faster here (#1) - -__PLOT: ; __FASTCALL__ entry (b, c) = pixel coords (y, x) - ld (COORDS), bc ; Saves current point - ld a, 191 ; Max y coord - call PIXEL_ADDR - res 6, h ; Starts from 0 - ld bc, (SCREEN_ADDR) - add hl, bc ; Now current offset - - ld b, a - inc b - ld a, 0FEh -LOCAL __PLOT_LOOP -__PLOT_LOOP: - rrca - djnz __PLOT_LOOP - - ld b, a - ld a, (P_FLAG) - ld c, a - ld a, (hl) - bit 0, c ; is it OVER 1 - jr nz, __PLOT_OVER1 - and b - -__PLOT_OVER1: - bit 2, c ; is it inverse 1 - jr nz, __PLOT_END - - xor b - cpl - -LOCAL __PLOT_END -__PLOT_END: - ld (hl), a - jp SET_PIXEL_ADDR_ATTR - -__PLOT_ERR: - jp __OUT_OF_SCREEN_ERR ; Spent 3 bytes, but saves 3 T-States at (#1) - -PLOT_SUB EQU 22ECh -PIXEL_ADDR EQU 22ACh -COORDS EQU 5C7Dh - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/print.asm b/src/lib/arch/zxnext/runtime/print.asm index c60ec5d47..0aabb3d55 100644 --- a/src/lib/arch/zxnext/runtime/print.asm +++ b/src/lib/arch/zxnext/runtime/print.asm @@ -1,583 +1,8 @@ -; vim:ts=4:sw=4:et: -; PRINT command routine -; Does not print attribute. Use PRINT_STR or PRINT_NUM for that - -#include once -#include once -#include once -#include once -#include once -#include once -#include once -#include once -#include once -#include once -#include once -#include once -#include once - -; Putting a comment starting with @INIT
-; will make the compiler to add a CALL to
-; It is useful for initialization routines. -#init .core.__PRINT_INIT - - push namespace core - -__PRINT_INIT: ; To be called before program starts (initializes library) - PROC - - ld hl, __PRINT_START - ld (PRINT_JUMP_STATE), hl - - ;; Clears ATTR2 flags (OVER 2, etc) - xor a - ld (FLAGS2), a - ld hl, TV_FLAG - res 0, (hl) - - LOCAL SET_SCR_ADDR - call __LOAD_S_POSN - jp __SET_SCR_PTR - - ;; Receives HL = future value of S_POSN - ;; Stores it at (S_POSN) and refresh screen pointers (ATTR, SCR) -SET_SCR_ADDR: - ld (S_POSN), hl - ex de, hl - ld hl, SCR_SIZE - or a - sbc hl, de - ex de, hl - dec e - jp __SET_SCR_PTR - -__PRINTCHAR: ; Print character store in accumulator (A register) - ; Modifies H'L', B'C', A'F', D'E', A - - LOCAL PO_GR_1 - - LOCAL __PRCHAR - LOCAL __PRINT_JUMP - LOCAL __SRCADDR - LOCAL __PRINT_UDG - LOCAL __PRGRAPH - LOCAL __PRINT_START - -PRINT_JUMP_STATE EQU __PRINT_JUMP + 2 - -__PRINT_JUMP: - exx ; Switch to alternative registers - jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively - -__PRINT_START: - -__PRINT_CHR: - cp ' ' - jr c, __PRINT_SPECIAL ; Characters below ' ' are special ones - ex af, af' ; Saves a value (char to print) for later - - ld hl, (S_POSN) - dec l - jr nz, 1f - ld l, SCR_COLS - 1 - dec h - jr nz, 2f - -#ifndef __ZXB_DISABLE_SCROLL - inc h - push hl - call __SCROLL_SCR - pop hl -#else - ld h, SCR_ROWS - 1 -#endif -2: - call SET_SCR_ADDR - jr 4f -1: - ld (S_POSN), hl -4: - ex af, af' - - cp 80h ; Is it a "normal" (printable) char - jr c, __SRCADDR - - cp 90h ; Is it an UDG? - jr nc, __PRINT_UDG - - ; Print an 8 bit pattern (80h to 8Fh) - - ld b, a - call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 - ld hl, MEM0 - jp __PRGRAPH - -PO_GR_1 EQU 0B38h - -__PRINT_UDG: - sub 90h ; Sub ASC code - ld bc, (UDG) - jr __PRGRAPH0 - -__SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source -__SRCADDR: - ld bc, (CHARS) - -__PRGRAPH0: - add a, a ; A = a * 2 (since a < 80h) ; Thanks to Metalbrain at http://foro.speccy.org - ld l, a - ld h, 0 ; HL = a * 2 (accumulator) - add hl, hl - add hl, hl ; HL = a * 8 - add hl, bc ; HL = CHARS address - -__PRGRAPH: - ex de, hl ; HL = Write Address, DE = CHARS address - -#ifndef __ZXB_DISABLE_BOLD - bit 2, (iy + $47) - call nz, __BOLD -#endif - -#ifndef __ZXB_DISABLE_ITALIC - bit 4, (iy + $47) - call nz, __ITALIC -#endif - - ld hl, (DFCC) - push hl - - ld b, 8 ; 8 bytes per char - -__PRCHAR: - ld a, (de) ; DE *must* be source, and HL destiny - -PRINT_MODE: ; Which operation is used to write on the screen - ; Set it with: - ; LD A, - ; LD (PRINT_MODE), A - ; - ; Available operations: - ; NORMAL : 0h --> NOP ; OVER 0 - ; XOR : AEh --> XOR (HL) ; OVER 1 - ; OR : B6h --> OR (HL) ; PUTSPRITE - ; AND : A6h --> AND (HL) ; PUTMASK - nop ; Set to one of the values above - -INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 - nop ; 2F -> CPL -> INVERSE 1 - - ld (hl), a - - inc de - inc h ; Next line - djnz __PRCHAR - - pop hl - inc hl - ld (DFCC), hl - - ld hl, (DFCCL) ; current ATTR Pos - inc hl - ld (DFCCL), hl - dec hl - call __SET_ATTR - exx - ret - -; ------------- SPECIAL CHARS (< 32) ----------------- - -__PRINT_SPECIAL: ; Jumps here if it is a special char - ld hl, __PRINT_TABLE - jp JUMP_HL_PLUS_2A - -PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence - exx - -__PRINT_0Dh: ; Called WHEN printing CHR$(13) - ld hl, (S_POSN) - dec l - jr nz, 1f - dec h - jr nz, 1f -#ifndef __ZXB_DISABLE_SCROLL - inc h - push hl - call __SCROLL_SCR - pop hl -#else - ld h, SCR_ROWS - 1 -#endif -1: - ld l, 1 - -__PRINT_EOL_END: - call SET_SCR_ADDR - exx - ret - -__PRINT_COM: - exx - push hl - push de - push bc - call PRINT_COMMA - pop bc - pop de - pop hl - ret - -__PRINT_TAB: - ld hl, __PRINT_TAB1 - jr __PRINT_SET_STATE - -__PRINT_TAB1: - ld (MEM0), a - ld hl, __PRINT_TAB2 - jr __PRINT_SET_STATE - -__PRINT_TAB2: - ld a, (MEM0) ; Load tab code (ignore the current one) - ld hl, __PRINT_START - ld (PRINT_JUMP_STATE), hl - exx - push hl - push bc - push de - call PRINT_TAB - pop de - pop bc - pop hl - ret - -__PRINT_AT: - ld hl, __PRINT_AT1 - jr __PRINT_SET_STATE - -__PRINT_NOP: -__PRINT_RESTART: - ld hl, __PRINT_START - -__PRINT_SET_STATE: - ld (PRINT_JUMP_STATE), hl ; Saves next entry call - exx - ret - -__PRINT_AT1: ; Jumps here if waiting for 1st parameter - ld hl, (S_POSN) - ld h, a - ld a, SCR_ROWS - sub h - ld (S_POSN + 1), a - - ld hl, __PRINT_AT2 - jr __PRINT_SET_STATE - -__PRINT_AT2: - call __LOAD_S_POSN - ld e, a - call __SAVE_S_POSN - jr __PRINT_RESTART - -__PRINT_DEL: - call __LOAD_S_POSN ; Gets current screen position - dec e - ld a, -1 - cp e - jr nz, 3f - ld e, SCR_COLS - 2 - dec d - cp d - jr nz, 3f - ld d, SCR_ROWS - 1 -3: - call __SAVE_S_POSN - exx - ret - -__PRINT_INK: - ld hl, __PRINT_INK2 - jr __PRINT_SET_STATE - -__PRINT_INK2: - call INK_TMP - jr __PRINT_RESTART - -__PRINT_PAP: - ld hl, __PRINT_PAP2 - jr __PRINT_SET_STATE - -__PRINT_PAP2: - call PAPER_TMP - jr __PRINT_RESTART - -__PRINT_FLA: - ld hl, __PRINT_FLA2 - jr __PRINT_SET_STATE - -__PRINT_FLA2: - call FLASH_TMP - jr __PRINT_RESTART - -__PRINT_BRI: - ld hl, __PRINT_BRI2 - jr __PRINT_SET_STATE - -__PRINT_BRI2: - call BRIGHT_TMP - jr __PRINT_RESTART - -__PRINT_INV: - ld hl, __PRINT_INV2 - jr __PRINT_SET_STATE - -__PRINT_INV2: - call INVERSE_TMP - jr __PRINT_RESTART - -__PRINT_OVR: - ld hl, __PRINT_OVR2 - jr __PRINT_SET_STATE - -__PRINT_OVR2: - call OVER_TMP - jr __PRINT_RESTART - -#ifndef __ZXB_DISABLE_BOLD -__PRINT_BOLD: - ld hl, __PRINT_BOLD2 - jp __PRINT_SET_STATE - -__PRINT_BOLD2: - call BOLD_TMP - jp __PRINT_RESTART -#endif - -#ifndef __ZXB_DISABLE_ITALIC -__PRINT_ITA: - ld hl, __PRINT_ITA2 - jp __PRINT_SET_STATE - -__PRINT_ITA2: - call ITALIC_TMP - jp __PRINT_RESTART -#endif - -#ifndef __ZXB_DISABLE_BOLD - LOCAL __BOLD - -__BOLD: - push hl - ld hl, MEM0 - ld b, 8 -1: - ld a, (de) - ld c, a - rlca - or c - ld (hl), a - inc hl - inc de - djnz 1b - pop hl - ld de, MEM0 - ret -#endif - -#ifndef __ZXB_DISABLE_ITALIC - LOCAL __ITALIC - -__ITALIC: - push hl - ld hl, MEM0 - ex de, hl - ld bc, 8 - ldir - ld hl, MEM0 - srl (hl) - inc hl - srl (hl) - inc hl - srl (hl) - inc hl - inc hl - inc hl - sla (hl) - inc hl - sla (hl) - inc hl - sla (hl) - pop hl - ld de, MEM0 - ret -#endif - -#ifndef __ZXB_DISABLE_SCROLL - LOCAL __SCROLL_SCR - -# ifdef __ZXB_ENABLE_BUFFER_SCROLL -__SCROLL_SCR: ;; Scrolls screen and attrs 1 row up - ld de, (SCREEN_ADDR) - ld b, 3 -3: - push bc - ld a, 8 -1: - ld hl, 32 - add hl, de - ld bc, 32 * 7 - push de - ldir - pop de - inc d - dec a - jr nz, 1b - push hl - ld bc, -32 - 256 * 7 - add hl, bc - ex de, hl - ld a, 8 -2: - ld bc, 32 - push hl - push de - ldir - pop de - pop hl - inc d - inc h - dec a - jr nz, 2b - pop de - pop bc - djnz 3b - - dec de - ld h, d - ld l, e - ld a, 8 -3: - push hl - push de - ld (hl), b - dec de - ld bc, 31 - lddr - pop de - pop hl - dec d - dec h - dec a - jr nz, 3b - - ld de, (SCREEN_ATTR_ADDR) - ld hl, 32 - add hl, de - ld bc, 32 * 23 - ldir - - ld h, d - ld l, e - ld a, (ATTR_P) - ld (hl), a - inc de - ld bc, 31 - ldir - ret -# else -__SCROLL_SCR EQU 0DFEh ; Use ROM SCROLL -# endif -#endif - - -PRINT_COMMA: - call __LOAD_S_POSN - ld a, e - and 16 - add a, 16 - -PRINT_TAB: - ; Tabulates the number of spaces in A register - ; If the current cursor position is already A, does nothing - PROC - LOCAL LOOP - - call __LOAD_S_POSN ; e = current row - sub e - and 31 - ret z - - ld b, a -LOOP: - ld a, ' ' - call __PRINTCHAR - djnz LOOP - ret - ENDP - -PRINT_AT: ; Changes cursor to ROW, COL - ; COL in A register - ; ROW in stack - - pop hl ; Ret address - ex (sp), hl ; callee H = ROW - ld l, a - ex de, hl - - call __IN_SCREEN - ret nc ; Return if out of screen - jp __SAVE_S_POSN - - LOCAL __PRINT_COM - LOCAL __PRINT_AT1 - LOCAL __PRINT_AT2 - LOCAL __PRINT_BOLD - LOCAL __PRINT_ITA - LOCAL __PRINT_INK - LOCAL __PRINT_PAP - LOCAL __PRINT_SET_STATE - LOCAL __PRINT_TABLE - LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 - -#ifndef __ZXB_DISABLE_ITALIC - LOCAL __PRINT_ITA2 -#else - __PRINT_ITA EQU __PRINT_NOP -#endif - -#ifndef __ZXB_DISABLE_BOLD - LOCAL __PRINT_BOLD2 -#else - __PRINT_BOLD EQU __PRINT_NOP -#endif - -__PRINT_TABLE: ; Jump table for 0 .. 22 codes - - DW __PRINT_NOP ; 0 - DW __PRINT_NOP ; 1 - DW __PRINT_NOP ; 2 - DW __PRINT_NOP ; 3 - DW __PRINT_NOP ; 4 - DW __PRINT_NOP ; 5 - DW __PRINT_COM ; 6 COMMA - DW __PRINT_NOP ; 7 - DW __PRINT_DEL ; 8 DEL - DW __PRINT_NOP ; 9 - DW __PRINT_NOP ; 10 - DW __PRINT_NOP ; 11 - DW __PRINT_NOP ; 12 - DW __PRINT_0Dh ; 13 - DW __PRINT_BOLD ; 14 - DW __PRINT_ITA ; 15 - DW __PRINT_INK ; 16 - DW __PRINT_PAP ; 17 - DW __PRINT_FLA ; 18 - DW __PRINT_BRI ; 19 - DW __PRINT_INV ; 20 - DW __PRINT_OVR ; 21 - DW __PRINT_AT ; 22 AT - DW __PRINT_TAB ; 23 TAB - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/print_eol_attr.asm b/src/lib/arch/zxnext/runtime/print_eol_attr.asm index 6d4e25a2e..9cd40a8d9 100644 --- a/src/lib/arch/zxnext/runtime/print_eol_attr.asm +++ b/src/lib/arch/zxnext/runtime/print_eol_attr.asm @@ -1,13 +1,8 @@ -; Calls PRINT_EOL and then COPY_ATTR, so saves -; 3 bytes - -#include once -#include once - - push namespace core - -PRINT_EOL_ATTR: - call PRINT_EOL - jp COPY_ATTR - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/printf.asm b/src/lib/arch/zxnext/runtime/printf.asm index 73c713f09..2a36acd80 100644 --- a/src/lib/arch/zxnext/runtime/printf.asm +++ b/src/lib/arch/zxnext/runtime/printf.asm @@ -1,47 +1,8 @@ -#include once -#include once -#include once - - push namespace core - -__PRINTF: ; Prints a Fixed point Number stored in C ED LH - PROC - - LOCAL RECLAIM2 - LOCAL STK_END -STK_END EQU 5C65h - - ld hl, (ATTR_T) - push hl ; Saves ATTR_T since BUG ROM changes it - - ld hl, (STK_END) - push hl ; Stores STK_END - - call __FPSTACK_PUSH ; Push number into stack - rst 28h ; # Rom Calculator - defb 2Eh ; # STR$(x) - defb 38h ; # END CALC - call __FPSTACK_POP ; Recovers string parameters to A ED CB - - pop hl - ld (STK_END), hl ; Balance STK_END to avoid STR$ bug - - pop hl - ld (ATTR_T), hl ; Restores ATTR_T - - ex de, hl ; String position now in HL - - push bc - xor a ; Avoid the str to be FREED from heap - call __PRINT_STR - pop bc - inc bc - - jp RECLAIM2 ; Frees TMP Memory - -RECLAIM2 EQU 19E8h - - ENDP - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/printf16.asm b/src/lib/arch/zxnext/runtime/printf16.asm index e5b4e8f2c..47ac20207 100644 --- a/src/lib/arch/zxnext/runtime/printf16.asm +++ b/src/lib/arch/zxnext/runtime/printf16.asm @@ -1,61 +1,8 @@ -#include once -#include once -#include once - - push namespace core - -__PRINTF16: ; Prints a 32bit 16.16 fixed point number - PROC - - LOCAL __PRINT_FIX_LOOP - LOCAL __PRINTF16_2 - - bit 7, d - jr z, __PRINTF16_2 - call __NEG32 - call __PRINT_MINUS - -__PRINTF16_2: - push hl - ex de, hl - call __PRINTU16 ; Prints integer part - pop hl - - ld a, h - or l - ret z ; Returns if integer - - push hl - ld a, '.' - call __PRINT_DIGIT ; Prints decimal point - pop hl - -__PRINT_FIX_LOOP: - ld a, h - or l - ret z ; Returns if no more decimals - - xor a - ld d, h - ld e, l - ; Fast NUM * 10 multiplication - add hl, hl ; - adc a, a ; AHL = AHL * 2 (= X * 2) - add hl, hl ; - adc a, a ; AHL = AHL * 2 (= X * 4) - - add hl, de ; - adc a, 0 ; AHL = AHL + DE (= X * 5) - add hl, hl - adc a, a ; AHL = AHL * 2 (= X * 10) - - push hl - or '0' - call __PRINT_DIGIT - pop hl - jp __PRINT_FIX_LOOP - - ENDP - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/printi16.asm b/src/lib/arch/zxnext/runtime/printi16.asm index d44b22cbf..d884d3031 100644 --- a/src/lib/arch/zxnext/runtime/printi16.asm +++ b/src/lib/arch/zxnext/runtime/printi16.asm @@ -1,42 +1,8 @@ -#include once -#include once -#include once -#include once - - push namespace core - -__PRINTI16: ; Prints a 16bits signed in HL - ; Converts 16 to 32 bits - PROC - - LOCAL __PRINTU_LOOP - ld a, h - or a - - jp p, __PRINTU16 - - call __PRINT_MINUS - call __NEGHL - -__PRINTU16: - - ld b, 0 -__PRINTU_LOOP: - ld a, h - or l - jp z, __PRINTU_START - - push bc - ld de, 10 - call __DIVU16_FAST ; Divides by DE. DE = MODULUS at exit. Since < 256, E = Modulus - pop bc - - ld a, e - or '0' ; Stores ASCII digit (must be print in reversed order) - push af - inc b - jp __PRINTU_LOOP ; Uses JP in loops - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/printi32.asm b/src/lib/arch/zxnext/runtime/printi32.asm index eba8e215a..5fe1e6937 100644 --- a/src/lib/arch/zxnext/runtime/printi32.asm +++ b/src/lib/arch/zxnext/runtime/printi32.asm @@ -1,50 +1,8 @@ -#include once -#include once -#include once -#include once - - - push namespace core - -__PRINTI32: - ld a, d - or a - jp p, __PRINTU32 - - call __PRINT_MINUS - call __NEG32 - -__PRINTU32: - PROC - LOCAL __PRINTU_LOOP - - ld b, 0 ; Counter - -__PRINTU_LOOP: - ld a, h - or l - or d - or e - jp z, __PRINTU_START - - push bc - - ld bc, 0 - push bc - ld bc, 10 - push bc ; Push 00 0A (10 Dec) into the stack = divisor - - call __DIVU32 ; Divides by 32. D'E'H'L' contains modulo (L' since < 10) - pop bc - - exx - ld a, l - or '0' ; Stores ASCII digit (must be print in reversed order) - push af - exx - inc b - jp __PRINTU_LOOP ; Uses JP in loops - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/printi8.asm b/src/lib/arch/zxnext/runtime/printi8.asm index a12ecbfad..b95f74e53 100644 --- a/src/lib/arch/zxnext/runtime/printi8.asm +++ b/src/lib/arch/zxnext/runtime/printi8.asm @@ -1,41 +1,8 @@ -#include once -#include once - - push namespace core - -__PRINTI8: ; Prints an 8 bits number in Accumulator (A) - ; Converts 8 to 32 bits - or a - jp p, __PRINTU8 - - push af - call __PRINT_MINUS - pop af - neg - -__PRINTU8: - PROC - - LOCAL __PRINTU_LOOP - - ld b, 0 ; Counter - -__PRINTU_LOOP: - or a - jp z, __PRINTU_START - - push bc - ld h, 10 - call __DIVU8_FAST ; Divides by 10. D'E'H'L' contains modulo (L' since < 10) - pop bc - - ld a, l - or '0' ; Stores ASCII digit (must be print in reversed order) - push af - ld a, h - inc b - jp __PRINTU_LOOP ; Uses JP in loops - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/printnum.asm b/src/lib/arch/zxnext/runtime/printnum.asm index 28e4e98fe..c465f8039 100644 --- a/src/lib/arch/zxnext/runtime/printnum.asm +++ b/src/lib/arch/zxnext/runtime/printnum.asm @@ -1,36 +1,8 @@ -#include once -#include once - - push namespace core - -__PRINTU_START: - PROC - - LOCAL __PRINTU_CONT - - ld a, b - or a - jp nz, __PRINTU_CONT - - ld a, '0' - jp __PRINT_DIGIT - - -__PRINTU_CONT: - pop af - push bc - call __PRINT_DIGIT - pop bc - djnz __PRINTU_CONT - ret - - ENDP - - -__PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER must preserve registers - ld a, '-' - jp __PRINT_DIGIT - -__PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/printstr.asm b/src/lib/arch/zxnext/runtime/printstr.asm index 808e31878..20ff96df2 100644 --- a/src/lib/arch/zxnext/runtime/printstr.asm +++ b/src/lib/arch/zxnext/runtime/printstr.asm @@ -1,59 +1,8 @@ -#include once -#include once -#include once -#include once - -; PRINT command routine -; Prints string pointed by HL - - push namespace core - -PRINT_STR: -__PRINTSTR: ; __FASTCALL__ Entry to print_string - PROC - LOCAL __PRINT_STR_LOOP - LOCAL __PRINT_STR_END - - ld d, a ; Saves A reg (Flag) for later - - ld a, h - or l - ret z ; Return if the pointer is NULL - - push hl - - ld c, (hl) - inc hl - ld b, (hl) - inc hl ; BC = LEN(a$); HL = &a$ - -__PRINT_STR_LOOP: - ld a, b - or c - jr z, __PRINT_STR_END ; END if BC (counter = 0) - - ld a, (hl) - call __PRINTCHAR - inc hl - dec bc - jp __PRINT_STR_LOOP - -__PRINT_STR_END: - pop hl - ld a, d ; Recovers A flag - or a ; If not 0 this is a temporary string. Free it - ret z - jp __MEM_FREE ; Frees str from heap and return from there - -__PRINT_STR: - ; Fastcall Entry - ; It ONLY prints strings - ; HL = String start - ; BC = String length (Number of chars) - push hl ; Push str address for later - ld d, a ; Saves a FLAG - jp __PRINT_STR_LOOP - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/printu16.asm b/src/lib/arch/zxnext/runtime/printu16.asm index 1b62d6617..546dabdac 100644 --- a/src/lib/arch/zxnext/runtime/printu16.asm +++ b/src/lib/arch/zxnext/runtime/printu16.asm @@ -1,2 +1,8 @@ -#include once +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/printu32.asm b/src/lib/arch/zxnext/runtime/printu32.asm index 4114ac5cc..3d60d748e 100644 --- a/src/lib/arch/zxnext/runtime/printu32.asm +++ b/src/lib/arch/zxnext/runtime/printu32.asm @@ -1,2 +1,8 @@ -#include once +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/printu8.asm b/src/lib/arch/zxnext/runtime/printu8.asm index 77cc7c03c..fe3f865d7 100644 --- a/src/lib/arch/zxnext/runtime/printu8.asm +++ b/src/lib/arch/zxnext/runtime/printu8.asm @@ -1,2 +1,8 @@ -#include once +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/pstore32.asm b/src/lib/arch/zxnext/runtime/pstore32.asm index aac91082b..1bb80284f 100644 --- a/src/lib/arch/zxnext/runtime/pstore32.asm +++ b/src/lib/arch/zxnext/runtime/pstore32.asm @@ -1,14 +1,8 @@ -#include once +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -; Stores a 32 bit integer number (DE,HL) at (IX + BC) - push namespace core - -__PSTORE32: - push hl - push ix - pop hl - add hl, bc - pop bc - jp __STORE32 - - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/pstoref.asm b/src/lib/arch/zxnext/runtime/pstoref.asm index c387b5547..e113ee1ba 100644 --- a/src/lib/arch/zxnext/runtime/pstoref.asm +++ b/src/lib/arch/zxnext/runtime/pstoref.asm @@ -1,21 +1,8 @@ -; Stores FP number in A ED CB at location HL+IX -; HL = Offset -; IX = Stack Frame -; A ED CB = FP Number - -#include once - -; Stored a float number in A ED CB into the address pointed by IX + HL - push namespace core - -__PSTOREF: - push de - ex de, hl ; DE <- HL - push ix - pop hl ; HL <- IX - add hl, de ; HL <- IX + DE - pop de - jp __STOREF - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/pstorestr.asm b/src/lib/arch/zxnext/runtime/pstorestr.asm index e65d1e340..7eee7b67e 100644 --- a/src/lib/arch/zxnext/runtime/pstorestr.asm +++ b/src/lib/arch/zxnext/runtime/pstorestr.asm @@ -1,18 +1,8 @@ -; vim:ts=4:et:sw=4 +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Stores an string (pointer to the HEAP by DE) into the address pointed -; by (IX + BC). A new copy of the string is created into the HEAP -; - -#include once - - push namespace core - -__PSTORE_STR: - push ix - pop hl - add hl, bc - jp __STORE_STR - - pop namespace +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/pstorestr2.asm b/src/lib/arch/zxnext/runtime/pstorestr2.asm index 85109c3b3..eee4c276c 100644 --- a/src/lib/arch/zxnext/runtime/pstorestr2.asm +++ b/src/lib/arch/zxnext/runtime/pstorestr2.asm @@ -1,19 +1,8 @@ -; vim:ts=4:et:sw=4 +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; Stores an string (pointer to the HEAP by DE) into the address pointed -; by (IX + BC). No new copy of the string is created into the HEAP, since -; it's supposed it's already created (temporary string) -; - -#include once - - push namespace core - -__PSTORE_STR2: - push ix - pop hl - add hl, bc - jp __STORE_STR2 - - pop namespace +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/pushf.asm b/src/lib/arch/zxnext/runtime/pushf.asm index d8a76c5ec..d910fe473 100644 --- a/src/lib/arch/zxnext/runtime/pushf.asm +++ b/src/lib/arch/zxnext/runtime/pushf.asm @@ -1,32 +1,8 @@ - -; Routine to push Float pointed by HL -; Into the stack. Notice that the hl points to the last -; byte of the FP number. -; Uses H'L' B'C' and D'E' to preserve ABCDEHL registers - - push namespace core - -__FP_PUSH_REV: - push hl - exx - pop hl - pop bc ; Return Address - ld d, (hl) - dec hl - ld e, (hl) - dec hl - push de - ld d, (hl) - dec hl - ld e, (hl) - dec hl - push de - ld d, (hl) - push de - push bc ; Return Address - exx - ret - - pop namespace - - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/random.asm b/src/lib/arch/zxnext/runtime/random.asm index 0e80a4b04..da9d274ed 100644 --- a/src/lib/arch/zxnext/runtime/random.asm +++ b/src/lib/arch/zxnext/runtime/random.asm @@ -1,109 +1,8 @@ -; RANDOM functions - - push namespace core - -RANDOMIZE: - ; Randomize with 32 bit seed in DE HL - ; if SEED = 0, calls ROM to take frames as seed - PROC - - LOCAL TAKE_FRAMES - LOCAL FRAMES - - ld a, h - or l - or d - or e - jr z, TAKE_FRAMES - - ld (RANDOM_SEED_LOW), hl - ld (RANDOM_SEED_HIGH), de - ret - -TAKE_FRAMES: - ; Takes the seed from frames - ld hl, (FRAMES) - ld (RANDOM_SEED_LOW), hl - ld hl, (FRAMES + 2) - ld (RANDOM_SEED_HIGH), hl - ret - -FRAMES EQU 23672 - ENDP - -RANDOM_SEED_HIGH EQU RAND+1 ; RANDOM seed, 16 higher bits -RANDOM_SEED_LOW EQU 23670 ; RANDOM seed, 16 lower bits - - -RAND: - PROC - ld de,0C0DEh ; yw -> zt - ld hl,(RANDOM_SEED_LOW) ; xz -> yw - ld (RANDOM_SEED_LOW),de ; x = y, z = w - ld a,e ; w = w ^ ( w << 3 ) - add a,a - add a,a - add a,a - xor e - ld e,a - ld a,h ; t = x ^ (x << 1) - add a,a - xor h - ld d,a - rra ; t = t ^ (t >> 1) ^ w - xor d - xor e - ld d,l ; y = z - ld e,a ; w = t - ld (RANDOM_SEED_HIGH),de - ret - ENDP - -RND: - ; Returns a FLOATING point integer - ; using RAND as a mantissa - PROC - LOCAL RND_LOOP - - call RAND - ; BC = HL since ZX BASIC uses ED CB A registers for FP - ld b, h - ld c, l - - ld a, e - or d - or c - or b - ret z ; Returns 0 if BC=DE=0 - - ; We already have a random 32 bit mantissa in ED CB - ; From 0001h to FFFFh - - ld l, 81h ; Exponent - ; At this point we have [0 .. 1) FP number; - - ; Now we must shift mantissa left until highest bit goes into carry - ld a, e ; Use A register for rotating E faster (using RLA instead of RL E) -RND_LOOP: - dec l - sla b - rl c - rl d - rla - jp nc, RND_LOOP - - ; Now undo last mantissa left-shift once - ccf ; Clears carry to insert a 0 bit back into mantissa -> positive FP number - rra - rr d - rr c - rr b - - ld e, a ; E must have the highest byte - ld a, l ; exponent in A - ret - - ENDP - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/read_restore.asm b/src/lib/arch/zxnext/runtime/read_restore.asm index f97bc97f0..82afa7d77 100644 --- a/src/lib/arch/zxnext/runtime/read_restore.asm +++ b/src/lib/arch/zxnext/runtime/read_restore.asm @@ -1,362 +1,8 @@ -;; This implements READ & RESTORE functions -;; Reads a new element from the DATA Address code -;; Updates the DATA_ADDR read ptr for the next read - -;; Data codification is 1 byte for type followed by data bytes -;; Byte type is encoded as follows - -;; 00: End of data -;; 01: String -;; 02: Byte -;; 03: Ubyte -;; 04: Integer -;; 05: UInteger -;; 06: Long -;; 07: ULong -;; 08: Fixed -;; 09: Float - -;; bit7 is set for a parameter-less function -;; In that case, the next two bytes are the ptr of the function to jump - -#include once -#include once -#include once -#include once -#include once -#include once -#include once - -#define _str 1 -#define _i8 2 -#define _u8 3 -#define _i16 4 -#define _u16 5 -#define _i32 6 -#define _u32 7 -#define _f16 8 -#define _flt 9 - - -;; Updates restore point to the given HL mem. address - push namespace core - -__RESTORE: - PROC - LOCAL __DATA_ADDR - - ld (__DATA_ADDR), hl - ret - -;; Reads a value from the DATA mem area and updates __DATA_ADDR ptr to the -;; next item. On Out Of Data, restarts -;; -__READ: - LOCAL read_restart, cont, cont2, table, no_func - LOCAL dynamic_cast, dynamic_cast2, dynamic_cast3, dynamic_cast4 - LOCAL _decode_table, coerce_to_int, coerce_to_int2, promote_to_i16 - LOCAL _from_i8, _from_u8 - LOCAL _from_i16, _from_u16 - LOCAL _from_i32, _from_u32 - LOCAL _from_fixed, __data_error - - push af ; type of data to read - ld hl, (__DATA_ADDR) -read_restart: - ld a, (hl) - or a ; 0 => OUT of data - jr nz, cont - ;; Signals out of data - - ld hl, .DATA.__DATA__0 - ld (__DATA_ADDR), hl - jr read_restart ; Start again -cont: - and 0x80 - ld a, (hl) - push af - jp z, no_func ;; Loads data directly, not a function - inc hl - ld e, (hl) - inc hl - ld d, (hl) - inc hl - ld (__DATA_ADDR), hl ;; Store address of next DATA - ex de, hl -cont2: - ld de, dynamic_cast - push de ; ret address - jp (hl) ; "call (hl)" - - ;; Now tries to convert the given result to the expected type or raise an error -dynamic_cast: - exx - ex af, af' - pop af ; type READ - and 0x7F ; clear bit 7 - pop hl ; type requested by USER (type of the READ variable) - ld c, h ; save requested type (save it in register C) - cp h - exx - jr nz, dynamic_cast2 ; Types are identical? - ;; yes, they are - ex af, af' - ret - -dynamic_cast2: - cp _str ; Requested a number, but read a string? - jr nz, dynamic_cast3 - call __MEM_FREE ; Frees str from memory - jr __data_error - -dynamic_cast3: - exx - ld b, a ; Read type - ld a, c ; Requested type - cp _str - jr z, __data_error - cp b - jr c, dynamic_cast4 - ;; here the user expected type is "larger" than the read one - ld a, b - sub _i8 - add a, a - ld l, a - ld h, 0 - ld de, _decode_table - add hl, de - ld e, (hl) - inc hl - ld h, (hl) - ld l, e - push hl - ld a, c ; Requested type - exx - ret - -__data_error: - ;; When a data is read, but cannot be converted to the requested type - ;; that is, the user asked for a string and we read a number or vice versa - ld a, ERROR_InvalidArg - call __STOP ; The user expected a string, but read a number - xor a - ld h, a - ld l, a - ld e, a - ld d, a - ld b, a - ld c, a - ret - -_decode_table: - dw _from_i8 - dw _from_u8 - dw _from_i16 - dw _from_u16 - dw _from_i32 - dw _from_u32 - dw _from_fixed - -_from_i8: - cp _i16 - jr nc, promote_to_i16 - ex af, af' - ret ;; Was from Byte to Ubyte - -promote_to_i16: - ex af, af' - ld l, a - rla - sbc a, a - ld h, a ; copy sgn to h - ex af, af' - jr _before_from_i16 - -_from_u8: - ex af, af' - ld l, a - ld h, 0 - ex af, af' - ;; Promoted to i16 - -_before_from_i16: -_from_i16: - cp _i32 - ret c ;; from i16 to u16 - ;; Promote i16 to i32 - ex af, af' - ld a, h - rla - sbc a, a - ld e, a - ld d, a - ex af, af' -_from_i32: - cp _u32 - ret z ;; From i32 to u32 - ret c ;; From u16 to i32 - cp _flt - jp z, __I32TOFREG -_from_u32: - cp _flt - jp z, __U32TOFREG - ex de, hl - ld hl, 0 - cp _f16 - ret z -_from_fixed: ;; From fixed to float - jp __F16TOFREG -_from_u16: - ld de, 0 ; HL 0x0000 => 32 bits - jp _from_i32 - -dynamic_cast4: - ;; The user type is "shorter" than the read one - ld a, b ;; read type - cp _i16 ;; if user type < read type < _i16 => From Ubyte to Byte. Return af' - jr nc, 1f - ex af, af' - ret -1: - ld a, c ;; recover user required type - cp _f16 ;; required type - jr c, before_to_int ;; required < fixed (f16) - ex af, af' - exx ;; Ok, we must convert from float to f16 - jp __FTOF16REG - -before_to_int: - ld a, b ;; read type - cp _f16 ;; - jr c, coerce_to_int2 - jr nz, coerce_to_int ;; From float to int - ld a, c ;; user type - exx - ;; f16 to Long - ex de, hl - ld a, h - rla - sbc a, a - ld d, a - ld e, a - exx - jr coerce_to_int2 -coerce_to_int: - exx - ex af, af' - call __FTOU32REG - ex af, af' ; a contains user type - exx -coerce_to_int2: ; At this point we have an u/integer in hl - exx - cp _i16 - ret nc ; Already done. Return the result - ld a, l ; Truncate to byte - ret - -no_func: - exx - ld de, dynamic_cast - push de ; Ret address - dec a ; 0 => string; 1, 2 => byte; 3, 4 => integer; 5, 6 => long, 7 => fixed; 8 => float - ld h, 0 - add a, a - ld l, a - ld de, table - add hl, de - ld e, (hl) - inc hl - ld h, (hl) - ld l, e - push hl ; address to jump to - exx - inc hl - ret ; jp (sp) => jump to table[a - 1] - -table: - LOCAL __01_decode_string - LOCAL __02_decode_byte - LOCAL __03_decode_ubyte - LOCAL __04_decode_integer - LOCAL __05_decode_uinteger - LOCAL __06_decode_long - LOCAL __07_decode_ulong - LOCAL __08_decode_fixed - LOCAL __09_decode_float - - ;; 1 -> Decode string - ;; 2, 3 -> Decode Byte, UByte - ;; 4, 5 -> Decode Integer, UInteger - ;; 6, 7 -> Decode Long, ULong - ;; 8 -> Decode Fixed - ;; 9 -> Decode Float - dw __01_decode_string - dw __02_decode_byte - dw __03_decode_ubyte - dw __04_decode_integer - dw __05_decode_uinteger - dw __06_decode_long - dw __07_decode_ulong - dw __08_decode_fixed - dw __09_decode_float - -__01_decode_string: - ld e, (hl) - inc hl - ld d, (hl) - inc hl - ld (__DATA_ADDR), hl ;; Store address of next DATA - ex de, hl - jp __LOADSTR - -__02_decode_byte: -__03_decode_ubyte: - ld a, (hl) - inc hl - ld (__DATA_ADDR), hl - ret - -__04_decode_integer: -__05_decode_uinteger: - ld e, (hl) - inc hl - ld d, (hl) - inc hl - ld (__DATA_ADDR), hl - ex de, hl - ret - -__06_decode_long: -__07_decode_ulong: -__08_decode_fixed: - ld b, h - ld c, l - inc bc - inc bc - inc bc - inc bc - ld (__DATA_ADDR), bc - jp __ILOAD32 - -__09_decode_float: - call __LOADF - inc hl - ld (__DATA_ADDR), hl - ld h, a ; returns A in H; sets A free - ret - -__DATA_ADDR: ;; Stores current DATA ptr - dw .DATA.__DATA__0 - ENDP - -#undef _str -#undef _i8 -#undef _u8 -#undef _i16 -#undef _u16 -#undef _i32 -#undef _u32 -#undef _f16 -#undef _flt - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/save.asm b/src/lib/arch/zxnext/runtime/save.asm index 791088bab..a78a5f67a 100644 --- a/src/lib/arch/zxnext/runtime/save.asm +++ b/src/lib/arch/zxnext/runtime/save.asm @@ -1,186 +1,8 @@ -; Save code "XXX" at address YYY of length ZZZ -; Parameters in the stack are XXX (16 bit) address of string name -; (only first 12 chars will be taken into account) -; YYY and ZZZ are 16 bit on top of the stack. - -#include once -#include once - - push namespace core - -SAVE_CODE: - - PROC - - LOCAL MEMBOT - LOCAL SAVE_CONT - LOCAL ROM_SAVE - LOCAL __ERR_EMPTY - LOCAL SAVE_STOP - LOCAL STR_PTR - LOCAL SAVE_EMPTY_ERROR - -#ifdef __ENABLE_BREAK__ - ROM_SAVE EQU 0970h -#endif - MEMBOT EQU 23698 ; Use the CALC mem to store header - STR_PTR EQU MEMBOT + 17 - - pop hl ; Return address - pop bc ; data length in bytes - pop de ; address start - ex (sp), hl ; CALLE => now hl = String - ld (STR_PTR), hl - -; This function will call the ROM SAVE CODE Routine -; Parameters in the stack are HL => String with SAVE name -; (only first 12 chars will be taken into account) -; DE = START address of CODE to save -; BC = Length of data in bytes - -__SAVE_CODE: ; INLINE version - ld a, b - or c - jr z, SAVE_EMPTY_ERROR ; Return if block length == 0 - - push ix - ld a, h - or l - jr z, __ERR_EMPTY ; Return if NULL STRING - - ld ix, MEMBOT - ld (ix + 00), 3 ; CODE - - ld (ix + 11), c - ld (ix + 12), b ; Store long in bytes - ld (ix + 13), e - ld (ix + 14), d ; Store address in bytes - - push hl - ld bc, 9 - ld HL, MEMBOT + 1 - ld DE, MEMBOT + 2 - ld (hl), ' ' - ldir ; Fill the filename with blanks - pop hl - - ld c, (hl) - inc hl - ld b, (hl) - inc hl - ld a, b - or c - -__ERR_EMPTY: - ld a, ERROR_InvalidFileName - jr z, SAVE_STOP ; Return if str len == 0 - - ex de, hl ; Saves HL in DE - ld hl, 10 - or a - sbc hl, bc ; Test BC > 10? - ex de, hl - jr nc, SAVE_CONT ; Ok BC <= 10 - ld bc, 10 ; BC at most 10 chars - -SAVE_CONT: - ld de, MEMBOT + 1 - ldir ; Copy String block NAME - ld hl, (STR_PTR) - call MEM_FREE - ld l, (ix + 13) - ld h, (ix + 14) ; Restores start of bytes - - ld a, r - push af - call ROM_SAVE - - LOCAL NO_INT - pop af - jp po, NO_INT - ei -NO_INT: - ; Recovers ECHO_E since ROM SAVE changes it - ld hl, 1821h - ld (23682), hl - pop ix - ret - -SAVE_EMPTY_ERROR: - ld a, ERROR_InvalidArg - -SAVE_STOP: - pop ix - push af - ld hl, (STR_PTR) - call MEM_FREE - pop af - jp __STOP - -#ifndef __ENABLE_BREAK__ - LOCAL CHAN_OPEN - LOCAL PO_MSG - LOCAL WAIT_KEY - LOCAL SA_BYTES - LOCAL SA_CHK_BRK - LOCAL SA_CONT - - CHAN_OPEN EQU 1601h - PO_MSG EQU 0C0Ah - WAIT_KEY EQU 15D4h - SA_BYTES EQU 04C6h - -ROM_SAVE: - push hl - ld a, 0FDh - call CHAN_OPEN - xor a - ld de, 09A1h - call PO_MSG - set 5, (iy + 02h) - call WAIT_KEY - push ix - ld de, 0011h - xor a - call SA_BYTES - pop ix - - call SA_CHK_BRK - jr c, SA_CONT - pop ix - ret - -SA_CONT: - ei - ld b, 32h - -LOCAL SA_1_SEC -SA_1_SEC: - halt - djnz SA_1_SEC - - ld e, (ix + 0Bh) - ld d, (ix + 0Ch) - ld a, 0FFh - pop ix - call SA_BYTES - -SA_CHK_BRK: - ld b, a - ld a, (5C48h) - and 38h - rrca - rrca - rrca - out (0FEh), a - ld a, 7Fh - in a, (0FEh) - rra - ld a, b - ret - -#endif - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/set_pixel_addr_attr.asm b/src/lib/arch/zxnext/runtime/set_pixel_addr_attr.asm index 383d027da..1d03b7a2c 100644 --- a/src/lib/arch/zxnext/runtime/set_pixel_addr_attr.asm +++ b/src/lib/arch/zxnext/runtime/set_pixel_addr_attr.asm @@ -1,23 +1,8 @@ -#include once - -push namespace core - -; Sets the attribute at a given screen pixel address in hl -; HL contains the address in RAM for a given pixel (not a coordinate) -SET_PIXEL_ADDR_ATTR: - ;; gets ATTR position with offset given in SCREEN_ADDR - ld de, (SCREEN_ADDR) - or a - sbc hl, de - ld a, h - rrca - rrca - rrca - and 3 - ld h, a - - ld de, (SCREEN_ATTR_ADDR) - add hl, de ;; Final screen addr - jp __SET_ATTR2 - -pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/sgn.asm b/src/lib/arch/zxnext/runtime/sgn.asm index 47eb9e452..d75fbeff2 100644 --- a/src/lib/arch/zxnext/runtime/sgn.asm +++ b/src/lib/arch/zxnext/runtime/sgn.asm @@ -1,44 +1,8 @@ -; Returns SGN (SIGN) for 32, 16 and 8 bits signed integers, Fixed and FLOAT - - push namespace core - - PROC - LOCAL __ENDSGN - -__SGNF: - or b - or c - or d - or e - ret z - ld a, e - jr __ENDSGN - -__SGNF16: -__SGNI32: - ld a, h - or l - or e - or d - ret z - - ld a, d - jr __ENDSGN - -__SGNI16: - ld a, h - or l - ret z - ld a, h - -__ENDSGN: - or a - ld a, 1 - ret p - neg - ret - - ENDP - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/sgnf.asm b/src/lib/arch/zxnext/runtime/sgnf.asm index a264ec077..b478c5294 100644 --- a/src/lib/arch/zxnext/runtime/sgnf.asm +++ b/src/lib/arch/zxnext/runtime/sgnf.asm @@ -1,2 +1,8 @@ -#include once +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/sgnf16.asm b/src/lib/arch/zxnext/runtime/sgnf16.asm index a264ec077..53e69c4fe 100644 --- a/src/lib/arch/zxnext/runtime/sgnf16.asm +++ b/src/lib/arch/zxnext/runtime/sgnf16.asm @@ -1,2 +1,8 @@ -#include once +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/sgni16.asm b/src/lib/arch/zxnext/runtime/sgni16.asm index a264ec077..d46e6ee4d 100644 --- a/src/lib/arch/zxnext/runtime/sgni16.asm +++ b/src/lib/arch/zxnext/runtime/sgni16.asm @@ -1,2 +1,8 @@ -#include once +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/sgni32.asm b/src/lib/arch/zxnext/runtime/sgni32.asm index a264ec077..b4d02e549 100644 --- a/src/lib/arch/zxnext/runtime/sgni32.asm +++ b/src/lib/arch/zxnext/runtime/sgni32.asm @@ -1,2 +1,8 @@ -#include once +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/sgni8.asm b/src/lib/arch/zxnext/runtime/sgni8.asm index bbfddd63c..c8334c54b 100644 --- a/src/lib/arch/zxnext/runtime/sgni8.asm +++ b/src/lib/arch/zxnext/runtime/sgni8.asm @@ -1,14 +1,8 @@ -; Returns SGN (SIGN) for 8 bits signed integer - - push namespace core - -__SGNI8: - or a - ret z - ld a, 1 - ret p - neg - ret - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/sgnu16.asm b/src/lib/arch/zxnext/runtime/sgnu16.asm index 6f649236f..046778634 100644 --- a/src/lib/arch/zxnext/runtime/sgnu16.asm +++ b/src/lib/arch/zxnext/runtime/sgnu16.asm @@ -1,13 +1,8 @@ -; Returns SGN (SIGN) for 16 bits unsigned integer - - push namespace core - -__SGNU16: - ld a, h - or l - ret z - ld a, 1 - ret - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/sgnu32.asm b/src/lib/arch/zxnext/runtime/sgnu32.asm index 8130a3b71..0a0f1f8a2 100644 --- a/src/lib/arch/zxnext/runtime/sgnu32.asm +++ b/src/lib/arch/zxnext/runtime/sgnu32.asm @@ -1,16 +1,8 @@ -; Returns SGN (SIGN) for 32 bits unsigned integer - - push namespace core - -__SGNU32: - ld a, h - or l - or d - or e - ret z - - ld a, 1 - ret - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/sgnu8.asm b/src/lib/arch/zxnext/runtime/sgnu8.asm index 89de890f8..47e9a2b8f 100644 --- a/src/lib/arch/zxnext/runtime/sgnu8.asm +++ b/src/lib/arch/zxnext/runtime/sgnu8.asm @@ -1,12 +1,8 @@ -; Returns SGN (SIGN) for 8 bits unsigned integera - - push namespace core - -__SGNU8: - or a - ret z - ld a, 1 - ret - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/spectranet.inc b/src/lib/arch/zxnext/runtime/spectranet.inc index 052e61f22..73fbd2c1e 100644 --- a/src/lib/arch/zxnext/runtime/spectranet.inc +++ b/src/lib/arch/zxnext/runtime/spectranet.inc @@ -1,143 +1,143 @@ -;The MIT License -; -;Copyright (c) 2008 Dylan Smith -; -;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. - -; This file can be included in assembly language programs to give -; symbolic access to the public jump table entry points. - -; Avoid double inclusion -#ifndef __SPECTRANET_INC__ -#define __SPECTRANET_INC__ - -NAMESPACE Spectranet ; NAME PREFIX to avoid naming clash - -; Hardware page-in entry points -MODULECALL equ $3FF8 -MODULECALL_NOPAGE equ $28 -PAGEIN equ $3FF9 -PAGEOUT equ $007C -HLCALL equ $3FFA -IXCALL equ $3FFD - -; Port defines -CTRLREG equ $033B -CPLDINFO equ $023B - -; Jump table entry points -SOCKET equ $3E00 ; Allocate a socket -CLOSE equ $3E03 ; Close a socket -LISTEN equ $3E06 ; Listen for incoming connections -ACCEPT equ $3E09 ; Accept an incoming connection -BIND equ $3E0C ; Bind a local address to a socket -CONNECT equ $3E0F ; Connect to a remote host -SEND equ $3E12 ; Send data -RECV equ $3E15 ; Receive data -SENDTO equ $3E18 ; Send data to an address -RECVFROM equ $3E1B ; Receive data from an address -POLL equ $3E1E ; Poll a list of sockets -POLLALL equ $3E21 ; Poll all open sockets -POLLFD equ $3E24 ; Poll a single socket -GETHOSTBYNAME equ $3E27 ; Look up a hostname -PUTCHAR42 equ $3E2A ; 42 column print write a character -PRINT42 equ $3E2D ; 42 column print a null terminated string -CLEAR42 equ $3E30 ; Clear the screen and reset 42-col print -SETPAGEA equ $3E33 ; Sets page area A -SETPAGEB equ $3E36 ; Sets page area B -LONG2IPSTRING equ $3E39 ; Convert a 4 byte big endian long to an IP -IPSTRING2LONG equ $3E3C ; Convert an IP to a 4 byte big endian long -ITOA8 equ $3E3F ; Convert a byte to ascii -RAND16 equ $3E42 ; 16 bit PRNG -REMOTEADDRESS equ $3E45 ; Fill struct sockaddr_in -IFCONFIG_INET equ $3E48 ; Set IPv4 address -IFCONFIG_NETMASK equ $3E4B ; Set netmask -IFCONFIG_GW equ $3E4E ; Set gateway -INITHW equ $3E51 ; Set the MAC address and initial hw registers -GETHWADDR equ $3E54 ; Read the MAC address -DECONFIG equ $3E57 ; Deconfigure inet, netmask and gateway -MAC2STRING equ $3E5A ; Convert 6 byte MAC address to a string -STRING2MAC equ $3E5D ; Convert a hex string to a 6 byte MAC address -ITOH8 equ $3E60 ; Convert accumulator to hex string -HTOI8 equ $3E63 ; Convert hex string to byte in A -GETKEY equ $3E66 ; Get a key from the keyboard, and put it in A -KEYUP equ $3E69 ; Wait for key release -INPUTSTRING equ $3E6C ; Read a string into buffer at DE -GET_IFCONFIG_INET equ $3E6F ; Gets the current IPv4 address -GET_IFCONFIG_NETMASK equ $3E72 ; Gets the current netmask -GET_IFCONFIG_GW equ $3E75 ; Gets the current gateway address -SETTRAP equ $3E78 ; Sets the programmable trap -DISABLETRAP equ $3E7B ; Disables the programmable trap -ENABLETRAP equ $3E7E ; Enables the programmable trap -PUSHPAGEA equ $3E81 ; Pages a page into area A, pushing the old one -POPPAGEA equ $3E84 ; Restores the previous page in area A -PUSHPAGEB equ $3E87 ; Pages into area B pushing the old one -POPPAGEB equ $3E8A ; Restores the previous page in area B -PAGETRAPRETURN equ $3E8D ; Returns from a trap to page area B -TRAPRETURN equ $3E90 ; Returns from a trap that didn't page area B -ADDBASICEXT equ $3E93 ; Adds a BASIC command -STATEMENT_END equ $3E96 ; Check for statement end, exit at syntax time -EXIT_SUCCESS equ $3E99 ; Use this to exit successfully after cmd -PARSE_ERROR equ $3E9C ; Use this to exit to BASIC with a parse error -RESERVEPAGE equ $3E9F ; Reserve a page of static RAM -FREEPAGE equ $3EA2 ; Free a page of static RAM -REPORTERR equ $3EA5 ; report an error via BASIC - -; Filesystem functions -MOUNT equ $3EA8 -UMOUNT equ $3EAB -OPENDIR equ $3EAE -OPEN equ $3EB1 -UNLINK equ $3EB4 -MKDIR equ $3EB7 -RMDIR equ $3EBA -SIZE equ $3EBD -FREE equ $3EC0 -STAT equ $3EC3 -CHMOD equ $3EC6 -READ equ $3EC9 -WRITE equ $3ECC -LSEEK equ $3ECF -VCLOSE equ $3ED2 -VPOLL equ $3ED5 -READDIR equ $3ED8 -CLOSEDIR equ $3EDB -CHDIR equ $3EDE -GETCWD equ $3EE1 -RENAME equ $3EE4 -SETMOUNTPOINT equ $3EE7 -FREEMOUNTPOINT equ $3EEA -RESALLOC equ $3EED - - -; Definitions -ALLOCFD equ 1 -FREEFD equ 0 -ALLOCDIRHND equ 3 -FREEDIRHND equ 2 - -; POLL status bits -BIT_RECV equ 2 -BIT_DISCON equ 1 -BIT_CONN equ 0 - -NAMESPACE DEFAULT ; Clears namespace - -#endif - +;The MIT License +; +;Copyright (c) 2008 Dylan Smith +; +;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. + +; This file can be included in assembly language programs to give +; symbolic access to the public jump table entry points. + +; Avoid double inclusion +#ifndef __SPECTRANET_INC__ +#define __SPECTRANET_INC__ + +NAMESPACE Spectranet ; NAME PREFIX to avoid naming clash + +; Hardware page-in entry points +MODULECALL equ $3FF8 +MODULECALL_NOPAGE equ $28 +PAGEIN equ $3FF9 +PAGEOUT equ $007C +HLCALL equ $3FFA +IXCALL equ $3FFD + +; Port defines +CTRLREG equ $033B +CPLDINFO equ $023B + +; Jump table entry points +SOCKET equ $3E00 ; Allocate a socket +CLOSE equ $3E03 ; Close a socket +LISTEN equ $3E06 ; Listen for incoming connections +ACCEPT equ $3E09 ; Accept an incoming connection +BIND equ $3E0C ; Bind a local address to a socket +CONNECT equ $3E0F ; Connect to a remote host +SEND equ $3E12 ; Send data +RECV equ $3E15 ; Receive data +SENDTO equ $3E18 ; Send data to an address +RECVFROM equ $3E1B ; Receive data from an address +POLL equ $3E1E ; Poll a list of sockets +POLLALL equ $3E21 ; Poll all open sockets +POLLFD equ $3E24 ; Poll a single socket +GETHOSTBYNAME equ $3E27 ; Look up a hostname +PUTCHAR42 equ $3E2A ; 42 column print write a character +PRINT42 equ $3E2D ; 42 column print a null terminated string +CLEAR42 equ $3E30 ; Clear the screen and reset 42-col print +SETPAGEA equ $3E33 ; Sets page area A +SETPAGEB equ $3E36 ; Sets page area B +LONG2IPSTRING equ $3E39 ; Convert a 4 byte big endian long to an IP +IPSTRING2LONG equ $3E3C ; Convert an IP to a 4 byte big endian long +ITOA8 equ $3E3F ; Convert a byte to ascii +RAND16 equ $3E42 ; 16 bit PRNG +REMOTEADDRESS equ $3E45 ; Fill struct sockaddr_in +IFCONFIG_INET equ $3E48 ; Set IPv4 address +IFCONFIG_NETMASK equ $3E4B ; Set netmask +IFCONFIG_GW equ $3E4E ; Set gateway +INITHW equ $3E51 ; Set the MAC address and initial hw registers +GETHWADDR equ $3E54 ; Read the MAC address +DECONFIG equ $3E57 ; Deconfigure inet, netmask and gateway +MAC2STRING equ $3E5A ; Convert 6 byte MAC address to a string +STRING2MAC equ $3E5D ; Convert a hex string to a 6 byte MAC address +ITOH8 equ $3E60 ; Convert accumulator to hex string +HTOI8 equ $3E63 ; Convert hex string to byte in A +GETKEY equ $3E66 ; Get a key from the keyboard, and put it in A +KEYUP equ $3E69 ; Wait for key release +INPUTSTRING equ $3E6C ; Read a string into buffer at DE +GET_IFCONFIG_INET equ $3E6F ; Gets the current IPv4 address +GET_IFCONFIG_NETMASK equ $3E72 ; Gets the current netmask +GET_IFCONFIG_GW equ $3E75 ; Gets the current gateway address +SETTRAP equ $3E78 ; Sets the programmable trap +DISABLETRAP equ $3E7B ; Disables the programmable trap +ENABLETRAP equ $3E7E ; Enables the programmable trap +PUSHPAGEA equ $3E81 ; Pages a page into area A, pushing the old one +POPPAGEA equ $3E84 ; Restores the previous page in area A +PUSHPAGEB equ $3E87 ; Pages into area B pushing the old one +POPPAGEB equ $3E8A ; Restores the previous page in area B +PAGETRAPRETURN equ $3E8D ; Returns from a trap to page area B +TRAPRETURN equ $3E90 ; Returns from a trap that didn't page area B +ADDBASICEXT equ $3E93 ; Adds a BASIC command +STATEMENT_END equ $3E96 ; Check for statement end, exit at syntax time +EXIT_SUCCESS equ $3E99 ; Use this to exit successfully after cmd +PARSE_ERROR equ $3E9C ; Use this to exit to BASIC with a parse error +RESERVEPAGE equ $3E9F ; Reserve a page of static RAM +FREEPAGE equ $3EA2 ; Free a page of static RAM +REPORTERR equ $3EA5 ; report an error via BASIC + +; Filesystem functions +MOUNT equ $3EA8 +UMOUNT equ $3EAB +OPENDIR equ $3EAE +OPEN equ $3EB1 +UNLINK equ $3EB4 +MKDIR equ $3EB7 +RMDIR equ $3EBA +SIZE equ $3EBD +FREE equ $3EC0 +STAT equ $3EC3 +CHMOD equ $3EC6 +READ equ $3EC9 +WRITE equ $3ECC +LSEEK equ $3ECF +VCLOSE equ $3ED2 +VPOLL equ $3ED5 +READDIR equ $3ED8 +CLOSEDIR equ $3EDB +CHDIR equ $3EDE +GETCWD equ $3EE1 +RENAME equ $3EE4 +SETMOUNTPOINT equ $3EE7 +FREEMOUNTPOINT equ $3EEA +RESALLOC equ $3EED + + +; Definitions +ALLOCFD equ 1 +FREEFD equ 0 +ALLOCDIRHND equ 3 +FREEDIRHND equ 2 + +; POLL status bits +BIT_RECV equ 2 +BIT_DISCON equ 1 +BIT_CONN equ 0 + +NAMESPACE DEFAULT ; Clears namespace + +#endif + diff --git a/src/lib/arch/zxnext/runtime/sposn.asm b/src/lib/arch/zxnext/runtime/sposn.asm index 223d8dd89..12b204802 100644 --- a/src/lib/arch/zxnext/runtime/sposn.asm +++ b/src/lib/arch/zxnext/runtime/sposn.asm @@ -1,58 +1,8 @@ -#include once -#include once - -; Printing positioning library. - push namespace core - -; Loads into DE current ROW, COL print position from S_POSN mem var. -__LOAD_S_POSN: - PROC - - ld de, (S_POSN) - ld hl, SCR_SIZE - or a - sbc hl, de - ex de, hl - ret - - ENDP - - -; Saves ROW, COL from DE into S_POSN mem var. -__SAVE_S_POSN: - PROC - - ld hl, SCR_SIZE - or a - sbc hl, de - ld (S_POSN), hl ; saves it again - -__SET_SCR_PTR: ;; Fast - push de - call __ATTR_ADDR - ld (DFCCL), hl - pop de - - ld a, d - ld c, a ; Saves it for later - - and 0F8h ; Masks 3 lower bit ; zy - ld d, a - - ld a, c ; Recovers it - and 07h ; MOD 7 ; y1 - rrca - rrca - rrca - - or e - ld e, a - - ld hl, (SCREEN_ADDR) - add hl, de ; HL = Screen address + DE - ld (DFCC), hl - ret - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/stackf.asm b/src/lib/arch/zxnext/runtime/stackf.asm index 5546bdd5a..a9aa18a47 100644 --- a/src/lib/arch/zxnext/runtime/stackf.asm +++ b/src/lib/arch/zxnext/runtime/stackf.asm @@ -1,49 +1,8 @@ -; ------------------------------------------------------------- -; Functions to manage FP-Stack of the ZX Spectrum ROM CALC -; ------------------------------------------------------------- - - - push namespace core - -__FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) -__FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) - -__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) - ; Second argument to push into the stack calculator is popped out of the stack - ; Since the caller routine also receives the parameters into the top of the stack - ; four bytes must be removed from SP before pop them out - - call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK - exx - pop hl ; Caller-Caller return addr - exx - pop hl ; Caller return addr - - pop af - pop de - pop bc - - push hl ; Caller return addr - exx - push hl ; Caller-Caller return addr - exx - - jp __FPSTACK_PUSH - - -__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK - ; This format is specified in the ZX 48K Manual - ; You can push a 16 bit signed integer as - ; 0 SS LL HH 0, being SS the sign and LL HH the low - ; and High byte respectively - ld a, h - rla ; sign to Carry - sbc a, a ; 0 if positive, FF if negative - ld e, a - ld d, l - ld c, h - xor a - ld b, a - jp __FPSTACK_PUSH - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/store32.asm b/src/lib/arch/zxnext/runtime/store32.asm index 753b20e28..a21256507 100644 --- a/src/lib/arch/zxnext/runtime/store32.asm +++ b/src/lib/arch/zxnext/runtime/store32.asm @@ -1,27 +1,8 @@ - push namespace core - -__PISTORE32: - push hl - push ix - pop hl - add hl, bc - pop bc - -__ISTORE32: ; Load address at hl, and stores E,D,B,C integer at that address - ld a, (hl) - inc hl - ld h, (hl) - ld l, a - -__STORE32: ; Stores the given integer in DEBC at address HL - ld (hl), c - inc hl - ld (hl), b - inc hl - ld (hl), e - inc hl - ld (hl), d - ret - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/storef.asm b/src/lib/arch/zxnext/runtime/storef.asm index bda382fcf..6cd101e51 100644 --- a/src/lib/arch/zxnext/runtime/storef.asm +++ b/src/lib/arch/zxnext/runtime/storef.asm @@ -1,32 +1,8 @@ - push namespace core - -__PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) - push de - ex de, hl ; DE <- HL - push ix - pop hl ; HL <- IX - add hl, de ; HL <- IX + HL - pop de - -__ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address. Modifies A' register - ex af, af' - ld a, (hl) - inc hl - ld h, (hl) - ld l, a ; HL = (HL) - ex af, af' - -__STOREF: ; Stores the given FP number in A EDCB at address HL - ld (hl), a - inc hl - ld (hl), e - inc hl - ld (hl), d - inc hl - ld (hl), c - inc hl - ld (hl), b - ret - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/storestr.asm b/src/lib/arch/zxnext/runtime/storestr.asm index 61dc63bdb..f907d1e4d 100644 --- a/src/lib/arch/zxnext/runtime/storestr.asm +++ b/src/lib/arch/zxnext/runtime/storestr.asm @@ -1,48 +1,8 @@ -; vim:ts=4:et:sw=4 -; Stores value of current string pointed by DE register into address pointed by HL -; Returns DE = Address pointer (&a$) -; Returns HL = HL (b$ => might be needed later to free it from the heap) +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; e.g. => HL = _variableName (DIM _variableName$) -; DE = Address into the HEAP -; -; This function will resize (REALLOC) the space pointed by HL -; before copying the content of b$ into a$ - - -#include once - - push namespace core - -__PISTORE_STR: ; Indirect assignment at (IX + BC) - push ix - pop hl - add hl, bc - -__ISTORE_STR: ; Indirect assignment, hl point to a pointer to a pointer to the heap! - ld c, (hl) - inc hl - ld h, (hl) - ld l, c ; HL = (HL) - -__STORE_STR: - push de ; Pointer to b$ - push hl ; Pointer to a$ - - ld c, (hl) - inc hl - ld h, (hl) - ld l, c ; HL = (HL) - - call __STRASSIGN ; HL (a$) = DE (b$); HL changed to a new dynamic memory allocation - ex de, hl ; DE = new address of a$ - pop hl ; Recover variable memory address pointer - - ld (hl), e - inc hl - ld (hl), d ; Stores a$ ptr into element ptr - - pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) - ret +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/storestr2.asm b/src/lib/arch/zxnext/runtime/storestr2.asm index 343d2c4a6..685fd9cec 100644 --- a/src/lib/arch/zxnext/runtime/storestr2.asm +++ b/src/lib/arch/zxnext/runtime/storestr2.asm @@ -1,42 +1,8 @@ -; Similar to __STORE_STR, but this one is called when -; the value of B$ if already duplicated onto the stack. -; So we needn't call STRASSING to create a duplication -; HL = address of string memory variable -; DE = address of 2n string. It just copies DE into (HL) -; freeing (HL) previously. - -#include once - - push namespace core - -__PISTORE_STR2: ; Indirect store temporary string at (IX + BC) - push ix - pop hl - add hl, bc - -__ISTORE_STR2: - ld c, (hl) ; Dereferences HL - inc hl - ld h, (hl) - ld l, c ; HL = *HL (real string variable address) - -__STORE_STR2: - push hl - ld c, (hl) - inc hl - ld h, (hl) - ld l, c ; HL = *HL (real string address) - - push de - call __MEM_FREE - pop de - - pop hl - ld (hl), e - inc hl - ld (hl), d - dec hl ; HL points to mem address variable. This might be useful in the future. - - ret - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/str.asm b/src/lib/arch/zxnext/runtime/str.asm index a3cc9b8d1..c3a18fb83 100644 --- a/src/lib/arch/zxnext/runtime/str.asm +++ b/src/lib/arch/zxnext/runtime/str.asm @@ -1,80 +1,8 @@ -; The STR$( ) BASIC function implementation - -; Given a FP number in C ED LH -; Returns a pointer (in HL) to the memory heap -; containing the FP number string representation - -#include once -#include once -#include once - - push namespace core - -__STR: - -__STR_FAST: - - PROC - LOCAL __STR_END - LOCAL RECLAIM2 - LOCAL STK_END - - ld hl, (STK_END) - push hl; Stores STK_END - ld hl, (ATTR_T) ; Saves ATTR_T since it's changed by STR$ due to a ROM BUG - push hl - - call __FPSTACK_PUSH ; Push number into stack - rst 28h ; # Rom Calculator - defb 2Eh ; # STR$(x) - defb 38h ; # END CALC - call __FPSTACK_POP ; Recovers string parameters to A ED CB (Only ED LH are important) - - pop hl - ld (ATTR_T), hl ; Restores ATTR_T - pop hl - ld (STK_END), hl ; Balance STK_END to avoid STR$ bug - - push bc - push de - - inc bc - inc bc - call __MEM_ALLOC ; HL Points to new block - - pop de - pop bc - - push hl - ld a, h - or l - jr z, __STR_END ; Return if NO MEMORY (NULL) - - push bc - push de - ld (hl), c - inc hl - ld (hl), b - inc hl ; Copies length - - ex de, hl ; HL = start of original string - ldir ; Copies string content - - pop de ; Original (ROM-CALC) string - pop bc ; Original Length - -__STR_END: - ex de, hl - inc bc - - call RECLAIM2 ; Frees TMP Memory - pop hl ; String result - - ret - -RECLAIM2 EQU 19E8h -STK_END EQU 5C65h - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/strcat.asm b/src/lib/arch/zxnext/runtime/strcat.asm index 1d1ed1829..cac2544e5 100644 --- a/src/lib/arch/zxnext/runtime/strcat.asm +++ b/src/lib/arch/zxnext/runtime/strcat.asm @@ -1,129 +1,8 @@ -#include once -#include once - - push namespace core - -__ADDSTR: ; Implements c$ = a$ + b$ - ; hl = &a$, de = &b$ (pointers) - - -__STRCAT2: ; This routine creates a new string in dynamic space - ; making room for it. Then copies a$ + b$ into it. - ; HL = a$, DE = b$ - - PROC - - LOCAL __STR_CONT - LOCAL __STRCATEND - - push hl - call __STRLEN - ld c, l - ld b, h ; BC = LEN(a$) - ex (sp), hl ; (SP) = LEN (a$), HL = a$ - push hl ; Saves pointer to a$ - - inc bc - inc bc ; +2 bytes to store length - - ex de, hl - push hl - call __STRLEN - ; HL = len(b$) - - add hl, bc ; Total str length => 2 + len(a$) + len(b$) - - ld c, l - ld b, h ; BC = Total str length + 2 - call __MEM_ALLOC - pop de ; HL = c$, DE = b$ - - ex de, hl ; HL = b$, DE = c$ - ex (sp), hl ; HL = a$, (SP) = b$ - - exx - pop de ; D'E' = b$ - exx - - pop bc ; LEN(a$) - - ld a, d - or e - ret z ; If no memory: RETURN - -__STR_CONT: - push de ; Address of c$ - - ld a, h - or l - jr nz, __STR_CONT1 ; If len(a$) != 0 do copy - - ; a$ is NULL => uses HL = DE for transfer - ld h, d - ld l, e - ld (hl), a ; This will copy 00 00 at (DE) location - inc de ; - dec bc ; Ensure BC will be set to 1 in the next step - -__STR_CONT1: ; Copies a$ (HL) into c$ (DE) - inc bc - inc bc ; BC = BC + 2 - ldir ; MEMCOPY: c$ = a$ - pop hl ; HL = c$ - - exx - push de ; Recovers b$; A ex hl,hl' would be very handy - exx - - pop de ; DE = b$ - -__STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ - ; NOTE: Both DE, BC and AF are modified and lost - ; Returns HL (pointer to a$) - ; a$ Must be NOT NULL - ld a, d - or e - ret z ; Returns if de is NULL (nothing to copy) - - push hl ; Saves HL to return it later - - ld c, (hl) - inc hl - ld b, (hl) - inc hl - add hl, bc ; HL = end of (a$) string ; bc = len(a$) - push bc ; Saves LEN(a$) for later - - ex de, hl ; DE = end of string (Begin of copy addr) - ld c, (hl) - inc hl - ld b, (hl) ; BC = len(b$) - - ld a, b - or c - jr z, __STRCATEND; Return if len(b$) == 0 - - push bc ; Save LEN(b$) - inc hl ; Skip 2nd byte of len(b$) - ldir ; Concatenate b$ - - pop bc ; Recovers length (b$) - pop hl ; Recovers length (a$) - add hl, bc ; HL = LEN(a$) + LEN(b$) = LEN(a$+b$) - ex de, hl ; DE = LEN(a$+b$) - pop hl - - ld (hl), e ; Updates new LEN and return - inc hl - ld (hl), d - dec hl - ret - -__STRCATEND: - pop hl ; Removes Len(a$) - pop hl ; Restores original HL, so HL = a$ - ret - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/strcpy.asm b/src/lib/arch/zxnext/runtime/strcpy.asm index d864623d2..5b2e6463b 100644 --- a/src/lib/arch/zxnext/runtime/strcpy.asm +++ b/src/lib/arch/zxnext/runtime/strcpy.asm @@ -1,99 +1,8 @@ -#include once - -; String library - - - push namespace core - -__STRASSIGN: ; Performs a$ = b$ (HL = address of a$; DE = Address of b$) - PROC - - LOCAL __STRREALLOC - LOCAL __STRCONTINUE - LOCAL __B_IS_NULL - LOCAL __NOTHING_TO_COPY - - ld b, d - ld c, e - ld a, b - or c - jr z, __B_IS_NULL - - ex de, hl - ld c, (hl) - inc hl - ld b, (hl) - dec hl ; BC = LEN(b$) - ex de, hl ; DE = &b$ - -__B_IS_NULL: ; Jumps here if B$ pointer is NULL - inc bc - inc bc ; BC = BC + 2 ; (LEN(b$) + 2 bytes for storing length) - - push de - push hl - - ld a, h - or l - jr z, __STRREALLOC - - dec hl - ld d, (hl) - dec hl - ld e, (hl) ; DE = MEMBLOCKSIZE(a$) - dec de - dec de ; DE = DE - 2 ; (Membloksize takes 2 bytes for memblock length) - - ld h, b - ld l, c ; HL = LEN(b$) + 2 => Minimum block size required - ex de, hl ; Now HL = BLOCKSIZE(a$), DE = LEN(b$) + 2 - - or a ; Prepare to subtract BLOCKSIZE(a$) - LEN(b$) - sbc hl, de ; Carry if len(b$) > Blocklen(a$) - jr c, __STRREALLOC ; No need to realloc - ; Need to reallocate at least to len(b$) + 2 - ex de, hl ; DE = Remaining bytes in a$ mem block. - ld hl, 4 - sbc hl, de ; if remaining bytes < 4 we can continue - jr nc,__STRCONTINUE ; Otherwise, we realloc, to free some bytes - -__STRREALLOC: - pop hl - call __REALLOC ; Returns in HL a new pointer with BC bytes allocated - push hl - -__STRCONTINUE: ; Pops hl and de SWAPPED - pop de ; DE = &a$ - pop hl ; HL = &b$ - - ld a, d ; Return if not enough memory for new length - or e - ret z ; Return if DE == NULL (0) - -__STRCPY: ; Copies string pointed by HL into string pointed by DE - ; Returns DE as HL (new pointer) - ld a, h - or l - jr z, __NOTHING_TO_COPY - ld c, (hl) - inc hl - ld b, (hl) - dec hl - inc bc - inc bc - push de - ldir - pop hl - ret - -__NOTHING_TO_COPY: - ex de, hl - ld (hl), e - inc hl - ld (hl), d - dec hl - ret - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/strictbool.asm b/src/lib/arch/zxnext/runtime/strictbool.asm index 80a7460a4..ab3cb5888 100644 --- a/src/lib/arch/zxnext/runtime/strictbool.asm +++ b/src/lib/arch/zxnext/runtime/strictbool.asm @@ -1,12 +1,8 @@ -; This routine is called if --strict-boolean was set at the command line. -; It will make any boolean result to be always 0 or 1 +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - push namespace core - -__NORMALIZE_BOOLEAN: - sub 1 - sbc a, a - inc a - ret - - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/strlen.asm b/src/lib/arch/zxnext/runtime/strlen.asm index cb4a880af..f02bd8700 100644 --- a/src/lib/arch/zxnext/runtime/strlen.asm +++ b/src/lib/arch/zxnext/runtime/strlen.asm @@ -1,20 +1,8 @@ -; Returns len if a string -; If a string is NULL, its len is also 0 -; Result returned in HL - - push namespace core - -__STRLEN: ; Direct FASTCALL entry - ld a, h - or l - ret z - - ld a, (hl) - inc hl - ld h, (hl) ; LEN(str) in HL - ld l, a - ret - - pop namespace - - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/strslice.asm b/src/lib/arch/zxnext/runtime/strslice.asm index c63ad4e47..8d9a4d425 100644 --- a/src/lib/arch/zxnext/runtime/strslice.asm +++ b/src/lib/arch/zxnext/runtime/strslice.asm @@ -1,109 +1,8 @@ -; String slicing library -; HL = Str pointer -; DE = String start -; BC = String character end -; A register => 0 => the HL pointer wont' be freed from the HEAP -; e.g. a$(5 TO 10) => HL = a$; DE = 5; BC = 10 - -; This implements a$(X to Y) being X and Y first and -; last characters respectively. If X > Y, NULL is returned - -; Otherwise returns a pointer to a$ FROM X to Y (starting from 0) -; if Y > len(a$), then a$ will be padded with spaces (reallocating -; it in dynamic memory if needed). Returns pointer (HL) to resulting -; string. NULL (0) if no memory for padding. +; ---------------------------------------------------------------- +; This file is released under the MIT License ; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- -#include once -#include once -#include once - - push namespace core - -__STRSLICE: ; Callee entry - pop hl ; Return ADDRESS - pop bc ; Last char pos - pop de ; 1st char pos - ex (sp), hl ; CALLEE. -> String start - -__STRSLICE_FAST: ; __FASTCALL__ Entry - PROC - - LOCAL __CONT - LOCAL __EMPTY - LOCAL __FREE_ON_EXIT - - push hl ; Stores original HL pointer to be recovered on exit - ex af, af' ; Saves A register for later - - push hl - call __STRLEN - inc bc ; Last character position + 1 (string starts from 0) - or a - sbc hl, bc ; Compares length with last char position - jr nc, __CONT ; If Carry => We must copy to end of string - add hl, bc ; Restore back original LEN(a$) in HL - ld b, h - ld c, l ; Copy to the end of str - ccf ; Clears Carry flag for next subtraction - -__CONT: - ld h, b - ld l, c ; HL = Last char position to copy (1 for char 0, 2 for char 1, etc) - sbc hl, de ; HL = LEN(a$) - DE => Number of chars to copy - jr z, __EMPTY ; 0 Chars to copy => Return HL = 0 (NULL STR) - jr c, __EMPTY ; If Carry => Nothing to return (NULL STR) - - ld b, h - ld c, l ; BC = Number of chars to copy - inc bc - inc bc ; +2 bytes for string length number - - push bc - push de - call __MEM_ALLOC - pop de - pop bc - ld a, h - or l - jr z, __EMPTY ; Return if NULL (no memory) - - dec bc - dec bc ; Number of chars to copy (Len of slice) - - ld (hl), c - inc hl - ld (hl), b - inc hl ; Stores new string length - - ex (sp), hl ; Pointer to A$ now in HL; Pointer to new string chars in Stack - inc hl - inc hl ; Skip string length - add hl, de ; Were to start from A$ - pop de ; Start of new string chars - push de ; Stores it again - ldir ; Copies BC chars - pop de - dec de - dec de ; Points to String LEN start - ex de, hl ; Returns it in HL - jr __FREE_ON_EXIT - -__EMPTY: ; Return NULL (empty) string - pop hl - ld hl, 0 ; Return NULL - - -__FREE_ON_EXIT: - ex af, af' ; Recover original A register - ex (sp), hl ; Original HL pointer - - or a - call nz, __MEM_FREE - - pop hl ; Recover result - ret - - ENDP - - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/swap32.asm b/src/lib/arch/zxnext/runtime/swap32.asm index d615c7127..3158f67a3 100644 --- a/src/lib/arch/zxnext/runtime/swap32.asm +++ b/src/lib/arch/zxnext/runtime/swap32.asm @@ -1,20 +1,8 @@ -; Exchanges current DE HL with the -; ones in the stack - - push namespace core - -__SWAP32: - pop bc ; Return address - ex (sp), hl - inc sp - inc sp - ex de, hl - ex (sp), hl - ex de, hl - dec sp - dec sp - push bc - ret - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/table_jump.asm b/src/lib/arch/zxnext/runtime/table_jump.asm index 370d2faf8..b58b92254 100644 --- a/src/lib/arch/zxnext/runtime/table_jump.asm +++ b/src/lib/arch/zxnext/runtime/table_jump.asm @@ -1,21 +1,8 @@ - - push namespace core - -JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A - add a, a - -JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE - ld e, a - ld d, 0 - -JUMP_HL_PLUS_DE: ; Does JP (HL + DE) - add hl, de - ld e, (hl) - inc hl - ld d, (hl) - ex de, hl -CALL_HL: - jp (hl) - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/u32tofreg.asm b/src/lib/arch/zxnext/runtime/u32tofreg.asm index 19adf7cb7..ed808145c 100644 --- a/src/lib/arch/zxnext/runtime/u32tofreg.asm +++ b/src/lib/arch/zxnext/runtime/u32tofreg.asm @@ -1,93 +1,8 @@ -#include once - push namespace core - -__I8TOFREG: - ld l, a - rlca - sbc a, a ; A = SGN(A) - ld h, a - ld e, a - ld d, a - -__I32TOFREG: ; Converts a 32bit signed integer (stored in DEHL) - ; to a Floating Point Number returned in (A ED CB) - - ld a, d - or a ; Test sign - - jp p, __U32TOFREG ; It was positive, proceed as 32bit unsigned - - call __NEG32 ; Convert it to positive - call __U32TOFREG ; Convert it to Floating point - - set 7, e ; Put the sign bit (negative) in the 31bit of mantissa - ret - -__U8TOFREG: - ; Converts an unsigned 8 bit (A) to Floating point - ld l, a - ld h, 0 - ld e, h - ld d, h - -__U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) - ; to a Floating point number returned in A ED CB - - PROC - - LOCAL __U32TOFREG_END - - ld a, d - or e - or h - or l - ld b, d - ld c, e ; Returns 00 0000 0000 if ZERO - ret z - - push de - push hl - - exx - pop de ; Loads integer into B'C' D'E' - pop bc - exx - - ld l, 128 ; Exponent - ld bc, 0 ; DEBC = 0 - ld d, b - ld e, c - -__U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG - exx - ld a, d ; B'C'D'E' == 0 ? - or e - or b - or c - jp z, __U32TOFREG_END ; We are done - - srl b ; Shift B'C' D'E' >> 1, output bit stays in Carry - rr c - rr d - rr e - exx - - rr e ; Shift EDCB >> 1, inserting the carry on the left - rr d - rr c - rr b - - inc l ; Increment exponent - jp __U32TOFREG_LOOP - - -__U32TOFREG_END: - exx - ld a, l ; Puts the exponent in a - res 7, e ; Sets the sign bit to 0 (positive) - - ret - ENDP - - pop namespace - +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/usr.asm b/src/lib/arch/zxnext/runtime/usr.asm index 12e89485d..c5e3b276d 100644 --- a/src/lib/arch/zxnext/runtime/usr.asm +++ b/src/lib/arch/zxnext/runtime/usr.asm @@ -1,21 +1,8 @@ -; Emulates the USR Sinclair BASIC function -; Result value returns in BC -; We use HL for returning values, su we must -; copy BC into HL before returning +; ---------------------------------------------------------------- +; This file is released under the MIT License ; -; The incoming parameter is HL (Address to JUMP) -; - -#include once - - push namespace core - -USR: - push ix ; must preserve IX - call CALL_HL - pop ix - ld h, b - ld l, c - ret +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- - pop namespace +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/usr_str.asm b/src/lib/arch/zxnext/runtime/usr_str.asm index 2753397fa..0401a8798 100644 --- a/src/lib/arch/zxnext/runtime/usr_str.asm +++ b/src/lib/arch/zxnext/runtime/usr_str.asm @@ -1,73 +1,8 @@ -; This function just returns the address of the UDG of the given str. -; If the str is EMPTY or not a letter, 0 is returned and ERR_NR set -; to "A: Invalid Argument" - -; On entry HL points to the string -; and A register is non-zero if the string must be freed (TMP string) - -#include once -#include once -#include once - - push namespace core - -USR_STR: - PROC - ex af, af' ; Saves A flag - - ld a, h - or l - jr z, USR_ERROR ; a$ = NULL => Invalid Arg - - ld d, h ; Saves HL in DE, for - ld e, l ; later usage - - ld c, (hl) - inc hl - ld a, (hl) - or c - jr z, USR_ERROR ; a$ = "" => Invalid Arg - - inc hl - ld a, (hl) ; Only the 1st char is needed - and %11011111 ; Convert it to UPPER CASE - sub 144 ; CODE(UDG "A") - jr nc, CONT - add a, 144 ; It was a letter - sub 'A' - - LOCAL CONT -CONT: - - ld l, a - ld h, 0 - add hl, hl - add hl, hl - add hl, hl ; hl = A * 8 - ld bc, (UDG) - add hl, bc - - ;; Now checks if the string must be released - ex af, af' ; Recovers A flag - or a - ret z ; return if not - - push hl ; saves result since __MEM_FREE changes HL - ex de, hl ; Recovers original HL value - call __MEM_FREE - pop hl - ret - -USR_ERROR: - ex de, hl ; Recovers original HL value - ex af, af' ; Recovers A flag - or a - call nz, __MEM_FREE - - ld a, ERROR_InvalidArg - ld (ERR_NR), a - ld hl, 0 - ret - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/runtime/val.asm b/src/lib/arch/zxnext/runtime/val.asm index 988b06907..6ec2ee9e5 100644 --- a/src/lib/arch/zxnext/runtime/val.asm +++ b/src/lib/arch/zxnext/runtime/val.asm @@ -1,120 +1,8 @@ -#include once -#include once -#include once - - push namespace core - -VAL: ; Computes VAL(a$) using ROM FP-CALC - ; HL = address of a$ - ; Returns FP number in C ED LH registers - ; A Register = 1 => Free a$ on return - - PROC - - LOCAL STK_STO_S - LOCAL __RET_ZERO - LOCAL ERR_SP - LOCAL STKBOT - LOCAL RECLAIM1 - LOCAL CH_ADD - LOCAL __VAL_ERROR - LOCAL __VAL_EMPTY - LOCAL SET_MIN - -RECLAIM1 EQU 6629 -STKBOT EQU 23651 -ERR_SP EQU 23613 -CH_ADD EQU 23645 -STK_STO_S EQU 2AB2h -SET_MIN EQU 16B0h - - ld d, a ; Preserves A register in DE - ld a, h - or l - jr z, __RET_ZERO ; NULL STRING => Return 0 - - push de ; Saves A Register (now in D) - push hl ; Not null string. Save its address for later - - ld c, (hl) - inc hl - ld b, (hl) - inc hl - - ld a, b - or c - jr z, __VAL_EMPTY ; Jumps VAL_EMPTY on empty string - - ex de, hl ; DE = String start - - ld hl, (CH_ADD) - push hl - - ld hl, (STKBOT) - push hl - - ld hl, (ERR_SP) - push hl - - ;; Now put our error handler on ERR_SP - ld hl, __VAL_ERROR - push hl - ld (ERR_SP), sp - - call STK_STO_S ; Enter it on the stack - - ld b, 1Dh ; "VAL" - rst 28h ; ROM CALC - defb 1Dh ; VAL - defb 38h ; END CALC - - pop hl ; Discards our current error handler - pop hl - ld (ERR_SP), hl ; Restores ERR_SP - - pop de ; old STKBOT - ld hl, (STKBOT) ; current SKTBOT - call RECLAIM1 ; Recover unused space - - pop hl ; Discards old CH_ADD value - pop hl ; String pointer - pop af ; Deletion flag - or a - call nz, __MEM_FREE ; Frees string content before returning - - ld a, ERROR_Ok ; Sets OK in the result - ld (ERR_NR), a - - jp __FPSTACK_POP ; Recovers result and return from there - -__VAL_ERROR: ; Jumps here on ERROR - pop hl - ld (ERR_SP), hl ; Restores ERR_SP - - ld hl, (STKBOT) ; current SKTBOT - pop de ; old STKBOT - pop hl - ld (CH_ADD), hl ; Recovers old CH_ADD - - call 16B0h ; Resets temporary areas after an error - -__VAL_EMPTY: ; Jumps here on empty string - pop hl ; Recovers initial string address - pop af ; String flag: If not 0 => it's temporary - or a - call nz, __MEM_FREE ; Frees "" string - -__RET_ZERO: ; Returns 0 Floating point on error - ld a, ERROR_InvalidArg - ld (ERR_NR), a - - xor a - ld b, a - ld c, a - ld d, b - ld e, c - ret - - ENDP - - pop namespace +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/IM2.bas b/src/lib/arch/zxnext/stdlib/IM2.bas index 86147f2b2..c3b9d683e 100644 --- a/src/lib/arch/zxnext/stdlib/IM2.bas +++ b/src/lib/arch/zxnext/stdlib/IM2.bas @@ -1,122 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2023 -' by Juan Segura (a.k.a. Duefectu) -' -' Mode 2 interrupt handling library -' ---------------------------------------------------------------- - -#ifndef __LIBRARY_IM2__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_IM2__ - - -' ---------------------------------------------------------------- -' IM2Start configures and initialises the interrupt service. -' At each interruption the address "callBack" shall be called. -' When this service is activated, the FRAMES counter and the -' PAUSE and INKEY$ instructions no longer work correctly. -' Not working with "INVES +" models -' -' Sample of use: -' IM2Start(@MyIM2Routine) ' Use @ -' SUB MyIM2Routine() -' ' Do something not too complicated -' END SUB -' -' Parameters: -' callback (UInteger): Address to jump on every interrupt -' ---------------------------------------------------------------- -sub IM2Start(callBack AS UInteger) -ASM - PROC - LOCAL IM2_Table, IM2_Tick, IM2_Tick_End, IM2_End, IM2_CallBack - - ld [IM2_CallBack],hl - - di ; Disable interrupts - ld hl,IM2_Table-256 ; The end of the IM2 table - ld a,h ; Adjust the value of I at the end of the table - ld i,a - - ld hl,IM2_Tick ; Set IM2_Tick address - ld a,l ; Al final de la tabla - ld (IM2_Table-1),a - ld a,h - ld (IM2_Table),a - - im 2 ; Set interrupt mode to 2 - ei ; Enable interrupts - jp IM2_End ; Jump to the end to exit - - - ; Here you will jump at every interrupt -IM2_Tick: - ; Save all registers - push af - push hl - push bc - push de - push ix - push iy - exx - ex af, af' - push af - push hl - push bc - push de - - ld hl,IM2_Tick_End ; Set return address - push hl - ld hl,[IM2_CallBack] ; Jump to callBack routine - jp [hl] - -IM2_Tick_End: - ; Restore registers - pop de - pop bc - pop hl - pop af - ex af, af' - exx - pop iy - pop ix - pop de - pop bc - pop hl - pop af - - ei ; Enable interrupts - reti ; Return from interrupt - - - ; Space for the interrupt vector table - ; Must start at an address multiple of 256 - db 0 ; The first jump value is in $ff - ALIGN 256 ; Align to 256 -IM2_Table: - db 0 ; The second jump value is $00 - -IM2_CallBack: ; CallBack address - DW 0 - -IM2_End: - ENDP -END ASM -end sub - - -' ---------------------------------------------------------------- -' IM2Stop stops the interrupt service and restores ROM interrupts -' ---------------------------------------------------------------- -sub IM2Stop() -ASM - di ; Disable interrupts - im 1 ; Set interrupt mode 1 (ROM) - ei ; Enable interrupts -END ASM -end sub -#endif +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/README b/src/lib/arch/zxnext/stdlib/README index 401199747..7d24b7e8a 100644 --- a/src/lib/arch/zxnext/stdlib/README +++ b/src/lib/arch/zxnext/stdlib/README @@ -1,4 +1,4 @@ -This directory contains a library of "basic" BASIC functions. -Some of them are related to ZX Spectrum BASIC (i.e. ATTR) while -others are more standard to FREE BASIC (csrlin or pos) - +This directory contains a library of "basic" BASIC functions. +Some of them are related to ZX Spectrum BASIC (i.e. ATTR) while +others are more standard to FREE BASIC (csrlin or pos) + diff --git a/src/lib/arch/zxnext/stdlib/SP/Fill.bas b/src/lib/arch/zxnext/stdlib/SP/Fill.bas index 3c979aee7..03df2a239 100644 --- a/src/lib/arch/zxnext/stdlib/SP/Fill.bas +++ b/src/lib/arch/zxnext/stdlib/SP/Fill.bas @@ -1,545 +1,8 @@ -' The SPLib fill routine (by Alvin Albretch, 2002-2012) -' ported to ZX Basic by Britlion (a.k.a. Paul Fisher) -' - -REM Avoid double inclusion -#ifndef __SP_FILL__ -#define __SP_FILL__ - - -SUB FASTCALL SPFill(xCoord as uByte, yCoord as uByte, fillPatternAddress as uInteger) -asm -push namespace core - -PROC -LOCAL SPPFill -LOCAL SPPFill_start -LOCAL SPPFill_end - -POP BC ; return address -POP HL ; Gets Y coord into H -POP DE ; Gets Fill pattern into DE -LD L,A ; Gets Y,X into HL -PUSH BC ; Puts return address back -push ix -call SPPFill_start -pop ix -ret -; -; SPFill -; - -; Patterned Flood Fill -; Alvin Albrecht 2002 -; Tweaked to work in ZX Basic by Britlion, 2012. -; -; This subroutine does a byte-at-a-time breadth-first patterned flood fill. -; It works by allocating a circular queue on the stack, with the size of the -; queue determined by an input parameter. The queue is divided into three -; contiguous blocks: the pattern block, the investigate block and the new block. -; The queue contains 3-byte structures (described below) with a special structure -; delimiting each block. The physical end of the queue is marked with a special -; byte. The contents of the queue grow down in memory. -; -; The pattern block holds pixels that have been blackened on screen and are -; only waiting to have the pattern applied to them before they are removed -; from the queue. -; -; The investigate block holds pixels that have been blackened on screen and -; are waiting to be investigated before they become part of the pattern -; block. 'Investigating' a pixel means trying to expand the fill in all -; four directions. Any successful expansion causes the new pixel to be -; added to the new block. -; -; The new block holds pixels that have been blackened on screen and are -; waiting to become part of the investigate block. The new block expands -; as the investigate block is investigated. -; -; The pattern fill algorithm follows these steps: -; 1. Examine the investigate block. Add new pixels to the new block if an -; expansion is possible. -; 2. Pattern the pattern block. All pixels waiting to be patterned are -; patterned on screen. -; 3. The investigate block becomes the pattern block. -; The new block becomes the investigate block. -; 4. Repeat until the investigate block is empty. -; -; The algorithm may bail out prematurely if the queue fills up. Bailing -; out causes any pending pixels in the queue to be patterned before the -; subroutine exits. If PFILL would continue to run by refusing to enter -; new pixels when the queue is full, there would be no guarantee that -; the subroutine would ever return. -; -; In English, the idea behind the patterned flood fill is simple. The -; start pixel grows out in a circular shape (actually a diamond). A -; fill boundary two pixels thick is maintained in that circular shape. -; The outermost boundary is the frontier, and is where the flood fill -; is growing from (ie the investigate block). The inner boundary is -; the pattern block, waiting to be patterned. A solid inner boundary -; is necessary to prevent the flood-fill frontier pixels from growing -; back toward the starting pixel through holes in the pattern. Once -; the frontier pixels are investigated, the innermost boundary is -; patterned. The newly investigated pixels become the outermost -; boundary (the investigate block) and the old investigate block becomes -; the new pattern block. -; -; Each entry in the queue is a 3-byte struct that grows down in memory: -; screen address (2-bytes, MSB first) -; fill byte (1-byte) -; A screen address with MSB < 0x40 is used to indicate the end of a block. -; A screen address with MSB >= 0x80 is used to mark the physical end of the Q. -; -; The fill pattern is a typical 8x8 pixel character, stored in 8 bytes. - -; enter: h = y coord, l = x coord, bc = max stack depth, de = address of fill pattern -; In hi-res mode, carry flag is most significant bit of x coord -; used : ix, af, bc, de, hl -; exit : no carry = success, carry = had to bail queue was too small -; stack: 3*bc+30 bytes, not including the call to PFILL or interrupts -SPPFill_IXBuffer: -DEFB 0,0 ; buffer - -SPPFill_start: -LD BC,300 ; Bytes allowed in stack. -LD (SPPFill_IXBuffer),IX - -; enter: h = y coord, l = x coord, bc = max stack depth, de = address of fill pattern -; In hi-res mode, carry flag is most significant bit of x coord - - -SPPFill: - push de ; save (pattern pointer) variable - dec bc ; we will start with one struct in the queue - push bc ; save max stack depth variable - - call SPGetScrnAddr ; de = screen address, b = pixel byte - ex de,hl ; hl = screen address - - call bytefill ; b = fill byte - jr c, viable - pop bc - pop de - jp SPPFill_end ; quit - not viable. - - LOCAL viable -viable: - ex de,hl ; de = screen address, b = fill byte - ld hl,-7 - add hl,sp - push hl ; create pattern block pointer = top of queue - push hl - pop ix ; ix = top of queue - dec hl - dec hl - dec hl - push hl ; create investigate block pointer - ld hl,-12 - add hl,sp - push hl ; create new block pointer - - xor a - push af - dec sp ; mark end of pattern block - push de ; screen address and fill byte are - push bc ; first struct in investigate block - inc sp - push af - dec sp ; mark end of investigate block - - ld c,(ix+7) - ld b,(ix+8) ; bc = max stack depth - 1 - inc bc - ld l,c - ld h,b - add hl,bc ; space required = 3*BC (max depth) + 10 - add hl,bc ; but have already taken 9 bytes - ld c,l - ld b,h ; bc = # uninitialized bytes in queue - ld hl,0 - sbc hl,bc ; negate hl, additions above will not set carry - add hl,sp - ld (hl),0 ; zero last byte in queue - ld sp,hl ; move stack below queue - ld a,$80 - push af ; mark end of queue with $80 byte - inc sp - ld e,l - ld d,h - inc de - dec bc - ldir ; zero the uninitialized bytes in queue - -; NOTE: Must move the stack before clearing the queue, otherwise if an interrupt -; occurred, garbage could overwrite portions of the (just cleared) queue. - -; ix = top of queue, bottom of queue marked with 0x80 byte - -; Variables indexed by ix, LSB first: -; ix + 11/12 return address -; ix + 09/10 fill pattern pointer -; ix + 07/08 max stack depth -; ix + 05/06 pattern block pointer -; ix + 03/04 investigate block pointer -; ix + 01/02 new block pointer - -; A picture of memory at this point: -; -;+-----------------------+ higher addresses -;| | | -;|- return address -| \|/ -;| | V -;+-----------------------+ lower addresses -;| fill | -;|- pattern pointer -| -;| | -;+-----------------------+ -;| | -;|- max stack depth -| -;| | -;+-----------------------+ -;| | -;|- pattern block -| -;| | -;+-----------------------+ -;| | -;|- investigate block -| -;| | -;+-----------------------+ -;| | -;|- new block -| -;| | -;+-----------------------+ -;| end of block marker | <- ix = pattern block = top of queue -;| ? | -;| ? | -;+-----------------------+ -;| screen address MSB | <- investigate block -;| screen address LSB | -;| fill byte | -;+-----------------------+ -;| end of block marker | -;| ? | -;| ? | -;+-----------------------+ -;| 0 | <- new block -;| 0 | -;| 0 | -;+-----------------------+ -;| | -;| ...... | size is a multiple of 3 bytes -;| rest of queue | -;| all zeroed | -;| ...... | -;| | -;+-----------------------+ -;| 0x80 | <- sp, special byte marks end of queue -;+-----------------------+ - - LOCAL pfloop -pfloop: - ld l,(ix+3) - ld h,(ix+4) ; hl = investigate block - ld e,(ix+1) - ld d,(ix+2) ; de = new block - call investigate - ld (ix+1),e - ld (ix+2),d ; save new block - ld (ix+3),l - ld (ix+4),h ; save investigate block - - ld l,(ix+5) - ld h,(ix+6) ; hl = pattern block - ld c,(ix+7) - ld b,(ix+8) ; bc = max stack depth (available space) - call applypattern - ld (ix+7),c - ld (ix+8),b ; save stack depth - ld (ix+5),l - ld (ix+6),h ; save pattern block - - ld a,(hl) ; done if the investigate block was empty - cp 40h - jp nc, pfloop - - LOCAL endpfill -endpfill: - ld de,11 ; return address is at ix+11 - add ix,de - ld sp,ix - or a ; make sure carry is clear, indicating success - ret - -; IN/OUT: hl = investigate block, de = new block - - LOCAL investigate -investigate: - ld a,(hl) - cp 80h ; bit 15 of screen addr set if time to wrap - jp c, inowrap - push ix - pop hl ; hl = ix = top of queue - ld a,(hl) - - LOCAL inowrap -inowrap: - cp 40h ; screen address < 0x4000 marks end of block - jp c, endinv ; are we done yet? - ld b,a - dec hl - ld c,(hl) ; bc = screen address - dec hl - ld a,(hl) ; a = fill byte - dec hl - push hl ; save spot in investigate block - ld l,c - ld h,b ; hl = screen address - ld b,a ; b = fill byte - - LOCAL goup -goup: - push hl ; save screen address - call SP.PixelUp ; move screen address up one pixel - jr c, updeadend ; if went off-screen - push bc ; save fill byte - call bytefill - call c, addnew ; if up is not dead end, add this to new block - pop bc ; restore fill byte - - LOCAL updeadend -updeadend: - pop hl ; restore screen address - - LOCAL godown -godown: - push hl ; save screen address - call SP.PixelDown ; move screen address down one pixel - jr c, downdeadend - push bc ; save fill byte - call bytefill - call c, addnew ; if down is not dead end, add this to new block - pop bc ; restore fill byte - - LOCAL downdeadend -downdeadend: - pop hl ; restore screen address - - LOCAL goleft -goleft: - bit 7,b ; can only move left if leftmost bit of fill byte set - jr z, goright - ld a,l - and 31 - jr nz, okleft - bit 5,h ; for hi-res mode: column = 1 if l=0 and bit 5 of h is set - jr z, goright - - LOCAL okleft -okleft: - push hl ; save screen address - call SP.CharLeft - push bc ; save fill byte - ld b,01h ; set rightmost pixel for incoming byte - call bytefill - call c, addnew ; if left is not dead end, add this to new block - pop bc ; restore fill byte - pop hl ; restore screen address - - LOCAL goright -goright: - bit 0,b ; can only move right if rightmost bit of fill byte set - jr z, nextinv - or a ; clear carry - call SP.CharRight - jr c, nextinv ; went off screen - ld a,l - and 31 - jr z, nextinv ; wrapped around line - ld b,80h ; set leftmost pixel for incoming byte - call bytefill - call c, addnew ; if right is not dead end, add this to new block - - LOCAL nextinv -nextinv: - pop hl ; hl = spot in investigate block - jp investigate - - LOCAL endinv -endinv: - dec hl - dec hl - dec hl ; investigate block now points at new block - - ld a,(de) ; check if new block is at end of queue - cp 80h - jr c, nowrapnew - defb $dd - ld e,l - defb $dd - ld d,h ; de = ix = top of queue - - LOCAL nowrapnew -nowrapnew: - xor a - ld (de),a ; store end marker for new block - dec de - dec de - dec de - ret - -; enter b = incoming byte, hl = screen address -; exit b = fill byte, screen blackened with fill byte - - LOCAL bytefill -bytefill: - ld a,b - xor (hl) ; zero out incoming pixels that - and b ; run into set pixels in display - ret z - - LOCAL bfloop -bfloop: - ld b,a - rra ; expand incoming pixels - ld c,a ; to the right and left - ld a,b ; within byte - add a,a - or c - or b - ld c,a - xor (hl) ; zero out pixels that run into - and c ; set pixels on display - cp b - jp nz, bfloop ; keep going until incoming byte does not change - - or (hl) - ld (hl),a ; fill byte on screen - scf ; indicate that this was a viable step - ret - -; add incoming fill byte and screen address to new block -; enter b = incoming byte, hl = screen address, de = new block - - LOCAL addnew -addnew: - push hl ; save screen address - ld l,(ix+7) - ld h,(ix+8) ; hl = max stack depth - ld a,h - or l - jr z, bail ; no space in queue so bail! - dec hl ; available queue space decreases by one struct - ld (ix+7),l - ld (ix+8),h - pop hl ; hl = screen address - - ld a,(de) ; check if new block is at end of queue - cp 80h - jr c, annowrap - defb $dd - ld e,l - defb $dd - ld d,h ; de = ix = top of queue - - LOCAL annowrap -annowrap: - ex de,hl - ld (hl),d ; make struct, store screen address (2 bytes) - dec hl - ld (hl),e - dec hl - ld (hl),b ; store fill byte (1 byte) - dec hl - ex de,hl - ret - -; if the queue filled up, we need to bail. Bailing means patterning any set pixels -; which may still be on the display. If we didnt bail and tried to trudge along, -; there is no guarantee the fill would ever return. - - LOCAL bail -bail: - pop hl ; hl = screen address, b = fill byte - ld a,b - xor (hl) - ld (hl),a ; clear this byte on screen - - xor a - ld (de),a ; mark end of new block - - ld l,(ix+5) - ld h,(ix+6) ; hl = pattern block - call applypattern ; for pattern block - call applypattern ; for investigate block - call applypattern ; for new block - - ld de,11 ; return address is at ix+11 - add ix,de - ld sp,ix - scf ; indicate we had to bail - jp SPPFill_end - -; hl = pattern block, bc = max stack depth (available space) - - LOCAL applypattern -applypattern: - ld a,(hl) - cp 80h ; bit 15 of screen addr set if time to wrap - jp c, apnowrap - push ix - pop hl ; hl = ix = top of queue - ld a,(hl) - - LOCAL apnowrap -apnowrap: - cp 40h ; screen address < 0x4000 marks end of block - jr c, endapply ; are we done yet? - - and 07h ; use scan line 0..7 to index pattern - add a,(ix+9) - ld e,a - ld a,0 - adc a,(ix+10) - ld d,a ; de points into fill pattern - ld a,(de) ; a = pattern - - ld d,(hl) - dec hl - ld e,(hl) ; de = screen address - dec hl - - and (hl) ; and pattern with fill byte - sub (hl) ; or in complement of fill byte - dec a - ex de,hl - and (hl) ; apply pattern to screen - ld (hl),a - ex de,hl - dec hl - inc bc ; increase available queue space - jp applypattern - - LOCAL endapply -endapply: - dec hl - dec hl - dec hl ; pattern block now pts at investigate block - ret - - - -SPPFill_end: -LD IX,(SPPFill_IXBuffer) -ENDP - -pop namespace -END ASM -END SUB - -#require "SP/PixelUp.asm" -#require "SP/PixelDown.asm" -#require "SP/CharLeft.asm" -#require "SP/CharRight.asm" -#require "SP/GetScrnAddr.asm" - -#endif - +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/alloc.bas b/src/lib/arch/zxnext/stdlib/alloc.bas index acb561163..a61ca2653 100644 --- a/src/lib/arch/zxnext/stdlib/alloc.bas +++ b/src/lib/arch/zxnext/stdlib/alloc.bas @@ -5,234 +5,4 @@ ' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#ifndef __LIBRARY_ALLOC__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_ALLOC__ - -#pragma push(case_insensitive) -#pragma case_insensitive = True - -' ---------------------------------------------------------------- -' function malloc -' -' Allocates the requested bytes in the heap (dynamic memory) and -' returns the address (16 bit, unsigned) of the new bloc. If -' no memory, NULL (0) is returned. -' -' Parameters: -' n: number of bytes -' -' Returns: -' 16 bits (pointer) unsigned integer. NULL is returned if not -' enough memory to allocate the block -' ---------------------------------------------------------------- -function FASTCALL allocate(byval n as uinteger) as uinteger - ' This is a FastCall function. This means: - ' 1.- The 16 bit 'n' parameter is received in hl - ' 2.- Can return at any point with "ret" - ' 3.- The result (16bit) must be returned in HL - asm - push namespace core - ld b, h - ld c, l - jp __MEM_ALLOC ; Since malloc is FASTCALL, we can return from there - pop namespace - end asm -end function - - -' ---------------------------------------------------------------- -' function calloc -' -' Allocates the requested bytes in the heap (dynamic memory) and -' returns the address (16 bit, unsigned) of the new bloc. If -' no memory, NULL (0) is returned. -' The allocated block is cleared (filled with 0's) upon return. -' -' Parameters: -' n: number of bytes -' -' Returns: -' 16 bits (pointer) unsigned integer. NULL is returned if not -' enough memory to allocate the block -' ---------------------------------------------------------------- -function FASTCALL callocate(byval n as uinteger) as uinteger - ' This is a FastCall function. This means: - ' 1.- The 16 bit 'n' parameter is received in hl - ' 2.- Can return at any point with "ret" - ' 3.- The result (16bit) must be returned in HL - asm - push namespace core - ld b, h - ld c, l - jp __MEM_CALLOC ; Since calloc is FASTCALL, we can return from there - pop namespace - end asm -end function - - -' ---------------------------------------------------------------- -' sub free -' -' Frees a block previously allocated with malloc, returning it -' to the heap. -' ---------------------------------------------------------------- -sub FASTCALL deallocate(byval addr as integer) - ' This is a FastCall subroutine. This means: - ' 1.- The 16 bit 'n' parameter is received in hl - ' 2.- Can return at any point with "ret" - asm - push namespace core - jp __MEM_FREE - pop namespace - end asm -end sub - - -' ---------------------------------------------------------------- -' function realloc -' -' Reallocates the requested bytes in the heap (dynamic memory) and -' returns the address (16 bit, unsigned) of the new bloc. If -' no memory, NULL (0) is returned. -' -' Parameters: -' n: number of bytes for the new size to reallocate -' -' Returns: -' 16 bits (pointer) unsigned integer. NULL is returned if not -' enough memory to alloc the block -' ---------------------------------------------------------------- -function FASTCALL reallocate(byval addr as uinteger, byval n as uinteger) as uinteger - ' This is a FastCall function. This means: - ' 1.- The 16 bit 'n' parameter is received in hl - ' 2.- The 2nd parameter is in the stack (16 bit) - ' 3.- Can return at any point with "ret" - ' 4.- The result (16bit) must be returned in HL - asm - push namespace core - ex de, hl ; saves 'n' parameter in de - pop hl ; return address - ex (sp), hl ; hl -> now contains the 2nd parameter (new length) - ld b, h - ld c, l - ex de, hl ; recovers hl (current pointer) - jp __REALLOC ; Since realloc is FASTCALL, we can return from there - pop namespace - end asm -end function - - - -' ---------------------------------------------------------------- -' function memavail -' -' Returns the total amount of free memory in the heap -' ---------------------------------------------------------------- -function FASTCALL memavail as uInteger - asm - push namespace core - PROC - - LOCAL LOOP - - ld hl, ZXBASIC_MEM_HEAP - ld de, 0 ; Size accumulator - -LOOP: - ; BC = (HL) = Block size - ld c, (hl) - inc hl - ld b, (hl) - inc hl - - ; HL = (HL) = Block->next - ld a, (hl) - inc hl - ld h, (hl) - ld l, a - - ; DE += BC => Accum += Block size - ex de, hl - add hl, bc - ex de, hl - - ; If HL != NULL, goto LOOP - ld a, h - or l - jr nz, LOOP - - ex de, hl - - ENDP - pop namespace - end asm -end function - - -' ---------------------------------------------------------------- -' function maxavail -' -' Returns the max amount of free contiguous memory in the heap -' ---------------------------------------------------------------- -function FASTCALL maxavail as uInteger - asm - push namespace core - PROC - - LOCAL LOOP, CONT - - ld hl, ZXBASIC_MEM_HEAP - ld de, 0 ; Size accumulator - -LOOP: - ; BC = (HL) = Block size - ld c, (hl) - inc hl - ld b, (hl) - inc hl - - ; HL = (HL) = Block->next - ld a, (hl) - inc hl - ld h, (hl) - ld l, a - - ; Test if DE >= BC - ; DE -= BC => Accum -= Block size - ex de, hl - or a - sbc hl, bc ; set C if HL < BC - add hl, bc ; Restores current value - ex de, hl - - ; if not C skip this step - jr nc, CONT - ; DE < BC => SET DE = BC - ld d, b - ld e, c - -CONT: - ; If HL != NULL, goto LOOP - ld a, h - or l - jr nz, LOOP - - ex de, hl - - ENDP - pop namespace - end asm -end function - - -#pragma pop(case_insensitive) - -#require "mem/alloc.asm" -#require "mem/free.asm" -#require "mem/realloc.asm" -#require "mem/calloc.asm" - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/asc.bas b/src/lib/arch/zxnext/stdlib/asc.bas index 006467adf..7a38deff7 100644 --- a/src/lib/arch/zxnext/stdlib/asc.bas +++ b/src/lib/arch/zxnext/stdlib/asc.bas @@ -1,19 +1,8 @@ - -#ifndef __LIBRARY_ASC__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_ASC__ - -REM The asc function, as described in FREEBASIC - -function asc(s AS STRING, x AS UINTEGER) AS UBYTE - if len(s) <= x then - return 0 - end if - - return code s(x TO x) -end function - -#endif - +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/attr.bas b/src/lib/arch/zxnext/stdlib/attr.bas index 0a744247e..9326c6fc3 100644 --- a/src/lib/arch/zxnext/stdlib/attr.bas +++ b/src/lib/arch/zxnext/stdlib/attr.bas @@ -2,123 +2,7 @@ ' This file is released under the MIT License ' ' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#ifndef __LIBRARY_ATTR__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_ATTR__ - -#pragma push(case_insensitive) -#pragma case_insensitive = TRUE - -' ---------------------------------------------------------------- -' function ATTR -' -' Parameters: -' row: screen row -' col: screen column -' -' Returns: -' a byte value containing the screen attribute color value -' ---------------------------------------------------------------- -function attr(byval row as ubyte, byval col as ubyte) as ubyte - asm - push namespace core - - PROC - LOCAL __ATTR_END - - ld e, (ix+7) - ld d, (ix+5) - - ; Checks for valid coords - call __IN_SCREEN - jr nc, __ATTR_END - - call __ATTR_ADDR - ld a, (hl) ; byte values are returned in accumulator - -__ATTR_END: - ENDP - - pop namespace - end asm - -end function - - - -' ---------------------------------------------------------------- -' sub SETATTR -' -' Parameters: -' row: screen row -' col: screen column -' color: 8bit color attribute -' -' Action: Sets the attribute of screen(row, column) to the given -' color attribute value. -' ---------------------------------------------------------------- -sub setattr(byval row as ubyte, byval col as ubyte, byval value as ubyte) - asm - push namespace core - - PROC - LOCAL __ATTR_END - - ld e, (ix+7) - ld d, (ix+5) - - ; Checks for valid coords - call __IN_SCREEN - jr nc, __ATTR_END - - call __ATTR_ADDR - ld a, (ix+9) - ld (hl), a ; "POKE" attr address, color value) - -__ATTR_END: - ENDP - - pop namespace - end asm - -end sub - - -' ---------------------------------------------------------------- -' function fastcall ATTRADDR -' -' Parameters: -' row: screen row -' col: screen column -' -' Action: Gets the attribute address of screen(row, column) -' ---------------------------------------------------------------- -function fastcall attraddr(byval row as ubyte, byval col as ubyte) as uinteger - asm - push namespace core - ; a = row - pop hl ; ret address - ex (sp), hl ; Callee => H now has the col - ld d, a ; row - ld e, h - jp __ATTR_ADDR ; Return directly from there - pop namespace - end asm -end function - - - -#pragma pop(case_insensitive) - -' The following is required, because it defines the SCREEN_ADDR constant. -#require "attr.asm" - -' The following is required, because it defines the __IN_SCREEN subroutine. -#require "in_screen.asm" - -#endif \ No newline at end of file +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/basic.bas b/src/lib/arch/zxnext/stdlib/basic.bas index 6816b76ab..156ab94b5 100644 --- a/src/lib/arch/zxnext/stdlib/basic.bas +++ b/src/lib/arch/zxnext/stdlib/basic.bas @@ -1,148 +1,8 @@ - -/' -------------------------------------------------------------------------------------- -BASIC Interpreter KEYIN command for Sinclair BASIC -A function to execute a string as a BASIC command -Copyright 2018 Miguel Angel Rodriguez Jodar (mcleod_ideafix). zxprojects.com - - Licensed under the Apache License, Version 2.0 (the "License") - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -USAGE: - -Build a string with valid BASIC tokens and pass it to the EvalBASIC() function -execute it. - -10 EvalBASIC("BEEP 1,20"): REM this will execute command BEEP 1,20 - -Multiple sentences can be executed as well: - -20 a = EvalBASIC("FOR n=0 TO 7: BORDER n: BEEP 0.1,n*8: NEXT n") - -Of course, the parameter can be any valid string parameter, so a BASIC program -can build a sentence or a group of sentences and execute them using this -function. Think of it as a even more versatile VAL or VAL$ function. - -Note that this function returns the value of the BC register. - -BUGS: -Most probably a lot. I'm still learning how to interact with the BASIC interpreter. --------------------------------------------------------------------------------------- '/ - - -#ifndef __LIBRARY_EVALBASIC__ -REM Avoid recursive / multiple inclusion -#define __LIBRARY_EVALBASIC__ - -#pragma push(case_insensitive) -#pragma case_insensitive = true - -Function fastcall EvalBASIC(ByVal basic as String) as Uinteger - ASM - push namespace core - PROC - - LOCAL E_LINE - LOCAL CH_ADD - LOCAL SET_MIN - LOCAL MAKE_ROOM - LOCAL K_CUR - LOCAL LINE_SCAN - LOCAL LINE_RUN - LOCAL DEF_ADD - LOCAL NEWPPC - LOCAL NSPPC - LOCAL PPC - LOCAL SUBPPC - LOCAL NXTLIN - - E_LINE equ 5c59h - CH_ADD equ 5c5dh - SET_MIN equ 16b0h - MAKE_ROOM equ 1655h - K_CUR equ 5c5bh - LINE_SCAN equ 1b17h - LINE_RUN equ 1b8ah - DEF_ADD equ 5c0bh - NEWPPC equ 5c42h - NSPPC equ 5c44h - PPC equ 5c45h - SUBPPC equ 5c47h - NXTLIN equ 5c55h - - ld a, h - or l - ret z ; Empty (NULL) string? return - - ld de,(CH_ADD) ; Save some BASIC pointers - push de ; that will change while our string is - ld de,(NXTLIN) ; being executed, so at the end of it, - push de ; normal program can resume execution - ld de,(PPC) ; - push de ; - ld a,(SUBPPC) ; - push af ; - ld de,(NEWPPC) ; - push de ; - ld a,(NSPPC) ; - push af ; - push ix ; Not sure if IX is modified, but ... - - ld c, (hl) - inc hl - ld b, (hl) ; BC = string length - inc hl ; HL = start of string - push hl - push bc ; And save both address and length for future use - call SET_MIN ; Clear edit and work space - ld hl,(E_LINE) ; HL = start of editor area - pop bc ; Retrieve temporarily string address - push bc - call MAKE_ROOM ; Make room for BC bytes (length of the string) starting in the editor area (HL) - pop bc - pop hl ; Finally, retrieve both string address and length - ld de,(E_LINE) ; Destination: recently opened space in editor area - ldir ; Transfer string into there (key it in into editor) - ld (K_CUR),de ; Update K_CUR to point to the end of the "keyed in" line - call LINE_SCAN ; Check syntax - bit 7,(iy+0) ; If syntax error... - ld a, ERROR_NonsenseInBasic - jp z, __ERROR ; ... return with C Nonsense in BASIC - ld hl,(E_LINE) ; Copy the start of the now syntax clean line to execute - ld (CH_ADD),hl ; into CH_ADD to start executing - set 7,(iy+1) ; Signal we are going to execute a line - ld (iy+0),0xff ; Clear ERR-NO (OK) - ld (iy+10),1 ; Point NSPPC to the first statement into the line - call LINE_RUN ; Execute the line - - pop ix - pop af ; - ld (NSPPC),a ; Restore BASIC pointers - pop hl ; to resume execution of the - ld (NEWPPC),hl ; next line/sentence by the - pop af ; interpreter - ld (SUBPPC),a ; - pop hl ; - ld (PPC),hl ; - pop hl ; - ld (NXTLIN),hl ; - pop hl ; - ld (CH_ADD),hl ; - - ENDP - pop namespace - END ASM -End Function - -#pragma pop(case_insensitive) -#require "error.asm" - -#endif \ No newline at end of file +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/cb/maskedsprites.bas b/src/lib/arch/zxnext/stdlib/cb/maskedsprites.bas new file mode 100644 index 000000000..6099f7c56 --- /dev/null +++ b/src/lib/arch/zxnext/stdlib/cb/maskedsprites.bas @@ -0,0 +1,8 @@ +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/clearbox.bas b/src/lib/arch/zxnext/stdlib/clearbox.bas index 41bbb9346..b3e7cb78d 100644 --- a/src/lib/arch/zxnext/stdlib/clearbox.bas +++ b/src/lib/arch/zxnext/stdlib/clearbox.bas @@ -1,110 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License ' -' Copyleft (k) 2008-2023 -' by Paul Fisher (a.k.a. BritLion) +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#ifndef __LIBRARY_CLEARBOX__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_CLEARBOX__ - -#pragma push(case_insensitive) -#pragma case_insensitive = True - - -' ---------------------------------------------------------------- -' SUB clearBox -' -' Blanks the pixels for a box, measured in Character Squares -' from print positions X,Y to X + Width, Y + height -' -' Parameters: -' x - x coordinate (cell column) -' y - y coordinate (cell row) -' width - width (number of columns) -' height - height (number of rows) -' -' ---------------------------------------------------------------- -SUB clearBox(x as uByte, y as uByte, width as uByte, height as uByte) -' THE ERROR CHECKING IS NONEXISTENT. -' Please make sure you send sensible data - -' 0 < x < 32, 0 < y < 23, x + width < 32 and y + height < 23 -' Britlion 2012. - -ASM - PROC - LOCAL clearbox_outer_loop, clearbox_mid_loop - LOCAL clearbox_inner_loop, clearbox_row_skip - - ld b,(IX+5) ;' get x value - ld c,(IX+7) ;' get y value - - ld a, c ;' Set HL to screen offset byte for this character. - and 24 - ld h, a - ld a, c - and 7 - rra - rra - rra - rra - add a, b - ld l, a - - ld b, (IX+11) ;' get height - ld c,(IX+9) ;' get width - -clearbox_outer_loop: - xor a - push bc ;' save height. - push hl ;' save screen address. - - ld de, (.core.SCREEN_ADDR) - add hl, de ; 'Adds offset to the screen address pointer - ld d, 8 ;' 8 rows to a character. -clearbox_mid_loop: - push hl ;' save screen address - ld b,c ;' get width. - -clearbox_inner_loop: - ld (hl), a ;' write out a zero to the screen. - - inc hl ;' go right. - djnz clearbox_inner_loop ;' repeat. - - pop hl ;' recover screen address - inc h ;' down a row (+256) - - dec d - jp nz, clearbox_mid_loop ;' repeat for this row. - - pop hl ;' get back address at start of line - pop bc ;' get back char count. - - ld a, 32 ;' Go down to next character row. - add a, l - ld l, a - jr nc, clearbox_row_skip - - ld a, 8 - add a, h - ld h, a - -clearbox_row_skip: - djnz clearbox_outer_loop - - ENDP -END ASM -END SUB - - - -#require "sysvars.asm" - -#pragma pop(case_insensitive) - -#endif - +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/csrlin.bas b/src/lib/arch/zxnext/stdlib/csrlin.bas index 9b92e3911..b6719cf2c 100644 --- a/src/lib/arch/zxnext/stdlib/csrlin.bas +++ b/src/lib/arch/zxnext/stdlib/csrlin.bas @@ -2,41 +2,7 @@ ' This file is released under the MIT License ' ' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' -' Use this file as a template to develop your own library file -' ---------------------------------------------------------------- - -#ifndef __LIBRARY_CSRLIN__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_CSRLIN__ - -#pragma push(case_insensitive) -#pragma case_insensitive = true - +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -' function csrlin() -' -' Returns: -' A byte containing the current ROW printing position -' ---------------------------------------------------------------- -function FASTCALL csrlin as ubyte - asm - push namespace core - PROC ; Start new scope - - call __LOAD_S_POSN - ld a, d - - ENDP ; End scope - pop namespace - end asm -end function - -#pragma pop(case_insensitive) -#require "sposn.asm" - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/esxdos.bas b/src/lib/arch/zxnext/stdlib/esxdos.bas index 387bb1bf7..f672ea9cc 100644 --- a/src/lib/arch/zxnext/stdlib/esxdos.bas +++ b/src/lib/arch/zxnext/stdlib/esxdos.bas @@ -1,769 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License ' -' Copyleft (k) 2017 -' by Miguel Angel Rodriguez Jodar (mcleod_ideafix) -' and Jose Rodriguez-Rosa (a.k.a. Boriel) -' Revised and fixed by Duefectu -' ESXDOS file access usage +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#ifndef __LIBRARY_ESXDOS__ -REM Avoid recursive / multiple inclusion -#define __LIBRARY_ESXDOS__ - -#include -#include - -' Some ESXDOS system calls -#define HOOK_BASE 128 -#define MISC_BASE (HOOK_BASE+8) -#define FSYS_BASE (MISC_BASE+16) -#define M_GETSETDRV (MISC_BASE+1) -#define F_OPEN (FSYS_BASE+2) -#define F_CLOSE (FSYS_BASE+3) -#define F_READ (FSYS_BASE+5) -#define F_WRITE (FSYS_BASE+6) -#define F_SEEK (FSYS_BASE+7) -#define F_GETPOS (FSYS_BASE+8) -#define F_OPENDIR (FSYS_BASE+11) -#define F_READDIR (FSYS_BASE+12) -#define F_TELLDIR (FSYS_BASE+13) -#define F_SEEKDIR (FSYS_BASE+14) -#define F_REWINDDIR (FSYS_BASE+15) -#define F_GETCWD (FSYS_BASE+16) -#define F_CHDIR (FSYS_BASE+17) -#define F_MKDIR (FSYS_BASE+18) -#define F_RMDIR (FSYS_BASE+19) - -#define EDOS_FMODE_READ 0x1 ' Read access -#define EDOS_FMODE_WRITE 0x2 ' Write access -#define EDOS_FMODE_OPEN_EX 0x0 ' Open if exists, else error -#define EDOS_FMODE_OPEN_AL 0x8 ' Open if exists, if not create -#define EDOS_FMODE_CREATE_NEW 0x4 ' Create if not exist, else error -#define EDOS_FMODE_CREATE_AL 0xc ' Create if not exist, else open and trunc - -#define SEEK_START 0 ' From the beginning of file -#define SEEK_CUR 1 ' From the current position -#define SEEK_BKCUR 2 ' From the current position, backwards - -#define FATTR_RDONLY 1 -#define FATTR_HIDDEN 2 -#define FATTR_SYSTEM 4 -#define FATTR_VOLUME 8 -#define FATTR_DIR 16 -#define FATTR_ARCHIVE 32 - -#define EDOS_ERR_NR 23610 - -' ---------------------------------------------------------------- -' function ESXDosOpen -' -' Parameters: -' filename: file to open -' mode: one of FMODE -' -' Returns: -' File stream ID (ubyte) -' it can be -1 on error (variable ERR_NR will contain -' another value with extra information) -' ---------------------------------------------------------------- -Function ESXDosOpen(ByVal fname as String, ByVal mode as Ubyte) as Byte - fname = fname + chr$ 0 - poke EDOS_ERR_NR,255 - - Asm - ld l, (ix+4) - ld h, (ix+5) ; fname ptr - ld a, (ix+7) ; mode in a - inc hl - inc hl - push ix - push hl - push af - - xor a - rst 8 - db M_GETSETDRV ; Default drive in A - - pop bc ; Open mode in B - pop ix ; Uses IX for fname pointer - - rst 8 - db F_OPEN - pop ix - jr nc, open_ok - ld (EDOS_ERR_NR), a - ld a, 0FFh -open_ok: ; returns Ubyte result form A Register - End Asm -End Function - - -' ---------------------------------------------------------------- -' Sub ESXDosClose -' -' Parameters: -' handle: File stream ID to close -' ---------------------------------------------------------------- -Sub FASTCALL ESXDosClose(ByVal handle as Byte) - Asm - ;FASTCALL implies handle is already in A register - push ix - rst 8 - db F_CLOSE - pop ix - End Asm -End Sub - - -' ---------------------------------------------------------------- -' Function ESXDosWrite -' -' Parameters: -' handle: file handle (returned by ESXDOSOpen -' buffer: memory address for the buffer -' nbytes: number of bytes to write -' -' Returns: -' number of bytes effectively written -' ---------------------------------------------------------------- -Function FASTCALL ESXDosWrite(ByVal handle as Byte, _ - ByVal buffer as UInteger, _ - ByVal nbytes as UInteger) as Uinteger - Asm - ;FASTCALL implies handle is already in A register - ld hl, EDOS_ERR_NR - ld (hl), 255 ; sets 255 = OK - pop de ; ret address - pop hl ; buffer address - pop bc ; bc <- nbytes - push de ; put back ret address - push ix ; saves IX for ZX Basic - push hl - pop ix ; uses IX <- HL - rst 8h - db F_WRITE - jr nc, write_ok - ld bc, -1 - ld (EDOS_ERR_NR),a -write_ok: - ld h, b - ld l, c - pop ix ; recovers IX for ZX Basic - End Asm -End Function - - -' ---------------------------------------------------------------- -' Function ESXDosRead -' -' Parameters: -' handle: file handle (returned by ESXDOSOpen -' buffer: memory address for the buffer -' nbytes: number of bytes to read -' -' Returns: -' number of bytes effectively read -' ---------------------------------------------------------------- -Function FASTCALL ESXDosRead(ByVal handle as Byte, _ - ByVal buffer as UInteger, _ - ByVal nbytes as UInteger) as Uinteger - Asm - ;FASTCALL implies handle is already in A register - ld hl, EDOS_ERR_NR - ld (hl), 255 ; sets 255 = OK - pop de ; ret address - pop hl ; buffer address - pop bc ; bc <- nbytes - push de ; put back ret address - push ix ; saves IX for ZX Basic - push hl - pop ix ; uses IX <- HL - rst 8h - db F_READ - jr nc, read_ok - ld (EDOS_ERR_NR),a - ld bc, -1 -read_ok: - ld h, b - ld l, c - pop ix ; recovers IX for ZX Basic - End Asm -End Function - - -' ---------------------------------------------------------------- -' Sub ESXDosSeek -' -' Parameters: -' handle: file handle -' offset: file offset -' position: from position, one of SEEK_ constants -' ---------------------------------------------------------------- -Sub FASTCALL ESXDosSeek(ByVal handle as byte, _ - ByVal offset as Long, _ - ByVal position as UByte) - Asm - ld hl, EDOS_ERR_NR - ld (hl), 255 ; sets 255 = OK - ;FASTCALL implies handle is already in A register - pop hl ; ret address - pop de ; low (word) offset part - pop bc ; hi (word) offset part - ex (sp), hl ; pop position param and put ret address back in the stack - ld l, h ; l <- h <- position (0: start, 1: forward current, 2: back current) - push ix ; saves IX for ZX Basic - push hl - pop ix ; uses IX <- HL - rst 8 - db F_SEEK - pop ix ; recovers IX for ZX Basic - End Asm -End Sub - - -' ---------------------------------------------------------------- -' Function ESXDosGetPos -' -' Parameters: -' handle: file handle (returned by ESXDosOpen) -' -' Returns: -' the current file position -' ---------------------------------------------------------------- -Function FASTCALL ESXDosGetPos (ByVal handle as byte) as long - Asm - ld hl, EDOS_ERR_NR - ld (hl), 255 ; sets 255 = OK - rst 8 - db F_GETPOS - ld h,d - ld l,e ; BCDE -> DEHL for return - ld d,b - ld e,c - End Asm -End Function - - -' ---------------------------------------------------------------- -' Function ESXDosGetCwd -' -' Parameters: -' None -' -' Returns: -' the current working directory name as a string. -' -' Remarks: -' it currently uses a portion of the heap -' ---------------------------------------------------------------- -Function ESXDosGetCwd as String - Dim cwd$ as String - Dim addr as Uinteger - - Asm - push namespace core - proc - local NotNull - - xor a - rst 8 - db M_GETSETDRV ;Default drive in A - push af ;Preserve drive - ld bc,256 ;reserve 256 bytes from heap - call __MEM_ALLOC ;call to allocate - ld a,h - or l - jr nz,NotNull - rst 8 - db 3 ;4 Out of memory -NotNull: - pop af ;restore drive - ld (ix-4),l ;store address of alloced block into addr variable - ld (ix-3),h - push ix - push hl - pop ix - rst 8 - db F_GETCWD - pop ix - endp - pop namespace - End Asm - - cwd$ = "" - While peek(addr)<>0 'Read ASCIIZ string from the heap - cwd$ = cwd$ + chr$ (peek(addr)) 'and concatenate each character to a BASIC string - addr = addr + 1 - End While - - deallocate (addr) 'free memory block allocated from the heap in the ASM block - - return cwd$ -End Function - - -' ---------------------------------------------------------------- -' Function ESXDosOpenDir -' -' Parameters: -' path: string containing the directory path to open -' -' Returns: -' a handle to the opened directory, or 0 if error -' -' Remarks: -' it currently uses a portion of the heap that must be -' deallocated by calling ESXDosCloseDir -' ---------------------------------------------------------------- -Function ESXDosOpenDir (ByVal path as String) as UInteger - Dim handle as UInteger - - poke EDOS_ERR_NR,255 - handle = 65535 - if path = "" then - return 0 - end if - - Asm - push namespace core - Proc - local NotNull - local HandleOK - Local ExitFunction - - xor a - ld bc,32 ;reserve 32 bytes from heap - call __MEM_ALLOC ;call to allocate - ld a,h - or l - jr nz,NotNull - rst 8 - db 3 ;4 Out of memory - -NotNull: - ld (ix-2),l - ld (ix-1),h - - push hl - ex de,hl ; - ld l,(ix+4) ; - ld h,(ix+5) ; copy string from - ld c,(hl) ; BASIC to heap and - inc hl ; convert it to ASCIIZ - ld b,0 ; - inc hl ; - ldir ; - xor a ; - ld (de),a ; - pop hl - - rst 8 - db M_GETSETDRV ;Default drive in A - - ld l,(ix-2) - ld h,(ix-1) - ld b,0 - push ix - push hl - pop ix - rst 8 - db F_OPENDIR - pop ix - jr nc,HandleOK - - ld (EDOS_ERR_NR),a - call __MEM_FREE ;deallocate mem - ld (ix-2),0 - ld (ix-1),0 - jr ExitFunction - -HandleOK: - ld l,(ix-2) - ld h,(ix-1) - ld (hl),a ;store handle into offset 0 of memory block - -ExitFunction: - endp - pop namespace - End Asm - - return handle -End Function - - - -' ---------------------------------------------------------------- -' Sub ESXDosCloseDir -' -' Parameters: -' handle: UInteger containing a valid handle returned by ESXDosOpenDir -' -' ---------------------------------------------------------------- -Sub ESXDosCloseDir (ByVal handle as UInteger) - if (handle <> 0) then - ESXDosClose (peek (handle)) 'close directory handle - deallocate (handle) 'deallocate memory buffer used by opendir/readdir - end if -End Sub - - - -' ---------------------------------------------------------------- -' Function ESXDosReadDentry -' -' Parameters: -' handle: UInteger containing a valid handle returned by ESXDosOpenDir -' -' Returns: -' 0 if there was no more entries on this directory, or non 0 if a -' new entry was retrieved -' -' ---------------------------------------------------------------- -Function FASTCALL ESXDosReadDentry (ByVal handle as UInteger) as Byte - Asm - Proc - ld a, 0xFF - ld (EDOS_ERR_NR), a - - local read_ok - - ld a,(hl) - inc hl - - push ix - push hl - pop ix - rst 8 - db F_READDIR - pop ix - jr nc,read_ok - ld (EDOS_ERR_NR),a - xor a -read_ok: - Endp - End Asm - -End Function - - - -' ---------------------------------------------------------------- -' Function ESXDosGetDentryFilename -' -' Parameters: -' handle: UInteger containing a valid handle returned by ESXDosOpenDir -' -' Returns: -' a string containing the filename of the directory entry retrieved -' in the last call to ESXDosReadDentry -' -' ---------------------------------------------------------------- -Function ESXDosGetDentryFilename (ByVal handle as UInteger) as String - Dim filename$ as String - - filename$ = "" - if handle = 0 then - return "" - end if - - handle = handle + 2 'jump over ESXDOS handle and file attributes - while peek(handle) <> 0 - filename$ = filename$ + chr$(peek(handle)) - handle = handle + 1 - end while - return filename$ -End Function - - - -' ---------------------------------------------------------------- -' Function ESXDosGetDentryAttr -' -' Parameters: -' handle: UInteger containing a valid handle returned by ESXDosOpenDir -' -' Returns: -' an unsigned byte containing the file attributes of the directory entry -' retrieved in the last call to ESXDosReadDentry -' -' ---------------------------------------------------------------- -Function ESXDosGetDentryAttr (ByVal handle as UInteger) as UByte - if handle = 0 then - return 0 - end if - - return peek (handle+1) -End Function - - - -' ---------------------------------------------------------------- -' Function ESXDosGetDentryFilesize -' -' Parameters: -' handle: UInteger containing a valid handle returned by ESXDosOpenDir -' -' Returns: -' an unsigned long value containing the file size of the directory -' entry retrieved in the last call to ESXDosReadDentry -' -' ---------------------------------------------------------------- -Function FASTCALL ESXDosGetDentryFilesize (ByVal handle as UInteger) as ULong - Asm - Proc - Local ParseString - - ld a, 0xFF - ld (EDOS_ERR_NR), a - inc hl ;skip over ESXDOS directory handle - -ParseString: - inc hl ;skip over file attr (or over a character of the filename) - ld a,(hl) - or a - jr nz,ParseString - ld de,5 - add hl,de ;skip over null byte and timestamp - ld c,(hl) - inc hl - ld b,(hl) - inc hl - ld e,(hl) - inc hl - ld d,(hl) - ld h,b - ld l,c - endp - End Asm -End Function - - - -' ---------------------------------------------------------------- -' Function ESXDosChDir -' -' Parameters: -' path: string containing the directory path to change to -' -' ---------------------------------------------------------------- -Sub ESXDosChDir (ByVal path as String) - Dim handle as UInteger - - poke EDOS_ERR_NR,255 - if path = "" then - return - end if - - Asm - push namespace core - Proc - local NotNull - local HandleOK - - xor a - ld bc,256 ;reserve 256 bytes from heap - call __MEM_ALLOC ;call to allocate - ld a,h - or l - jr nz,NotNull - rst 8 - db 3 ;4 Out of memory - -NotNull: - push hl - ex de,hl ; - ld l,(ix+4) ; - ld h,(ix+5) ; copy string from - ld c,(hl) ; BASIC to heap and - inc hl ; convert it to ASCIIZ - ld b,0 ; - inc hl ; - ldir ; - xor a ; - ld (de),a ; - - rst 8 - db M_GETSETDRV ;Default drive in A - - pop hl - push hl - push ix - push hl - pop ix - rst 8 - db F_CHDIR - pop ix - jr nc,HandleOK - ld (EDOS_ERR_NR),a -HandleOK: - pop hl - call __MEM_FREE ;deallocate mem - endp - pop namespace - End Asm - -End Sub - - -' ---------------------------------------------------------------- -' Function ESXDosTellDir -' -' Parameters: -' handle: UInteger containing a valid handle returned by ESXDosOpenDir -' -' Returns: -' an unsigned long value which is the directory entry # to be retrieved -' on the next call to ESXDosReadDentry -' -' ---------------------------------------------------------------- -Function FASTCALL ESXDosTellDir (ByVal handle as UInteger) as ULong - Asm - Proc - ld a, 0xFF - ld (EDOS_ERR_NR), a - - local read_ok,divide_by_32 - - ld a, h - or l - ret z - - ld a,(hl) - - push ix - rst 8 - db F_TELLDIR - pop ix - jr nc,read_ok - ld (EDOS_ERR_NR),a - ld hl, 0 - ld d, h - ld e, l - ret -read_ok: - ld h,d ; - ld l,e ; BCDE -> DEHL for ZX Basic - ld d,b ; - ld e,c ; - - ld b,5 -divide_by_32: ; - srl d ; - rr e ; DEHL / 32 - rr h ; - rr l ; - djnz divide_by_32 - Endp - End Asm - -End Function - - -' ---------------------------------------------------------------- -' Sub ESXDosSeekDir -' -' Parameters: -' handle: UInteger containing a valid handle returned by ESXDosOpenDir -' entry: ULong containing the directory entry # to seek to. -' -' ---------------------------------------------------------------- -Sub FASTCALL ESXDosSeekDir (ByVal handle as UInteger, ByVal entry as ULong) - Asm - Proc - local multiply_by_32 - - ld a, 0xFF - ld (EDOS_ERR_NR), a - - ld a, h - or l - ld a,(hl) - - pop hl - pop de - ex (sp),hl - ret z - - ld b,5 -multiply_by_32: - sla e ; - rl d ; Multiply HLDE by 32 - rl l ; - rl h ; - djnz multiply_by_32 - ld b, h ; - ld c, l ; Offset is now at BCDE - - ld l,0 ; Just in case this is using L as in F_SEEK (to-do) - push ix - rst 8 - db F_SEEKDIR - pop ix - ret nc - ld (EDOS_ERR_NR),a - Endp - End Asm - -End Sub - - -' ---------------------------------------------------------------- -' Sub ESXDosRewindDir -' -' Parameters: -' handle: UInteger containing a valid handle returned by ESXDosOpenDir -' -' Remarks: -' Functionally equivalent to ESXDosSeekDir (handle, 0) -' -' ---------------------------------------------------------------- -Sub FASTCALL ESXDosRewindDir (ByVal handle as UInteger) - Asm - Proc - local read_ok - - ld a,0xFF - ld (EDOS_ERR_NR),a - - ld a, h - or l - ret z - - ld a,(hl) - - push ix - rst 8 - db F_REWINDDIR - pop ix - ret nc - ld (EDOS_ERR_NR),a -read_ok: - Endp - End Asm -End Sub - - -' Remove macros -#undef HOOK_BASE -#undef MISC_BASE -#undef FSYS_BASE -#undef M_GETSETDRV -#undef F_OPEN -#undef F_CLOSE -#undef F_READ -#undef F_WRITE -#undef F_SEEK -#undef F_GETPOS -#undef F_OPENDIR -#undef F_READDIR -#undef F_TELLDIR -#undef F_SEEKDIR -#undef F_REWINDDIR -#undef F_GETCWD -#undef F_CHDIR -#undef F_MKDIR -#undef F_RMDIR - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/fastplot.bas b/src/lib/arch/zxnext/stdlib/fastplot.bas index 531403dc3..717ac12ef 100644 --- a/src/lib/arch/zxnext/stdlib/fastplot.bas +++ b/src/lib/arch/zxnext/stdlib/fastplot.bas @@ -1,144 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License ' -' Copyleft (k) 2019 -' Contributed by Britlion and rearranged by John Mcgibbitts +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- - -#ifndef __LIBRARY_FASTPLOT__ -REM Avoid recursive / multiple inclusion -#define __LIBRARY_FASTPLOT__ - -#pragma push(case_insensitive) -#pragma case_insensitive = True - - -' ---------------------------------------------------------------- -' Plots a point at (x, y) in OVER 1 mode (XOR) with color as ATTR -' This routine is slightly faster than PLOT XOR 1 with ATTRs -' ---------------------------------------------------------------- -SUB fastcall fastPlotXORAttr (x AS UBYTE, y AS UBYTE, color AS UBYTE) -ASM - PROC - LOCAL plotPointXORAttr_loop - pop hl - pop de - ex (sp), hl - ld c, h - - ld e, a - ld a, 191 - sub d - ld d, a - ret c - - AND a - rra - scf - rra - AND a - rra - XOR d - AND 248 - XOR d - ld h, a - ld a, e - rlca - rlca - rlca - XOR d - AND 199 - XOR d - rlca - rlca - ld l, a - - ld a, e - AND 7 - ld b, a - inc b - ld a, 254 - -plotPointXORAttr_loop: - rrca - djnz plotPointXORAttr_loop - - cpl - xor (hl) - ld (hl), a - - ; ' Point plotted. - ld a, h ; ' HL = addr of attr - rrca - rrca - rrca - and $03 - or $58 - ld h, a ; ' HL = addr of attr - - ld (hl),c - ENDP -END ASM -END SUB - - -' ---------------------------------------------------------------- -' Plots a point at (x, y) in OVER 1 mode (XOR) with color as ATTR -' This routine is slightly faster than PLOT XOR 1 -' ---------------------------------------------------------------- -SUB fastcall fastPlotXOR(x AS UBYTE, y AS UBYTE) -ASM - PROC - LOCAL plotPointXOR_loop - pop hl - ex (sp), hl - - ld e, a - ld a, 191 - sub h - ld d, a - ret c - - AND a - rra - scf - rra - AND a - rra - XOR d - AND 248 - XOR d - ld h, a - ld a, e - rlca - rlca - rlca - XOR d - AND 199 - XOR d - rlca - rlca - ld l, a - - ld a, e - AND 7 - ld b, a - inc b - ld a, 254 - -plotPointXOR_loop: - rrca - djnz plotPointXOR_loop - - cpl - xor (hl) - ld (hl), a - ENDP -END ASM -END SUB - - -#pragma pop(case_insensitive) - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/fmath.bas b/src/lib/arch/zxnext/stdlib/fmath.bas index ee7bebe84..91b49bba6 100644 --- a/src/lib/arch/zxnext/stdlib/fmath.bas +++ b/src/lib/arch/zxnext/stdlib/fmath.bas @@ -1,140 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License ' -' Copyleft (k) 2024 -' Contributed by Britlion +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#pragma once - -#pragma push(case_insensitive) -#pragma case_insensitive = True - - -FUNCTION fSin(num as FIXED) as FIXED - DIM quad as byte - DIM est1,dif as uByte - - 'This change made now that MOD works with FIXED types. - 'This is much faster than the repeated subtraction method for large angles (much > 360) - 'while having some tiny rounding errors that should not significantly affect our results. - 'Note that the result may be positive or negative still, and for SIN(360) might come out - 'fractionally above 360 (which would cause issued) so the below code still is required. - - IF num >= 360 THEN - num = num MOD 360 - ELSEIF num < 0 THEN - num = 360 - ABS(num) MOD 360 - END IF - - IF num>180 then - quad=-1 - num=num-180 - ELSE - quad=1 - END IF - - IF num>90 then num=180-num - - num=num/2 - dif=num : rem Cast to byte loses decimal - num=num-dif : rem so this is just the decimal bit - - - est1=PEEK (@sinetable+dif) - dif=PEEK (@sinetable+dif+1)-est1 : REM this is just the difference to the next up number. - - num=est1+(num*dif): REM base +interpolate to the next value. - - return (num/255)*quad - - - sinetable: - asm - DEFB 000,009,018,027,035,044,053,062 - DEFB 070,079,087,096,104,112,120,127 - DEFB 135,143,150,157,164,171,177,183 - DEFB 190,195,201,206,211,216,221,225 - DEFB 229,233,236,240,243,245,247,249 - DEFB 251,253,254,254,255,255 - end asm -END FUNCTION - -FUNCTION fCos(num as FIXED) as FIXED - return fSin(90-num) -END FUNCTION - -FUNCTION fTan(num as FIXED) as FIXED - return fSin(num)/fSin(90-num) -END FUNCTION - -REM Fast floating Point Square Root Function -REM Adapted and modified for Boriel's ZX BASIC -REM By Britlion - -FUNCTION FASTCALL fSqrt (radicand as FLOAT) as FLOAT - ASM - push namespace core - - ; FLOAT value arrives in A ED CB - ; A is the exponent. - AND A ; Test for zero argument - RET Z ; Return with zero. - - ;Strictly we should test the number for being negative and quit if it is. - ;But let's assume we like imaginary numbers, hmm? - ; If you'd rather break it change to a jump to an error below. - ;BIT 7,E ; Test the bit. - ;JR NZ,REPORT ; back to REPORT_A - ; 'Invalid argument' - RES 7,E ; Now it's a positive number, no matter what. - - call __FPSTACK_PUSH ; Okay, We put it on the calc stack. Stack contains ABS(x) - - ; Halve the exponent to achieve a good guess.(accurate with .25 16 64 etc.) - - ; Remember, A is the exponent. - XOR $80 ; toggle sign of exponent - SRA A ; shift right, bit 7 unchanged. - INC A ; - JR Z,ASIS ; forward with say .25 -> .5 - JP P,ASIS ; leave increment if value > .5 - DEC A ; restore to shift only. - - ASIS: - XOR $80 ; restore sign. - call __FPSTACK_PUSH ; Okay, NOW we put the guess on the stack - rst 28h ; ROM CALC ;;guess,x - DEFB $C3 ;;st-mem-3 - DEFB $02 ;;delete - - SQRLOOP: - DEFB $31 ;;duplicate - DEFB $E3 ;;get-mem-3 - DEFB $C4 ;;st-mem-4 - DEFB $05 ;;div - DEFB $E3 ;;get-mem-3 - DEFB $0F ;;addition - DEFB $A2 ;;stk-half - DEFB $04 ;;multiply - DEFB $C3 ;;st-mem-3 - DEFB $E4 ;;get-mem-4 - DEFB $03 ;;subtract - DEFB $2A ;;abs - DEFB $37 ;;greater-0 - DEFB $00 ;;jump-true - - DEFB SQRLOOP - $ ;;to sqrloop - - DEFB $02 ;;delete - DEFB $E3 ;;get-mem-3 - DEFB $38 ;;end-calc sqr x. - - jp __FPSTACK_POP - - pop namespace - - END ASM -END FUNCTION - -#pragma pop(case_insensitive) +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/hex.bas b/src/lib/arch/zxnext/stdlib/hex.bas index be29fea23..6736f1e14 100644 --- a/src/lib/arch/zxnext/stdlib/hex.bas +++ b/src/lib/arch/zxnext/stdlib/hex.bas @@ -5,113 +5,4 @@ ' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#ifndef __LIBRARY_HEX__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_HEX__ - -#pragma push(case_insensitive) -#pragma case_insensitive = TRUE - -#pragma push(string_base) -#pragma string_base = 0 - -' ---------------------------------------------------------------- -' function HEX -' -' Parameters: -' num : 32 bit unsigned integer numbre -' -' Returns: -' 4 chars str containing the HEX string representation -' ---------------------------------------------------------------- -function FASTCALL hex(num as ULong) as String - asm - push namespace core - PROC - LOCAL SUB_CHAR - LOCAL SUB_CHAR2 - LOCAL END_CHAR - LOCAL DIGIT - - push hl - push de - ld bc,10 - call __MEM_ALLOC - ld a, h - or l - pop de - pop bc - ret z ; NO MEMORY - - push hl ; Saves String ptr - ld (hl), 8 - inc hl - ld (hl), 0 - inc hl ; 8 chars string length - - call DIGIT - ld d, e - call DIGIT - ld d, b - call DIGIT - ld d, c - call DIGIT - pop hl ; Recovers string ptr - ret - -DIGIT: - ld a, d - call SUB_CHAR - ld a, d - jr SUB_CHAR2 - -SUB_CHAR: - rrca - rrca - rrca - rrca - -SUB_CHAR2: - and 0Fh - add a, '0' - cp '9' + 1 - jr c, END_CHAR - add a, 7 - -END_CHAR: - ld (hl), a - inc hl - ret - - ENDP - pop namespace - end asm -end function - - -REM 16 bit version -function hex16(n as UInteger) as String - Dim a$ as String - a$ = hex(n) - return a$(4 TO 7) -end function - - -REM 8 bit version -Function hex8 (n as UByte) as String - Dim res$ as String - - res$ = hex(n) - return res$(6 TO 7) -end function - - -#pragma pop(string_base) -#pragma pop(case_insensitive) - -' The following is required to allocate dynamic memory for strings -#require "mem/alloc.asm" - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/input.bas b/src/lib/arch/zxnext/stdlib/input.bas index 2ed0a1811..9291339a6 100644 --- a/src/lib/arch/zxnext/stdlib/input.bas +++ b/src/lib/arch/zxnext/stdlib/input.bas @@ -2,122 +2,7 @@ ' This file is released under the MIT License ' ' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' -' Simple INPUT routine (not as powerful as Sinclair BASIC's). -' Usage: A$ = INPUT(MaxChars) +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#ifndef __LIBRARY_INPUT__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_INPUT__ - -REM The input subroutine -REM DOES NOT act like ZX Spectrum INPUT command -REM Uses ZX SPECTRUM ROM - -#include once -#include once - -#pragma push(case_insensitive) -#pragma case_insensitive = True - -FUNCTION input(MaxLen AS UINTEGER) AS STRING - DIM LastK AS UBYTE AT 23560: REM LAST_K System VAR - DIM result$ AS STRING - DIM i as UINTEGER - DIM tmp as UByte - - LET tmp = PEEK 23611 - POKE 23611, PEEK 23611 bOR 8 : REM sets FLAGS var to L mode - - result$ = "" - - DO - PRIVATEInputShowCursor() - - REM Wait for a Key Press - LastK = 0 - DO LOOP UNTIL LastK <> 0 - ASM - PROC - LOCAL PIP - LOCAL NO_CLICK - LOCAL BEEPER - - PIP EQU 23609 - BEEPER EQU 0x3B5 - - ld a, (PIP) - cp 0xFF - jr z, NO_CLICK - push ix - ld e, a - ld d, 0 - ld hl, 0x00C8 - CALL BEEPER - pop ix - - NO_CLICK: - ENDP - - END ASM - - PRIVATEInputHideCursor() - - IF LastK = 12 THEN - IF LEN(result$) THEN REM "Del" key code is 12 - IF LEN(result$) = 1 THEN - LET result$ = "" - ELSE - LET result$ = result$( TO LEN(result$) - 2) - END IF - PRINT CHR$(8); - END IF - ELSEIF LastK >= CODE(" ") AND LEN(result$) < MaxLen THEN - LET result$ = result$ + CHR$(LastK) - PRINT CHR$(LastK); - END IF - - LOOP UNTIL LastK = 13 : REM "Enter" key code is 13 - - POKE 23611, tmp : REM resets FLAGS var - - FOR i = 1 TO LEN(result$): - PRINT OVER 0; CHR$(8) + " " + chr$(8); - NEXT - - RETURN result$ - -END FUNCTION - -#pragma pop(case_insensitive) - -' ------------------------------------------------------------------ -' Function 'PRIVATE' to this module. -' Shows a flashing cursor -' ------------------------------------------------------------------ -SUB PRIVATEInputShowCursor - REM Print a Flashing cursor at current print position - DIM x, y as UBYTE - y = csrlin() - x = pos() - PRINT AT y, x; OVER 0; FLASH 1; " "; AT y, x; -END SUB - - -' ------------------------------------------------------------------ -' Function 'PRIVATE' to this module. -' Hides the flashing cursor -' ------------------------------------------------------------------ -SUB PRIVATEInputHideCursor - REM Print a Flashing cursor at current print position - DIM x, y as UBYTE - y = csrlin() - x = pos() - PRINT AT y, x; OVER 0; FLASH 0; " "; AT y, x; -END SUB - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/input42.bas b/src/lib/arch/zxnext/stdlib/input42.bas index ba0fb340f..138eee1e9 100644 --- a/src/lib/arch/zxnext/stdlib/input42.bas +++ b/src/lib/arch/zxnext/stdlib/input42.bas @@ -1,91 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' -' Simple INPUT routine (not as powerful as Sinclair BASIC's), but -' this one uses PRINT42 routine -' Usage: A$ = INPUT42(MaxChars) +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#ifndef __LIBRARY_INPUT42__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_INPUT42__ - -REM The input subroutine -REM DOES NOT act like ZX Spectrum INPUT command -REM Uses ZX SPECTRUM ROM - -#include once -#include once -#include once - -#pragma push(case_insensitive) -#pragma case_insensitive = True - -FUNCTION input42(MaxLen AS UINTEGER) AS STRING - DIM LastK AS UBYTE AT 23560: REM LAST_K System VAR - DIM result$ AS STRING - DIM i as UINTEGER - - result$ = "" - POKE 23611, PEEK 23611 bOR 8 - - DO - PRIVATEInputShowCursor42() - - REM Wait for a Key Press - LastK = 0 - DO LOOP UNTIL LastK <> 0 - - PRIVATEInputHideCursor42() - - IF LastK = 12 THEN - IF LEN(result$) THEN REM "Del" key code is 12 - IF LEN(result$) = 1 THEN - LET result$ = "" - ELSE - LET result$ = result$( TO LEN(result$) - 2) - END IF - PRINT42 CHR$(8) - END IF - ELSEIF LastK >= CODE(" ") AND LEN(result$) < MaxLen THEN - LET result$ = result$ + CHR$(LastK) - PRINT42 CHR$(LastK) - END IF - - LOOP UNTIL LastK = 13 : REM "Enter" key code is 13 - - FOR i = 1 TO LEN(result$): - PRINT42 CHR$(8) + " " + CHR$(8) - NEXT - - RETURN result$ - -END FUNCTION - -#pragma pop(case_insensitive) - -' ------------------------------------------------------------------ -' Function 'PRIVATE' to this module. -' Shows a flashing cursor -' ------------------------------------------------------------------ -SUB FASTCALL PRIVATEInputShowCursor42 - REM Print a Flashing cursor at current print position - OVER 1: PRINT42 "_" + CHR$(8): OVER 0 -END SUB - - -' ------------------------------------------------------------------ -' Function 'PRIVATE' to this module. -' Hides the flashing cursor -' ------------------------------------------------------------------ -SUB FASTCALL PRIVATEInputHideCursor42 - REM Print a Flashing cursor at current print position - OVER 0: PRINT42 " " + CHR$(8) -END SUB - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/keys.bas b/src/lib/arch/zxnext/stdlib/keys.bas index c071eccf0..f5c95fc51 100644 --- a/src/lib/arch/zxnext/stdlib/keys.bas +++ b/src/lib/arch/zxnext/stdlib/keys.bas @@ -2,157 +2,7 @@ ' This file is released under the MIT License ' ' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#ifndef __LIBRARY_IO_KEYS__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_IO_KEYS__ -#pragma push(case_insensitive) -#pragma case_insensitive = TRUE - -' ---------------------------------------------------------------- -' function GetKeys() -' -' Returns: -' Waits for a Key press and returns ASCII Code -' ---------------------------------------------------------------- -function GetKey AS UByte - Dim k AS UByte - do - k = CODE INKEY$ - loop UNTIL k - - return k -end function - - -' ---------------------------------------------------------------- -' function MultiKeys(x as Ubyte) -' -' Returns: -' Given the ScanCode, returns 0 if none of the given key(s) are -' not pressed, not zero otherwise. Scancode is one of the UInteger -' constants defined below (KEYB, for B Key in the Keyboard, KEYSPACE -' for Space Bar, KEYCAPS for shift key, etc...) -' -' It's possible to check for multiple keys at once using the bOR -' operator. eg. To check whether key H or L are being pressed (or both) -' use MultiKeys(KEYH | KEYL). The only restriction is that keys must -' be in the same row group defined below. -' -' It's possible to decode which keys of a row are pressed by -' reading the bits of byte returned. -' -' ---------------------------------------------------------------- -function FASTCALL MultiKeys(scancode as UInteger) AS UByte - asm - ld a, h - in a, (0FEh) - cpl - and l - end asm -end function - - -' ---------------------------------------------------------------- -' function GetKeyScanCode() -' -' Returns: -' The pressed Key Scan Codes or 0 if none -' -' To check for more than one key pressed at once use the bOR -' operand. i.e. -' IF GetKeyScanCode() = KEYL bOR KEYH THEN ... -' will check if both keys H and L are being pressed simultaneously. -' ---------------------------------------------------------------- -function FASTCALL GetKeyScanCode AS UInteger - asm - PROC - LOCAL END_KEY - LOCAL LOOP - - ld l, 1 - ld a, l - LOOP: - cpl - ld h, a - in a, (0FEh) - cpl - and 1Fh - jr nz, END_KEY - - ld a, l - rla - ld l, a - jr nc, LOOP - ld h, a - END_KEY: - ld l, a - ENDP - end asm -end function - -#pragma pop(case_insensitive) - -REM Scan Codes - -REM 1st ROW -const KEYB AS UInteger = 07F10h -const KEYN AS UInteger = 07F08h -const KEYM AS UInteger = 07F04h -const KEYSYMBOL AS UInteger = 07F02h -const KEYSPACE AS UInteger = 07F01h - -REM 2nd ROW -const KEYH AS UInteger = 0BF10h -const KEYJ AS UInteger = 0BF08h -const KEYK AS UInteger = 0BF04h -const KEYL AS UInteger = 0BF02h -const KEYENTER AS UInteger = 0BF01h - -REM 3rd ROW -const KEYY AS UInteger = 0DF10h -const KEYU AS UInteger = 0DF08h -const KEYI AS UInteger = 0DF04h -const KEYO AS UInteger = 0DF02h -const KEYP AS UInteger = 0DF01h - -REM 4th ROW -const KEY6 AS UInteger = 0EF10h -const KEY7 AS UInteger = 0EF08h -const KEY8 AS UInteger = 0EF04h -const KEY9 AS UInteger = 0EF02h -const KEY0 AS UInteger = 0EF01h - -REM 5th ROW -const KEY5 AS UInteger = 0F710h -const KEY4 AS UInteger = 0F708h -const KEY3 AS UInteger = 0F704h -const KEY2 AS UInteger = 0F702h -const KEY1 AS UInteger = 0F701h - -REM 6th ROW -const KEYT AS UInteger = 0FB10h -const KEYR AS UInteger = 0FB08h -const KEYE AS UInteger = 0FB04h -const KEYW AS UInteger = 0FB02h -const KEYQ AS UInteger = 0FB01h - -REM 7th ROW -const KEYG AS UInteger = 0FD10h -const KEYF AS UInteger = 0FD08h -const KEYD AS UInteger = 0FD04h -const KEYS AS UInteger = 0FD02h -const KEYA AS UInteger = 0FD01h - -REM 8th ROW -const KEYV AS UInteger = 0FE10h -const KEYC AS UInteger = 0FE08h -const KEYX AS UInteger = 0FE04h -const KEYZ AS UInteger = 0FE02h -const KEYCAPS AS UInteger = 0FE01h - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/megalz.bas b/src/lib/arch/zxnext/stdlib/megalz.bas index fbb75d3ef..9ae3cca8a 100644 --- a/src/lib/arch/zxnext/stdlib/megalz.bas +++ b/src/lib/arch/zxnext/stdlib/megalz.bas @@ -1,155 +1,8 @@ -' MegaLZ Depacker contributed by Britlion - - -#ifndef __LIBRARY_MEGALZ__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_MEGALZ__ - -#pragma push(case_insensitive) -#pragma case_insensitive = true - -SUB FastCall megaLZDepack(source as uInteger, dest as uInteger) -ASM - -; Prepare parameters -; HL already contains source (FASTCALL) - - POP BC ; Return address - POP DE ; DE contains Dest - PUSH BC ; Stack clean - -;Z80 depacker for megalz V4 packed files (C) fyrex^mhm - -;DESCRIPTION: -; -;Depacker is fully relocatable, not self-modifying, -;it's length is 110 bytes starting from DEC40. -;Register usage: AF,AF',BC,DE,HL. Must be CALL'ed, return is done by RET. -;Provide extra stack location for store 2 bytes (1 word). Depacker does not -;disable or enable interrupts, as well as could be interrupted at any time -;(no f*cking wicked stack usage :). - -; USAGE: -; -; - put depacker anywhere you want, -; - put starting address of packed block in HL, -; - put location where you want data to be depacked in DE, -; (much like LDIR command, but without BC) -; - make CALL to depacker (DEC40). -; - enjoy! ;) - -; PRECAUTIONS: -; -;Be very careful if packed and depacked blocks coincide somewhere in memory. -;Here are some advices: -; -; 1. put packed block to the highest addresses possible. -; Best if last byte of packed block has address #FFFF. -; -; 2. Leave some gap between ends of packed and depacked block. -; For example, last byte of depacked block at #FF00, -; last byte of packed block at #FFFF. -; -; 3. Place nonpackable data to the end of block. -; -; 4. Always check whether depacking occurs OK and neither corrupts depacked data -; nor hangs computer. -; - -PROC - -LOCAL MS -LOCAL M0, M1, M2, M3, M4 -LOCAL M1X -LOCAL X2, X3, X4, X5, X6 -LOCAL N1, N2, N5, N6 - - - LD A,80h - EX AF,AF' -MS: LDI -M0: LD BC,2FFh -M1: EX AF,AF' -M1X: ADD A,A - JR NZ,M2 - LD A,(HL) - INC HL - RLA -M2: RL C - JR NC,M1X - EX AF,AF' - DJNZ X2 - LD A,2 - SRA C - JR C,N1 - INC A - INC C - JR Z,N2 - LD BC,33Fh - JR M1 - -X2: DJNZ X3 - SRL C - JR C,MS - INC B - JR M1 -X6: - ADD A,C -N2: - LD BC,4FFh - JR M1 -N1: - INC C - JR NZ,M4 - EX AF,AF' - INC B -N5: RR C - RET C - RL B - ADD A,A - JR NZ,N6 - LD A,(HL) - INC HL - RLA -N6: JR NC,N5 - EX AF,AF' - ADD A,B - LD B,6 - JR M1 -X3: - DJNZ X4 - LD A,1 - JR M3 -X4: DJNZ X5 - INC C - JR NZ,M4 - LD BC,51Fh - JR M1 -X5: - DJNZ X6 - LD B,C -M4: LD C,(HL) - INC HL -M3: DEC B - PUSH HL - LD L,C - LD H,B - ADD HL,DE - LD C,A - LD B,0 - LDIR - POP HL - JR M0 - -ENDP - -END ASM -END SUB - -#pragma pop(case_insensitive) - -#endif - - +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/memcopy.bas b/src/lib/arch/zxnext/stdlib/memcopy.bas index 5a7942318..707a0687c 100644 --- a/src/lib/arch/zxnext/stdlib/memcopy.bas +++ b/src/lib/arch/zxnext/stdlib/memcopy.bas @@ -1,129 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License ' -' Copyleft (k) 2008-2018 -' Contributed by: -' - Jose Rodriguez-Rosa (a.k.a. Boriel) -' - Miguel Angel Diaz-Jodar (a.k.a. McLeod_Ideafix) +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#ifndef __LIBRARY_MEMCOPY__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_MEMCOPY__ - -#pragma push(case_insensitive) -#pragma case_insensitive = True - -' ---------------------------------------------------------------- -' Sub MemMove(sourceaddr, destaddr, blocklength) -' -' Parameters: -' souceaddr: memory address of source block to copy -' destaddr: memory address of destiny block to copy -' length: number of bytes to copy -' -' Copies block of memory safely from source to dest. -' Source and destiny blocks may overlap -' ---------------------------------------------------------------- -sub fastcall MemMove(source as uinteger, dest as uinteger, length as uinteger) - asm - push namespace core -; Emulates both memmove and memcpy C routines -; Blocks will safely copies if they overlap - -; HL => Start of source block -; DE => Start of destiny block -; BC => Block length - - exx - pop hl ; uses HL' to preserve HL - exx - pop de ; dest - pop bc ; length - exx - push hl ; stores ret addr back - exx - jp __MEMCPY - pop namespace - end asm -end sub - - -' ---------------------------------------------------------------- -' Sub MemCopy(sourceaddr, destaddr, blocklength) -' -' Parameters: -' souceaddr: memory address of source block to copy -' destaddr: memory address of destiny block to copy -' length: number of bytes to copy -' -' Copies block of memory from source to dest. -' Source and destiny blocks should not overlap. -' This sub is slightly faster than memmove -' ---------------------------------------------------------------- -sub fastcall MemCopy(source as uinteger, dest as uinteger, length as uinteger) - asm - push namespace core -; Emulates both memmove and memcpy C routines -; Blocks will safely copies if they DON'T overlap - -; HL => Start of source block -; DE => Start of destiny block -; BC => Block length - - exx - pop hl ; uses HL' to preserve HL - exx - pop de ; dest - pop bc ; length - exx - push hl ; stores ret addr back - exx - ldir - pop namespace - end asm -end sub - - -' ---------------------------------------------------------------- -' Sub MemSet(destaddr, value, blocklength) -' -' Parameters: -' destaddr: memory address of destiny block to fill -' value: value to fill with -' length: number of bytes to fill -' -' ---------------------------------------------------------------- -sub fastcall MemSet(dest as uinteger, value as ubyte, length as uinteger) - asm - push namespace core - -; HL => Start of destination block -; DE => Value (D) -; BC => Block length - - pop de ; ret addr - pop af ; value - pop bc ; length - push de ; stores ret addr back - ld (hl),a - dec bc - ld a, b - or c - ret z - ld d,h - ld e,l - inc de - ldir - pop namespace - end asm -end sub - - -#require "mem/memcopy.asm" - -#pragma pop(case_insensitive) - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/memorybank.bas b/src/lib/arch/zxnext/stdlib/memorybank.bas index 38435060d..125ee097f 100644 --- a/src/lib/arch/zxnext/stdlib/memorybank.bas +++ b/src/lib/arch/zxnext/stdlib/memorybank.bas @@ -1,82 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2023 -' by Juan Segura (a.k.a. Duefectu) -' -' Memory bank switch tools -' ---------------------------------------------------------------- - -#ifndef __LIBRARY_MEMORYBANK__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_MEMORYBANK__ - - -' ---------------------------------------------------------------- -' Place the bank indicated by bankNumber in the memory slot -' between $c000 and $ffff and updates the system variable BANKM. -' Only works on 128K and compatible models. -' Danger: If our program exceeds the address $c000 it may cause -' problems, use this function at your own risk. -' Parameters: -' bankNumber (UByte): Bank number to place at $c000 -' ---------------------------------------------------------------- -SUB FASTCALL SetBank(bankNumber AS UByte) -ASM - ; A = bankNumber to place at $c000 - ld d,a ; D = bankNumber - ld a,($5b5c) ; Read BANKM system variable - and %11111000 ; Reset bank bits - or d ; Set bank bits to bankNumber - ld bc,$7ffd ; Memory Bank control port - di ; Disable interrupts - ld ($5b5c),a ; Update BANKM system variable - out (c),a ; Set the bank - ei ; Enable interrupts -END ASM -END SUB - - -' ---------------------------------------------------------------- -' Returns the memory bank located at $c000 based on the system -' variable BANKM. -' Only works on 128K and compatible models. -' Returns: -' UByte: Bank number placed at $c000 -' ---------------------------------------------------------------- -FUNCTION FASTCALL GetBank() AS UByte - RETURN PEEK $5b5c bAND %111 -END FUNCTION - - -' ---------------------------------------------------------------- -' Place the bank indicated by bankNumber in the memory slot -' between $c000 and $ffff, copy the contents to $8000-$bfff and -' restore the bank that was at $c000 before you started. -' Only works on 128K and compatible models. -' Danger: The contents of memory located between $8000 and $bfff -' are lost, and if our program exceeds the address $8000 it may -' cause problems, use this function at your own risk. -' Parameters: -' bankNumber (UByte): Bank number to place at $8000 -' ---------------------------------------------------------------- -SUB SetCodeBank(bankNumber AS UByte) - DIM b AS UByte - - b = GetBank() - SetBank(bankNumber) - -ASM - ; Copy from $c000-$ffff to $8000-$bfff - ld hl,$c000 - ld de,$8000 - ld bc,$4000 - ldir -END ASM - - SetBank(b) -END SUB - -#endif +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/mj/fourspriter.bas b/src/lib/arch/zxnext/stdlib/mj/fourspriter.bas index 1bb8beecd..83327bc97 100644 --- a/src/lib/arch/zxnext/stdlib/mj/fourspriter.bas +++ b/src/lib/arch/zxnext/stdlib/mj/fourspriter.bas @@ -1,892 +1,8 @@ -'' ------------------------------------------------------------------------- -'' Mojon Twins' F O U R S P R I T E R -'' ------------------------------------------------------------------------- -'' Simple 16x16 sprite library, char-wise movement w/background preservation -'' ------------------------------------------------------------------------- -'' Copyleft 2009 / 2010 The Mojon Twins. -'' Pergreñado por na_th_an -'' ------------------------------------------------------------------------- -'' Use/modify as you like. You may want to try Fourspriter MK3 by Britlion, -'' which works differently and is really faster. Check ZX Basic forums at -'' http://www.boriel.com/forum/how-to-tutorials/topic400.html -'' ------------------------------------------------------------------------- - -'' ------------------------------------------------------------------------- -'' Version story -'' ------------------------------------------------------------------------- -'' Versión 1.0 :: Complete functionality, uses GDU -'' Versión 1.1 :: Uses the CHARS system var (23606+256*23607+256). -'' Versión 2.0 :: Complete recode for faster functionality. -'' Versión 2.1 :: Inhouse version, now embedded in a BAS file for ZX Basic -'' Uses a custom variable to locate graphics data, CHARS no -'' longer needed. BASIC/asm interaction with ideas from -'' Britlion's version. -'' Versión 2.2 :: Some memory optimizations, and library adaptation. -'' ------------------------------------------------------------------------- - -#ifndef _MJ_FOURSPRITER_ -#define _MJ_FOURSPRITER_ - -#pragma push(case_insensitive) -#pragma case_insensitive = true - -Sub MJfspMoveSprite (n As uByte, x As uByte, y As uByte) - ' Moves sprite #n to new coordinates (x, y) - - Dim dataBlock As uInteger - - ' 50 * n = 48 * n + 2 * n = 32 * n + 16 * n + 2 * n = (n << 5) + (n << 4) + (n << 1) - dataBlock = (n << 5) + (n << 4) + (n << 1) + @MJfspDataPool - Poke dataBlock + 2, x - Poke dataBlock + 3, y -End Sub - - -Sub MJfspColourSprite (n As uByte, attr1 As uByte, attr2 As uByte, attr3 As uByte, attr4 As uByte) - ' Colours sprite #n - - Dim dataBlock As uInteger - - ' 50 * n = 48 * n + 2 * n = 32 * n + 16 * n + 2 * n = (n << 5) + (n << 4) + (n << 1) - dataBlock = (n << 5) + (n << 4) + (n << 1) + @MJfspDataPool - Poke dataBlock + 42, attr1 - Poke dataBlock + 43, attr2 - Poke dataBlock + 44, attr3 - Poke dataBlock + 45, attr4 -End Sub - -Sub MJfspSetGfxSprite (n As uByte, char1 As uByte, char2 As uByte, char3 As uByte, char4 As uByte) - ' Sets sprite #n's four characters - - Dim dataBlock As uInteger - - ' 50 * n = 48 * n + 2 * n = 32 * n + 16 * n + 2 * n = (n << 5) + (n << 4) + (n << 1) - dataBlock = (n << 5) + (n << 4) + (n << 1) + @MJfspDataPool - Poke dataBlock + 6, char1 - Poke dataBlock + 7, char2 - Poke dataBlock + 8, char3 - Poke dataBlock + 9, char4 -End Sub - -Sub MJfspActivateSprite (n As uByte) - ' Activates sprite #n - - Dim dataBlock As uInteger - - ' 50 * n = 48 * n + 2 * n = 32 * n + 16 * n + 2 * n = (n << 5) + (n << 4) + (n << 1) - dataBlock = (n << 5) + (n << 4) + (n << 1) + @MJfspDataPool - Poke dataBlock, 1 -End Sub - -Sub MJfspDeactivateSprite (n As uByte) - ' Activates sprite #n - - Dim dataBlock As uInteger - - ' 50 * n = 48 * n + 2 * n = 32 * n + 16 * n + 2 * n = (n << 5) + (n << 4) + (n << 1) - dataBlock = (n << 5) + (n << 4) + (n << 1) + @MJfspDataPool - Poke dataBlock, 0 -End Sub - -Sub MJfspDuplicateCoordinatesSprite (n As uByte) - ' Does CX = X and CY = Y for Sprite #n, used when defining the sprite. - - Dim dataBlock As uInteger - - ' 50 * n = 48 * n + 2 * n = 32 * n + 16 * n + 2 * n = (n << 5) + (n << 4) + (n << 1) - dataBlock = (n << 5) + (n << 4) + (n << 1) + @MJfspDataPool - Poke dataBlock + 4, Peek (dataBlock + 2) - Poke dataBlock + 5, Peek (dataBlock + 3) -End Sub - -Sub MJfspSetGfxAddress (address As uInteger) - ' Sets the address where graphics data is read. - - Dim dataBlock As uInteger - - dataBlock = @MJfspDataPool + 200 - Poke uInteger dataBlock, address -End Sub - -Sub Fastcall MJfspInitSprites () - ' Calls init_sprite routine in fsp 2.1 - - Asm - call init_sprites - End asm -End Sub - -Sub FastCall MJfspBorraSprites () - ' Calls borra_sprites routine in fsp 2.1 - - Asm - call borra_sprites - End Asm -End Sub - -Sub FastCall MJfspPintaSprites () - ' Calls pinta_sprites routine in fsp 2.1 - - Asm - call pinta_sprites - End Asm -End Sub - -Sub FastCall MJfspUpdateCoordinates () - ' Calls upd_coord routine in fsp 2.1 - - Asm - call upd_coord - End Asm -End Sub - -Sub FastCall MJfspUpdateSprites () - ' Calls upd_sprites routine in fsp 2.1 - ' Basicly makes changes. Erases sprites, stores background, and repaints them - - Asm - call upd_sprites - End Asm -End Sub - -'' --------------------------------------------------------------------------- -'' End of BASIC interface. Now the core code. -'' --------------------------------------------------------------------------- - -Sub FastCall MJfspDummyContainer () - - ' This sub is not executable, just contains the ASM code and variables - ' which is called/modified from within other Subs in this module. - -MJfspDataPool: - Asm - PROC ;; Comenzamos un nuevo PROCedure => Se hace un push de los nombres - - ;; Aquí vienen todos los datos. Se pokean desde BASIC - - LOCAL datap - datap: ; Cada bloque ocupa 50 bytes. - - ;; Las etiquetas empleadas más abajo son puramente orientativas y no se - ;; emplean desde el código. Sin embargo, aparecerán en un archivo de tabla - ;; de símbolos, lo cual puede resultar útil si no se tiene ganas de calcular - ;; cosas. - - ;; Sprite 1 - - LOCAL act1, flags1, x_pos1, y_pos1, cx_pos1, cy_pos1, udg1, buffer1, attrs1, buffatrs1 - - act1: defb 0 ; Define si el sprite está activo - flags1: defb 0 ; Por ahora sin uso. - x_pos1: defb 0 ; Posición X en chars. - y_pos1: defb 0 ; Posición Y en chars. - cx_pos1: defb 0 ; Anterior posición X en chars. - cy_pos1: defb 0 ; Anterior posición Y en chars. - udg1: defb 0, 0, 0, 0 ; Los cuatro CHAR del primer sprite. - buffer1: defs 32, 0 ; Buffer de lo que había en la pantalla. - attrs1: defb 7, 7, 7, 7 ; Cuatro atributos - buffatrs1: defs 4, 0 ; Buffer de los atributos - - ;; Sprite 2 - - LOCAL act2, flags2, x_pos2, y_pos2, cx_pos2, cy_pos2, udg2, buffer2, attrs2, buffatrs2 - - act2: defb 0 ; Define si el sprite está activo - flags2: defb 0 ; Por ahora sin uso. - x_pos2: defb 0 ; Posición X en chars. - y_pos2: defb 0 ; Posición Y en chars. - cx_pos2: defb 0 ; Anterior posición X en chars. - cy_pos2: defb 0 ; Anterior posición Y en chars. - udg2: defb 0, 0, 0, 0 ; Los cuatro CHAR del segundo sprite. - buffer2: defs 32, 0 ; Buffer de lo que había en la pantalla. - attrs2: defb 7, 7, 7, 7 ; Cuatro atributos - buffatrs2: defs 4, 0 ; Buffer de los atributos - - ;; Sprite 3 - - LOCAL act3, flags3, x_pos3, y_pos3, cx_pos3, cy_pos3, udg3, buffer3, attrs3, buffatrs3 - - act3: defb 0 ; Define si el sprite está activo - flags3: defb 0 ; Por ahora sin uso. - x_pos3: defb 0 ; Posición X en chars. - y_pos3: defb 0 ; Posición Y en chars. - cx_pos3: defb 0 ; Anterior posición X en chars. - cy_pos3: defb 0 ; Anterior posición Y en chars. - udg3: defb 0, 0, 0, 0 ; Los cuatro CHAR del tercer sprite. - buffer3: defs 32, 0 ; Buffer de lo que había en la pantalla. - attrs3: defb 7, 7, 7, 7 ; Cuatro atributos - buffatrs3: defs 4, 0 ; Buffer de los atributos - - ;; Sprite 4 - - LOCAL act4, flags4, x_pos4, y_pos4, cx_pos4, cy_pos4, udg4, buffer4, attrs4, buffatrs4 - - act4: defb 0 ; Define si el sprite está activo - flags4: defb 0 ; Por ahora sin uso. - x_pos4: defb 0 ; Posición X en chars. - y_pos4: defb 0 ; Posición Y en chars. - cx_pos4: defb 0 ; Anterior posición X en chars. - cy_pos4: defb 0 ; Anterior posición Y en chars. - udg4: defb 0, 0, 0, 0 ; Los cuatro CHAR del cuarto sprite. - buffer4: defs 32, 0 ; Buffer de lo que había en la pantalla. - attrs4: defb 7, 7, 7, 7 ; Cuatro atributos - buffatrs4: defs 4, 0 ; Buffer de los atributos - - ;; Where to extract graphics data - - LOCAL setAddrLsb, setAddrMsb - - setAddrLsb: defb 0 - setAddrMsb: defb 0 - - ;; General y guarro, que todavía estoy empezando: - - LOCAL xpos, ypos, cxpos, cypos - - xpos: defb 0 - ypos: defb 0 - cxpos: defb 0 - cypos: defb 0 - - ;; --------------------------------------------------------------------------- - ;; rutina init_sprites - ;; --------------------------------------------------------------------------- - - ;; Primero tendremos que llamar a esta función que escribe lo que haya en - ;; en fondo en el buffer de cada sprite. - - init_sprites: - - ld de, datap ; Apuntamos a la zona de datos - - ld b, 4 ; 4 iteraciones - - LOCAL init_loop - init_loop: - push bc - - ;; Primero vemos si el sprite está activo - ld a, (de) - cp 0 - jr z, init_adv ; Si no está activo, nos lo saltamos - inc de - - ;; Por ahora nos saltamos los flags - inc de ; Ahora DE->X - - ;; Obtenemos las coordenadas X e Y - ld hl, xpos ; HL->XPOS - ex de, hl ; Cambiamos HL por DE. Ahora DE->XPOS, HL->X - ldi ; XPOS = X; DE->YPOS, HL->Y - ldi ; YPOS = Y; DE->YPOS+1, HL->Y+1 - ex de, hl ; Deshacemos el cambio. Ahora DE->Y+1=CX - - ; Nos saltamos CX y CY: - inc de ; Ahora DE->CY - inc de ; Ahora DE->UDG - - ;; Tenemos en nuestras variables XPOS e YPOS las coordenadas. - ;; Ahora tenemos que copiar los cuatro chars del bitmap al buffer - - ;; Avanzamos DE al buffer, para ello nos saltamos los 4 UDG - inc de - inc de - inc de - inc de ; Ahora DE->BUFFER - - call scr2buf - - ;; Ahora DE->ATTRS. Hay que avanzar hasta BUFATRS, 4 bytes más allá - inc de - inc de - inc de - inc de - - ;; reposicionamos las coordenadas xpos e ypos restando 1 a cada una: - - ; xpos -- - ld hl, xpos - dec (hl) - - ; ypos -- - inc hl - dec (hl) - - ; Calculamos la dirección de los atributos en HL - call getatraddr ; HL->atributos, DE->buffer_atributos - - ;; Copiamos los cuatro atributos - ldi ; Primer carácter - ldi ; Segundo carácter - ld bc, 30 - add hl, bc - ldi ; Tercer carácter - ldi ; Cuarto carácter - - ; Ahora DE apunta al principio del siguiente sprite. - - LOCAL init_nxt - init_nxt: pop bc - djnz init_loop - - ret - - LOCAL init_adv - init_adv: ld hl, 50 ; Sumamos 50 a de y seguimos - add hl, de - ex de, hl - jp init_nxt - - ;; --------------------------------------------------------------------------- - ;; rutina borra_sprites - ;; --------------------------------------------------------------------------- - - ;; Esta rutina restaura el fondo almacenado en los sprites en las coordenadas - ;; CX,CY (anteriores coordenadas) - - borra_sprites: - - ld de, datap ; Apuntamos a la zona de datos - - ld b, 4 ; 4 iteraciones - - LOCAL borra_loop - borra_loop: - push bc - - ;; Primero vemos si el sprite está activo - ld a, (de) - cp 0 - jr z, borra_adv ; Si no está activo, nos lo saltamos - inc de - - ;; Por ahora nos saltamos los flags - inc de ; Ahora DE->X - - ;; Obtenemos las coordenadas CX y CY - inc de ; Nos saltamos X, ahora DE->Y - inc de ; Nos saltamos Y, ahora DE->CX - ld hl, xpos ; HL->XPOS - ex de, hl ; Cambiamos HL por DE. Ahora DE->XPOS, HL->CX - ldi ; XPOS = CX; DE->YPOS, HL->CY - ldi ; YPOS = CY; DE->YPOS+1, HL->CY+1 - ex de, hl ; Deshacemos el cambio. Ahora DE->CY+1=UDG - - ;; Tenemos en nuestras variables XPOS e YPOS las coordenadas. - ;; Ahora tenemos que copiar los cuatro chars del buffer al bitmap - - ;; Avanzamos DE al buffer, para ello nos saltamos los 4 UDG - inc de - inc de - inc de - inc de ; Ahora DE->BUFFER - - call buf2scr - - ;; Ahora DE->ATTRS. Hay que avanzar hasta BUFATRS, 4 bytes más allá - inc de - inc de - inc de - inc de - - ;; reposicionamos las coordenadas xpos e ypos restando 1 a cada una: - - ; xpos -- - ld hl, xpos - dec (hl) - - ; ypos -- - inc hl - dec (hl) - - ; Calculamos la dirección de los atributos en HL - call getatraddr ; HL->atributos, DE->buffer_atributos - - ;; Copiamos los cuatro atributos - ex de, hl ; HL->buffer_atributos, DE->atributos - ldi ; Primer carácter - ldi ; Segundo carácter - ex de, hl - ld bc, 30 - add hl, bc - ex de, hl - ldi ; Tercer carácter - ldi ; Cuarto carácter - ex de, hl ; DE vuelve a apuntar a los datos... - - ; Ahora DE apunta al principio del siguiente sprite. - - LOCAL borra_nxt - borra_nxt: - pop bc - djnz borra_loop - - ret - - LOCAL borra_adv - borra_adv: - ld hl, 50 ; Sumamos 50 a de y seguimos - add hl, de - ex de, hl - jp borra_nxt - - ;; --------------------------------------------------------------------------- - ;; rutina pinta_sprites - ;; --------------------------------------------------------------------------- - - ;; Esta rutina restaura pinta los chars especificados en la tabla UDG - ;; En las coordenadas X, Y del sprite - - pinta_sprites: - - ld de, datap ; Apuntamos a la zona de datos - - ld b, 4 ; 4 iteraciones - - LOCAL pinta_loop - pinta_loop: - push bc - - ;; Primero vemos si el sprite está activo - ld a, (de) - cp 0 - jr z, pinta_adv ; Si no está activo, nos lo saltamos - inc de - - ;; Por ahora nos saltamos los flags - inc de ; Ahora DE->X - - ;; Obtenemos las coordenadas X e Y - ld hl, xpos ; HL->XPOS - ex de, hl ; Cambiamos HL por DE. Ahora DE->XPOS, HL->X - ldi ; XPOS = X; DE->YPOS, HL->Y - ldi ; YPOS = Y; DE->YPOS+1, HL->Y+1 - ex de, hl ; Deshacemos el cambio. Ahora DE->Y+1=CX - - ; Nos saltamos CX y CY: - inc de ; Ahora DE->CY - inc de ; Ahora DE->UDG - - ;; DE->UDG. Ahora hay que pintar los cuatro carácteres indexados ahí. - - call char2scr - - ;; DE->BUFFER. Tenemos que saltarnos 32 bytes para acceder - ;; a los atributos del sprite: - - ld hl, 32 - add hl, de - ex de, hl ; Ahora DE->ATTRS - - ;; Pintamos los 4 atributos - - ;; reposicionamos las coordenadas xpos e ypos restando 1 a cada una: - - ; xpos -- - ld hl, xpos - dec (hl) - - ; ypos -- - inc hl - dec (hl) - - ; Calculamos la dirección de los atributos en HL - call getatraddr ; HL->atributos, DE->ATTRS - - - ;; Efecto dandaresco: sólo imprimimos bright/ink - ;; pero tomamos el paper que haya. - - ;; --------------------------------------------------------------- - - ; 1 - ld a, (hl) ; a<- atributo en pantalla - and 56 ; 00111000 (nos quedamos con PAPER) - ld b, a - ld a, (de) - and 199 ; 11000111 (quitamos el PAPER) - or b ; Le pegamos el paper que había - ld (hl), a ; escribimos - inc hl - inc de ; siguiente - - ; 2 - ld a, (hl) ; a<- atributo en pantalla - and 56 ; 00111000 (nos quedamos con PAPER) - ld b, a - ld a, (de) - and 199 ; 11000111 (quitamos el PAPER) - or b ; Le pegamos el paper que había - ld (hl), a ; escribimos - inc de - ld bc, 31 - add hl, bc - - ; 3 - ld a, (hl) ; a<- atributo en pantalla - and 56 ; 00111000 (nos quedamos con PAPER) - ld b, a - ld a, (de) - and 199 ; 11000111 (quitamos el PAPER) - or b ; Le pegamos el paper que había - ld (hl), a ; escribimos - inc hl - inc de ; siguiente - - ; 4 - ld a, (hl) ; a<- atributo en pantalla - and 56 ; 00111000 (nos quedamos con PAPER) - ld b, a - ld a, (de) - and 199 ; 11000111 (quitamos el PAPER) - or b ; Le pegamos el paper que había - ld (hl), a ; escribimos - inc hl - inc de ; siguiente - - ;; --------------------------------------------------------------- - - ;; Avanzamos DE hasta el siguiente carácter: - inc de - inc de - inc de - inc de - - ; Ahora DE apunta al principio del siguiente sprite. - - LOCAL pinta_nxt - pinta_nxt: pop bc - djnz pinta_loop - - ret - - LOCAL pinta_adv - pinta_adv: ld hl, 50 ; Sumamos 50 a de y seguimos - add hl, de - ex de, hl - jp pinta_nxt - - ;; --------------------------------------------------------------------------- - ;; rutina upd_coord - ;; --------------------------------------------------------------------------- - - ;; Esta rutina hace CX = X, CY = Y. - - upd_coord: ld de, datap ; Apuntamos a la zona de datos - ld hl, datap ; Apuntamos a la zona de datos - - ld b, 4 ; 4 iteraciones - - LOCAL upd_loop - upd_loop: - push bc - - inc hl - inc hl ; HL->X - inc de - inc de - inc de - inc de ; DE->CX - ldi ; CX=X; HL->Y, DE->CY - ldi ; CY=Y; HL->CX, DE->UDG - - ; avanzar hasta el siguiente sprite: - - ;; DE = DATAP+6, sumamos 44 - - ld hl, 44 - add hl, de - ld d, h - ld e, l - - ; Ahora DE apunta al principio del siguiente sprite. - - LOCAL upd_nxt - upd_nxt: - pop bc - djnz upd_loop - - ret - - ;; --------------------------------------------------------------------------- - ;; rutina upd_sprites - ;; --------------------------------------------------------------------------- - - ;; Esta rutina llama a las cuatro anteriores para hacer la animación - ;; sincronizando con el retrazo - - upd_sprites: - halt - call borra_sprites - call init_sprites - call pinta_sprites - call upd_coord - ret - - ;; --------------------------------------------------------------------------- - ;; subrutinas - ;; --------------------------------------------------------------------------- - - ;; Esta subrutina copia el rectángulo del bitmap al buffer apuntado por DE. - - scr2buf: call char2buff ; HL = Dirección en el bitamp de XPOS,YPOS - - ;; Segundo char - ; xpos++ - ld hl, xpos - inc (hl) - call char2buff ; HL = Dirección en el bitamp de XPOS,YPOS - - ;; Tercer char - ; xpos-- - ld hl, xpos - dec (hl) - ; ypos++ - inc hl - inc (hl) - call char2buff ; HL = Dirección en el bitamp de XPOS,YPOS - - ;; Cuarto char - ; xpos++ - ld hl, xpos - inc (hl) - jp char2buff ; HL = Dirección en el bitamp de XPOS,YPOS - - ;; Esta subrutina copia el buffer apuntado por DE al bitmap - - buf2scr: ;; Primer char - call buf2chrscr ; HL = Dirección en el bitamp de XPOS,YPOS - - ;; Segundo char - ; xpos++ - ld hl, xpos - inc (hl) - call buf2chrscr ; HL = Dirección en el bitamp de XPOS,YPOS - - ;; Tercer char - ; xpos-- - ld hl, xpos - dec (hl) - ; ypos++ - inc hl - inc (hl) - call buf2chrscr ; HL = Dirección en el bitamp de XPOS,YPOS - - ;; Cuarto char - ; xpos++ - ld hl, xpos - inc (hl) - jp buf2chrscr ; HL = Dirección en el bitamp de XPOS,YPOS - - ;; Esta rutina apunta los chars en el buffer apuntado por DE a pantalla. - - char2scr: ;; Primer carácter - - ld a, (de) ; En A el # del carácter - push de ; Nos guardamos DE para luego - call chr2scr - pop de ; Restauramos de - - ;; Segundo carácter - inc de ; Siguiente char - ; xpos++ - ld hl, xpos - inc (hl) - - ld a, (de) ; En A el # del carácter - push de ; Nos guardamos DE para luego - call chr2scr - pop de ; Restauramos de - - ;; Tercer carácter - inc de ; Siguiente char - ; xpos-- - ld hl, xpos - dec (hl) - ; ypos ++ - inc hl - inc (hl) - - ld a, (de) ; En A el # del carácter - push de ; Nos guardamos DE para luego - call chr2scr - pop de ; Restauramos de - - ;; Cuarto carácter - inc de ; Siguiente char - ; xpos++ - ld hl, xpos - inc (hl) - - ld a, (de) ; En A el # del carácter - push de ; Nos guardamos DE para luego - call chr2scr - - pop de ; Restauramos de - inc de ; Fin de los UDG - - ret - - - ;; Esta subrutina devuelve en HL la dirección de memoria del atributo en las - ;; coordenadas XPOS,YPOS. Jonnathan Cauldwell ? - - getatraddr: ld a, (ypos) ; Cogemos y - rrca - rrca - rrca ; La multiplicamos por 32 - ld l, a ; nos lo guardamos en l - and 3 ; ponemos una mascarita 00000011 - add a, 88 ; 88 * 256 = 22528, aquí empieza el tema - ld h, a ; Hecho el bite superior. - ld a, l ; Nos volvemos a traer y * 32 - and 224 ; Mascarita 11100000 - ld l, a ; Lo volvemos a poner en l - ld a, (xpos) ; Cogemos x - add a, l ; Le sumamos lo que teníamos antes. - ld l, a ; Listo. Ya tenemos en HL la dirección. - - ret - - - LOCAL char2buff - ;; Copia un caracter en pantalla (xpos, ypos) a @DE - char2buff: - ;; Calcula en HL la dirección de memoria de las coordenadas - ;; XPOS,YPOS. Inspirado por código de Bloodbaz. - ld a, (ypos) ; Cogemos y - rrca - rrca - rrca ; La multiplicamos por 32 - ld l, a ; nos lo guardamos en l - and 3 ; ponemos una mascarita 00000011 - add a, 88 ; 88 * 256 = 22528, aquí empieza el tema - ld h, a ; Hecho el bite superior. - ld a, l ; Nos volvemos a traer y * 32 - and 224 ; Mascarita 11100000 - ld l, a ; Lo volvemos a poner en l - ld a, (xpos) ; Cogemos x - add a, l ; Le sumamos lo que teníamos antes. - ld l, a ; Listo. Ya tenemos en HL la dirección. - - ;; Ahora copiamos el caracter de pantalla a memoria - ld a, (hl) - ld (de), a - inc h - inc de - ld a, (hl) - ld (de), a - inc h - inc de - ld a, (hl) - ld (de), a - inc h - inc de - ld a, (hl) - ld (de), a - inc h - inc de - ld a, (hl) - ld (de), a - inc h - inc de - ld a, (hl) - ld (de), a - inc h - inc de - ld a, (hl) - ld (de), a - inc h - inc de - ld a, (hl) - ld (de), a - inc de - - ret - - - LOCAL chr2scr - chr2scr: - ;; Toma un carácter apuntado por A y lo vuelca en pantalla - ;; en las coordenadas (xpos, ypos) - - ;; Primero calcula la posición de memoria del carácter en A - ;; y la devuelve en HL. Pepito Grillo en pijama. - ld b, a - - ld a, (setAddrLsb) - ld e, a - - ld a, (setAddrMsb) - ld d, a - ld a, b - - ld h, 0 - ld l, a - add hl, hl - add hl, hl - add hl, hl - add hl, de - - ex de, hl ; DE ahora apunta al carácter - - LOCAL buf2chrscr - ;; Copia un caracter en @DE a pantalla (xpos, ypos) - buf2chrscr: - ld a, (ypos) ; Cogemos y - rrca - rrca - rrca ; La multiplicamos por 32 - ld l, a ; nos lo guardamos en l - and 3 ; ponemos una mascarita 00000011 - add a, 88 ; 88 * 256 = 22528, aquí empieza el tema - ld h, a ; Hecho el bite superior. - ld a, l ; Nos volvemos a traer y * 32 - and 224 ; Mascarita 11100000 - ld l, a ; Lo volvemos a poner en l - ld a, (xpos) ; Cogemos x - add a, l ; Le sumamos lo que teníamos antes. - ld l, a ; Listo. Ya tenemos en HL la dirección. - - ld a, (de) - ld (hl), a - inc h - inc de - ld a, (de) - ld (hl), a - inc h - inc de - ld a, (de) - ld (hl), a - inc h - inc de - ld a, (de) - ld (hl), a - inc h - inc de - ld a, (de) - ld (hl), a - inc h - inc de - ld a, (de) - ld (hl), a - inc h - inc de - ld a, (de) - ld (hl), a - inc h - inc de - ld a, (de) - ld (hl), a - inc de - - ret - - ENDP ;; Cerramos el bloque de PROCedure y se "olvidan" todas las etiquetas locales - End Asm -End Sub - -#pragma pop(case_insensitive) - -#endif - +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/point.bas b/src/lib/arch/zxnext/stdlib/point.bas index 0568de015..6896a61c4 100644 --- a/src/lib/arch/zxnext/stdlib/point.bas +++ b/src/lib/arch/zxnext/stdlib/point.bas @@ -1,86 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- - -#ifndef __LIBRARY_POINT__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_POINT__ - -#pragma push(case_insensitive) -#pragma case_insensitive = TRUE - -' ---------------------------------------------------------------- -' function POINT(x, y) -' -' Parameters: -' x: X coord (0 to 255) -' y: Y coord (0 to 191) ' -' Returns: -' 1 if point is plot 0 otherwise (byte) +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -function fastcall point(x as ubyte, y as ubyte) as byte - asm - - PROC - LOCAL PIXEL_ADDR - LOCAL POINT_LOOP - LOCAL POINT_END - LOCAL POINT_1 - -PIXEL_ADDR EQU (22AAh + 6) ; ROM addrs which calculate screen addr - - pop hl - ex (sp), hl ; callee => put RET address back. H = y - ld b, h - ld c, a - -#ifdef SCREEN_Y_OFFSET - ld a, SCREEN_Y_OFFSET - add a, b - ld b, a -#endif - -#ifdef SCREEN_X_OFFSET - ld a, SCREEN_X_OFFSET - add a, c - ld c, a -#endif - - ld a, 191 ; Max y value - sub b - jp nc, POINT_1 ; Error checking here - - ld a, -1 - jr POINT_END - -POINT_1: - call PIXEL_ADDR - - ; Ripped from the ZX ROM - - ld b, a ; pixel position to B, 0-7. - inc b ; increment to give rotation count 1-8. - ld a, (hl) ; fetch byte from screen. - -POINT_LOOP: - rlca ; rotate and loop back - djnz POINT_LOOP ; to POINT-LP until pixel at right. - and 1 ; test to give zero or one. - -POINT_END: - ENDP - - end asm -end function - -#pragma pop(case_insensitive) - - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/pos.bas b/src/lib/arch/zxnext/stdlib/pos.bas index 7badcdb29..c460d50fc 100644 --- a/src/lib/arch/zxnext/stdlib/pos.bas +++ b/src/lib/arch/zxnext/stdlib/pos.bas @@ -2,43 +2,7 @@ ' This file is released under the MIT License ' ' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' -' Use this file as a template to develop your own library file -' ---------------------------------------------------------------- - -#ifndef __LIBRARY_POS__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_POS__ - -#pragma push(case_insensitive) -#pragma case_insensitive = true - -REM Put your code here. - -' ---------------------------------------------------------------- -' function pos() -' -' Returns: -' Current COLUMN print position +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -function FASTCALL pos as ubyte - asm - push namespace core - PROC - - call __LOAD_S_POSN - ld a, e - - ENDP - pop namespace - end asm -end function - -#pragma pop(case_insensitive) -#require "sposn.asm" - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/print42.bas b/src/lib/arch/zxnext/stdlib/print42.bas index bbe8ba44c..1560d91a6 100644 --- a/src/lib/arch/zxnext/stdlib/print42.bas +++ b/src/lib/arch/zxnext/stdlib/print42.bas @@ -1,527 +1,8 @@ -' vim:ts=4:et: - -#ifndef __PRINT42__ -#define __PRINT42__ - -' -------------------------------------------------- -' A PRINT Routine with 42 COLUMNS! -' -' Contributed by Britlion -' -------------------------------------------------- - -#pragma push(case_insensitive) -#pragma case_insensitive = TRUE - -SUB printat42 (y as uByte, x as uByte) - POKE @printAt42Coords,x - POKE @printAt42Coords+1,y -END SUB - - -SUB print42(characters$ as string) -asm - PROC - - LD A, H - OR L - JP Z, print42end - - LD C,(HL) - INC HL - LD B,(HL) ; all told, LD BC with the length of the string. - - LD A, C - OR B - JP Z, print42end ; Is the length of the string 0? If so, quit. - - INC HL ;Puts HL to the first real character in the string. - -LOCAL examineChar -examineChar: - LD A,(HL) ; Grab the character at our pointer position - CP 128 ; Too high to print? - JR NC, nextChar ; Then we go to the next - - CP 22 ; Is this an AT? - JR NZ, isNewline ; If not jump over the AT routine to isNewline - -LOCAL isAt -isAt: - EX DE,HL ; Get DE to hold HL for a moment - ;;AND A ; Plays with the flags. One of the things it does is reset Carry. - ;;LD HL,00002 - ;;SBC HL,BC ; Subtract length of string from HL. - LD HL, -2 - ADD HL, BC - EX DE,HL ; Get HL back from DE - JP NC, print42end ; If the result WASN'T negative, return. (We need AT to have parameters to make sense) - - INC HL ; Onto our Y co-ordinate - LD D,(HL) ; Put it in D - DEC BC ; and move our string remaining counter down one - INC HL ; Onto our X co-ordinate - LD E,(HL) ; Put the next one in E - DEC BC ; and move our string remaining counter down one - ld (xycoords), de - JR nextChar - -LOCAL isNewline -isNewline: - CP 13 ; Is this character a newline? - JR NZ, checkdel ; If not, jump forward - -LOCAL newline -newline: - ld de, (xycoords) - CALL nxtline ; move to next line - ld (xycoords), de - JR nextChar - -LOCAL checkdel -checkdel: - CP 8 - JR NZ, checkvalid - ld de, (xycoords) - dec de - ld (xycoords), de - ld a, 41 - cp e - JR NC, nextChar - ld e, a - ld (xycoords), de - ld a, 23 - cp d - JR NC, nextChar - ld d, a - ld (xycoords), de - JR nextChar - -LOCAL checkvalid -checkvalid: - CP 31 ; Is character <31? - JR C, nextChar ; If not go to next character - -LOCAL prn -prn: - PUSH HL ; Save our position - PUSH BC ; Save our countdown of chars left - CALL printachar ; Go print a character - POP BC ; Recover our count - POP HL ; Recover our position - -LOCAL nextChar -nextChar: - INC HL ; Move to the next position - DEC BC ; count off a character - LD A,B - OR C ; Did we hit the end of our string? (BC=0?) - JR NZ, examineChar ; If not, we need to go look at the next character. - JP print42end ; End the print routine - - -; This routine forms the new 6-bit wide characters and -; alters the colours to match the text. The y,x co-ordinates and eight -; bytes of workspace are located at the end of this chunk. -; it starts with the character ascii code in the accumulator - -LOCAL printachar -printachar: - EXX - PUSH HL ; Store H'L' where we can get it. - EXX - - ld c, a ; Put a copy of the character in C - ld h, 0 - ld l, a ; Put the Character in HL - - ld de, whichcolumn-32 ; the character is at least 32, so space = 0th entry. - add hl, de ; HL -> table entry for char. - ld a, (hl) ; Load our column slice data from the table. - cp 32 ; Is it less than 32? - jr nc, calcChar ; If so, go to the calculated character subroutine - -; This is the special case 'we defined the character in the table' option - ld de, characters ; Point DE at our table - ld l, a ; Put our character number from our table lookup that's in HL in a - call mult8 ; multiplies L by 8 and adds in DE [so HL points at our table entry] - ld b, h - ld c, l ; Copy our character data address into BC - jr printdata ; We have our data source, so we print it. - -LOCAL calcChar -calcChar: ; this is the calculate from the ROM data option - ; a holds the column kill data - ld de, 15360 ; Character set-256. We could use CHARS here, maybe; but might not work with a redefiend character set. - ld l, c ; Get our character back from C - call mult8 ; Multiply l by 8 and add to DE. (HL points at the ROM data for our character now) - - ld de, workspace ; Point DE at our 8 byte workspace. - push de ; Save it - exx ; - ld c, a ; Put our kill column in C' - cpl ; Invert - ld b, a ; Put the inverse in B' - exx ; - ld b, 8 ; 8 bytes to a character loop counter - -LOCAL loop1 -loop1: - ld a, (hl) ; Load a byte of character data - inc hl ; point at the next byte - exx ; - ld e, a ; Put it in e' - and c ; keep the left column block we're using - ld d, a ; and put it in d' - ld a, e ; grab our original back - rla ; shift it left (which pushes out our unwanted column) - and b ; keep just the right block - or d ; mix with the left block - exx ; - ld (de), a ; put it into our workspace - inc de ; next workspace byte - djnz loop1 ; go round for our other bytes - - pop bc ; Recover a pointer to our workspace. - -LOCAL printdata -printdata: - call testcoords ; check our position, and wrap around if necessary. [returns with d=y,e=x] - inc e ; Bump along to next co-ordinate - ld (xycoords), de ; Store our coordinates for the next character - dec e ; Bump back to our current one - ld a, e ; get x - sla a ; Shift Left Arithmetic - *2 - ld l, a ; put x*2 into L - sla a ; make it x*4 - add a, l ; (x*2)+(x*4)=6x - ld l, a ; put 6x into L [Since we're in a 6 pixel font, L now contains the # of first pixel we're interested in] - srl a ; divide by 2 - srl a ; divide by another 2 (/4) - srl a ; divide by another 2 (/8) - ld e, a ; Put the result in e (Since the screen has 8 pixel bytes, pixel/8 = which char pos along our first pixel is in) - ld a, l ; Grab our pixel number again - and 7 ; And do mod 8 [So now we have how many pixels into the character square we're starting at] - push af ; Save A - ex af, af' - ld a, d ; Put y Coord into A' - sra a ; Divide by 2 - sra a ; Divide by another 2 (/4 total) - sra a ; Divide by another 2 (/8) [Gives us a 1/3 of screen number] - add a, 88 ; Add in start of screen attributes high byte - ld h, a ; And put the result in H - ld a, d ; grab our Y co-ord again - and 7 ; Mod 8 (why? *I thought to give a line in this 1/3 of screen, but we're in attrs here) - rrca ; - rrca - rrca ; Bring the bottom 3 bits to the top - Multiply by 32 - ; (since there are 32 bytes across the screen), here, in other words. [Faster than 5 SLA instructions] - add a, e ; add in our x coordinate byte to give us a low screen byte - ld l, a ; Put the result in L. So now HL -> screen byte at the top of the character - - ld a, (23693) ; ATTR P Permanent current colours, etc (as set up by colour statements). - ld e, a ; Copy ATTR into e - ld (hl), e ; Drop ATTR value into screen - inc hl ; Go to next position along - pop af ; Pull how many pixels into this square we are - cp 3 ; It more than 2? - jr c, hop1 ; No? It all fits in this square - jump changing the next attribute - - ld (hl), e ; 63446 Must be yes - we're setting the attributes in the next square too. - -LOCAL hop1 -hop1: - dec hl ; Back up to last position - ld a, d ; Y Coord into A' - and 248 ; Turn it into 0,8 or 16. (y=0-23) - add a, 64 ; Turn it into 64,72,80 [40,48,50 Hex] for high byte of screen pos - ld h, a ; Stick it in H - push hl ; Save it - exx ; Swap registers - pop hl ; Put it into H'L' - exx ; Swap Back - ld a, 8 - -LOCAL hop4 -hop4: - push af ; Save Accumulator - ld a, (bc) ; Grab a byte of workspace - exx ; Swap registers - push hl ; Save h'l' - ld c, 0 ; put 0 into c' - ld de, 1023 ; Put 1023 into D'E' - ex af, af' ; Swap AF - and a ; Flags on A - jr z, hop3 ; If a is zero jump forward - - ld b, a ; A -> B - ex af, af' ; Swap to A'F' - -LOCAL hop2 -hop2:; Slides a byte right to the right position in the block (and puts leftover bits in the left side of c) - and a ; Clear Carry Flag - rra ; Rotate Right A - rr c ; Rotate right C (Rotates a carry flag off A and into C) - scf ; Set Carry Flag - rr d ; Rotate Right D - rr e ; Rotate Right E (D flows into E, with help from the carry bit) - djnz hop2 ; Decrement B and loop back - - ex af, af' - -LOCAL hop3 -hop3: - ex af, af' - ld b, a - ld a, (hl) - and d - or b - ld (hl), a ; Write out our byte - inc hl ; Go one byte right - ld a, (hl) ; Bring it in - and e - or c ; mix those leftover bits into the next block - ld (hl), a ; Write it out again - pop hl - inc h ; Next line - exx - inc bc ; Next workspace byte - pop af - dec a - jr nz, hop4 ; And go back! - - exx ; Tidy up - pop hl ; Clear stack leftovers - exx ; And... - ret ; Go home. - -LOCAL mult8 -mult8: ; Multiplies L by 8 -> HL and adds it to DE. Used for 8 byte table vectors. - ld h, 0 - add hl, hl - add hl, hl - add hl, hl - add hl, de - ret - -LOCAL testcoords -testcoords: - ld de, (xycoords) ; get our current screen co-ordinates (d=y,e=x - little endian) - -LOCAL nxtchar -nxtchar: - ld a, e - cp 42 ; Are we >42? - jr c, ycoord ; if not, hop forward - -LOCAL nxtline -nxtline: - inc d ; if so, so bump us to the next line down - ld e, 0 ; and reset x to left edge - -LOCAL ycoord -ycoord: - ld a, d - cp 24 ; are we >24 lines? - ret c ; if no, exit subroutine - ld d, 0 ; if yes, wrap around to top line again. - ret ; exit subroutine -end asm -printAt42Coords: -asm -LOCAL xycoords -xycoords: - defb 0 ; x coordinate - defb 0 ; y coordinate - -LOCAL workspace -workspace: - defb 0 - defb 0 - defb 0 - defb 0 - defb 0 - defb 0 - defb 0 - defb 0 - -; The data below identifies a column in the character to remove. It consists of 1's -; from the left edge. First zero bit is the column we're removing. -; If the leftmost bit is NOT 1, then the byte represents a redefined character position -; in the lookup table. - -LOCAL whichcolumn -whichcolumn: - defb 254 ; SPACE - defb 254 ; ! - defb 128 ; "" - defb 224 ; # - defb 128 ; $ - defb 0 ; % (Redefined below) - defb 1 ; & (Redefined below) - defb 128 ; ' - defb 128 ; ( - defb 128 ; ) - defb 128 ; * - defb 128 ; + - defb 128 ; , - defb 128 ; - - defb 128 ; . - defb 128 ; / - defb 2 ; 0 (Redefined below) - defb 128 ; 1 - defb 224 ; 2 - defb 224 ; 3 - defb 252 ; 4 - defb 224 ; 5 - defb 224 ; 6 - defb 192 ; 7 - defb 240 ; 8 - defb 240 ; 9 - defb 240 ; : - defb 240 ; ; - defb 192 ; < - defb 240 ; = - defb 192 ; > - defb 192 ; ? - defb 248 ; @ - defb 240 ; A - defb 240 ; B - defb 240 ; C - defb 240 ; D - defb 240 ; E - defb 240 ; F - defb 240 ; G - defb 240 ; H - defb 128 ; I - defb 240 ; J - defb 192 ; K - defb 240 ; L - defb 240 ; M - defb 248 ; N - defb 240 ; O - defb 240 ; P - defb 248 ; Q - defb 240 ; R - defb 240 ; S - defb 3 ; T - defb 240 ; U - defb 240 ; V - defb 240 ; W - defb 240 ; X - defb 4 ; Y - defb 252 ; Z - defb 224 ; [ - defb 252 ; \ - defb 240 ; ] - defb 252 ; ^ - defb 6 ; _ - defb 240 ; UK Pound (Currency) Symbol - defb 255 ; a - defb 128 ; b - defb 255 ; c - defb 255 ; d - defb 255 ; e - defb 255 ; f - defb 255 ; g - defb 255 ; h - defb 255 ; i - defb 255 ; j - defb 255 ; k - defb 255 ; l - defb 255 ; m - defb 255 ; n - defb 255 ; o - defb 255 ; p - defb 255 ; q - defb 255 ; r - defb 255 ; s - defb 255 ; t - defb 255 ; u - defb 255 ; v - defb 255 ; w - defb 255 ; x - defb 255 ; y - defb 255 ; z - defb 128 ; { - defb 128 ; | - defb 255 ; } - defb 128 ; ~ - defb 5 ; (c) end column data - - -LOCAL characters -characters: - defb 0 ; % - defb 0 - defb 100 - defb 104 - defb 16 - defb 44 - defb 76 - defb 0 - - defb 0 ; & - defb 32 - defb 80 - defb 32 - defb 84 - defb 72 - defb 52 - defb 0 - - defb 0 ; digit 0 - defb 56 - defb 76 - defb 84 - defb 84 - defb 100 - defb 56 - defb 0 - - defb 0 ; Letter T - defb 124 - defb 16 - defb 16 - defb 16 - defb 16 - defb 16 - defb 0 - - defb 0 ; Letter Y - defb 68 - defb 68 - defb 40 - defb 16 - defb 16 - defb 16 - defb 0 - - defb 0 ; (c) symbol - defb 48 - defb 72 - defb 180 - defb 164 - defb 180 - defb 72 - defb 48 - - defb 0 - defb 0 - defb 0 - defb 0 - defb 0 - defb 0 - defb 0 - defb 0xFC - -LOCAL print42end -print42end: - ENDP - -end asm -END SUB - -#pragma pop(case_insensitive) - -#endif - +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/print64.bas b/src/lib/arch/zxnext/stdlib/print64.bas index b3ec86092..bee24b0b8 100644 --- a/src/lib/arch/zxnext/stdlib/print64.bas +++ b/src/lib/arch/zxnext/stdlib/print64.bas @@ -1,289 +1,8 @@ -' vim:ts=4:et: -' --------------------------------------------------------- -' 64 Characters wide PRINT Routine for ZX BASIC -' Contributed by Britlion -' --------------------------------------------------------- - -#ifndef __PRINT64__ -#define __PRINT64__ - -#pragma push(case_insensitive) -#pragma case_insensitive = TRUE - -' Changes print coordinates. -SUB printat64 (y as uByte, x as uByte) - POKE @p64coords,x - POKE @p64coords+1,y -END sub - -' Print given string at current position -SUB print64 (characters$ as String) - asm - PROC ; Declares begin of procedure - ; so we can now use LOCAL labels - - LD L,(IX+4) - LD H,(IX+5) ; Get String address of characters$ into HL. - - ld a, h - or l - jp z, p64_END ; Return if NULL string - - ; Load BC with length of string, and move HL to point to first character. - ld c, (hl) - inc hl - ld b, (hl) - inc hl - - ; Test string length. If Zero, exit. - ld a, c - or b - jp z, p64_END - - LOCAL examineChar - examineChar: - ld a, (hl) ; Grab the character - cp 128 ; too high to print? - jr nc, nextChar ; then we go to next. - - cp 22 ; Is this an AT? - jr nz, newLine ; If not, hop to newLine routine. - ex de, hl ; Swap DE and HL - and a ; Clear Carry - ld hl, 2 ; - sbc hl, bc ; Can we Shorten our string length by 2? If not then at (y,x) doesn't make sense. - ex de, hl ; Swap DE and HL back - jp nc, p64_END ; If we went negative, there wasn't anything to AT to, so we return. - - inc hl ; Onto our Y co-ordinate - ld d, (hl) ; And load it into D - dec bc ; Shorten our remaining string counter. - inc hl ; Onto the X Co-ordinate - ld e, (hl) ; Load it into E - dec bc ; Shorten our remaining string counter - call p64_test_X ; Make xy legal 60051 205 53 235 - jr p64_eaa3 ; Go to save coords 60054 24 11 - - LOCAL newLine - newLine: - cp 13 ; Is this a newline character? 60056 254 13 - jr nz, p64_isPrintable ; If not, hop to testing to see if we can print this 60058 32 13 - ld de, (p64_coords) ; Get coords 60060 237 91 68 235 - call p64_nxtLine ; Go to next line. - - LOCAL p64_eaa3 - p64_eaa3: - ld (p64_coords), de - jr nextChar - - LOCAL p64_isPrintable - p64_isPrintable: - cp 31 ; Bigger than 31? 60073 254 31 - jr c, nextChar ; If not, get the next one. 60075 56 7 - push hl ; Save position 60077 229 - push bc ; Save Count 60078 197 - call p64_PrintChar ; Call Print SubRoutine 60079 205 189 234 - pop bc ; Recover length count 60082 193 - pop hl ; Recover Position 60083 225 - - LOCAL nextChar - nextChar: - inc hl ; Point to next character 60084 35 - dec bc ; Count off this character 60085 11 - ld a, b ; Did we run out? 60086 120 - or c - jr nz, examineChar ; If not, examine the next one 60088 32 193 - jp p64_END ; Otherwise hop to END. 60090 201 - - LOCAL p64_PrintChar - p64_PrintChar: - exx - push hl ; Save HL' 60094 229 - exx - sub 32 ; Take out 32 to convert ascii to position in charset 60096 214 32 - ld h, 0 - rra ; Divide by 2 60100 31 - ld l, a ; Put our halved value into HL 60101 111 - ld a, 240 ; Set our mask to LEFT side 60102 62 240 - jr nc, p64_eacc ; If we didn't have a carry (even #), hop forward. 60104 48 2 - ld a, 15 ; If we were ab idd #, set our mask to RIGHT side instead 60106 62 15 - - LOCAL p64_eacc - p64_eacc: - add hl, hl - add hl, hl - add hl, hl ; Multiply our char number by 8 60110 41 - ld de, p64_charset ; Get our Charset position 60111 17 70 235 - add hl, de ; And add our character count, so we're now pointed at the first - ; byte of the right character. 60114 25 - exx - ld de, (p64_coords) - ex af, af' - call p64_loadAndTest - ex af, af' - inc e - ld (p64_coords), de ; Put position+1 into coords 60126 237 83 68 235 - dec e - ld b, a - rr e ; Divide X position by 2 60132 203 27 - ld c, 0 - rl c ; Bring carry flag into C (result of odd/even position) 60136 203 17 - and 1 ; Mask out lowest bit in A 60138 230 1 - xor c ; XOR with C (Matches position RightLeft with Char RightLeft) 60140 169 - ld c, a - jr z, p64_eaf6 ; If they are both the same, skip rotation. 60142 40 6 - ld a, b - rrca - rrca - rrca - rrca - ld b, a - - LOCAL p64_eaf6 - p64_eaf6: - ld a, d ; Get Y coord 60150 122 - sra a - sra a - sra a ; Multiply by 8 60155 203 47 - add a, 88 - ld h, a ; Put high byte value for attribute into H. 60159 103 - ld a, d - and 7 - rrca - rrca - rrca - add a, e - ld l, a ; Put low byte for attribute into l 60167 111 - ld a, (23693) ; Get permanent Colours from System Variable 60168 58 141 92 - ld (hl), a ; Write new attribute 60171 119 - - ld a, d - and 248 - add a, 64 - ld h, a - ld a, b - cpl - ld e, a - exx - ld b, 8 - - LOCAL p64_eb18 - p64_eb18: - ld a, (hl) - exx - bit 0, c - jr z, p64_eb22 - rrca - rrca - rrca - rrca - - LOCAL p64_eb22 - p64_eb22: - and b - ld d, a - ld a, (hl) - and e - or d - ld (hl), a - inc h - exx - inc hl - djnz p64_eb18 - exx - pop hl - exx - ret - - LOCAL p64_loadAndTest - p64_loadAndTest: - ld de, (p64_coords) - - ; SubRoutine to go to legal character position. (60213) - LOCAL p64_test_X - p64_test_X: - ld a, e ; Get column from e - cp 64 ; more than 64 ? - jr c, p64_test_Y ; If not, then jump over nextline - - LOCAL p64_nxtLine - p64_nxtLine: - inc d ; Move down 1 - ld e, 0 ; reset x co-ord to zero - - LOCAL p64_test_Y - p64_test_Y: - ld a, d ; get Y co-ord - cp 24 ; Past 24? - ret c ; Return if not. - ld d, 0 ; Rest y co-ord to top of screen. - ret ; Return. - end asm - p64coords: - asm - LOCAL p64_coords; - p64_coords: - defb 64; X Coordinate store 60228 64 - defb 23; Y Coordinate Store 60229 23 - - LOCAL p64_charset - p64_charset: - DEFB 0,2,2,2,2,0,2,0 ; Space ! - DEFB 0,80,82,7,2,7,2,0 ; "" # - DEFB 0,37,113,66,114,20,117,32 ; $ % - DEFB 0,34,84,32,96,80,96,0 ; & ' - DEFB 0,36,66,66,66,66,36,0 ; ( ) - DEFB 0,0,82,34,119,34,82,0 ; * + - DEFB 0,0,0,0,7,32,32,64 ; , - - DEFB 0,1,1,2,2,100,100,0 ; . / - DEFB 0,34,86,82,82,82,39,0 ; 0 1 - DEFB 0,34,85,18,33,69,114,0 ; 2 3 - DEFB 0,87,84,118,17,21,18,0 ; 4 5 - DEFB 0,55,65,97,82,84,36,0 ; 6 7 - DEFB 0,34,85,37,83,85,34,0 ; 8 9 - DEFB 0,0,2,32,0,34,2,4 ; : ; - DEFB 0,0,16,39,64,39,16,0 ; < = - DEFB 0,2,69,33,18,32,66,0 ; > ? - DEFB 0,98,149,183,181,133,101,0 ; @ A Changed from ;0,2,37,87,117,85,53,0 - DEFB 0,98,85,100,84,85,98,0 ; B C - DEFB 0,103,84,86,84,84,103,0 ; D E - DEFB 0,114,69,116,71,69,66,0 ; F G - DEFB 0,87,82,114,82,82,87,0 ; H I - DEFB 0,53,21,22,21,85,37,0 ; J K - DEFB 0,69,71,71,69,69,117,0 ; L M - DEFB 0,82,85,117,117,85,82,0 ; N O - DEFB 0,98,85,85,103,71,67,0 ; P Q - DEFB 0,98,85,82,97,85,82,0 ; R S - DEFB 0,117,37,37,37,37,34,0 ; T U - DEFB 0,85,85,85,87,39,37,0 ; V W - DEFB 0,85,85,37,82,82,82,0 ; X Y - DEFB 0,119,20,36,36,68,119,0 ; Z [ - DEFB 0,71,65,33,33,17,23,0 ; \ ] - DEFB 0,32,112,32,32,32,47,0 ; ^ _ - DEFB 0,32,86,65,99,69,115,0 ; £ a - DEFB 0,64,66,101,84,85,98,0 ; b c - DEFB 0,16,18,53,86,84,35,0 ; d e - DEFB 0,32,82,69,101,67,69,2 ; f g - DEFB 0,66,64,102,82,82,87,0 ; h i - DEFB 0,20,4,53,22,21,85,32 ; j k - DEFB 0,64,69,71,71,85,37,0 ; l m - DEFB 0,0,98,85,85,85,82,0 ; n o - DEFB 0,0,99,85,85,99,65,65 ; p q - DEFB 0,0,99,84,66,65,70,0 ; r s - DEFB 0,64,117,69,69,85,34,0 ; t u - DEFB 0,0,85,85,87,39,37,0 ; v w - DEFB 0,0,85,85,35,81,85,2 ; x y - DEFB 0,0,113,18,38,66,113,0 ; z { - DEFB 0,32,36,34,35,34,36,0 ; | { - DEFB 0,6,169,86,12,6,9,6 ; ~ (c) - -LOCAL p64_END - p64_END: - ENDP - end asm - - end sub - -#pragma pop(case_insensitive) - -#endif +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/printfzx.bas b/src/lib/arch/zxnext/stdlib/printfzx.bas index faaeedcf7..be652679f 100644 --- a/src/lib/arch/zxnext/stdlib/printfzx.bas +++ b/src/lib/arch/zxnext/stdlib/printfzx.bas @@ -1,307 +1,8 @@ -'----------------------------------------------------------------------------- -' FZX driver - Copyright (c) 2013 Einar Saukas -' FZX format - Copyright (c) 2013 Andrew Owen -' Ported to ZX Basic by Paul Fisher (@Britlion) -' ----------------------------------------------------------------------------- - - -#ifndef __PRINTFZX__ -#define __PRINTFZX__ - -#pragma push(case_insensitive) -#pragma case_insensitive = TRUE - - -sub fastcall printFzxSetFontAddr(addr as Uinteger) - ASM - ld (__PRINTFZX_FONT_ADDR), hl - END ASM -end sub - - -sub fastcall printFzxSetMargin(margin as ubyte) - ASM - ld (__PRINTFZX_MARGIN), a - END ASM -end sub - - -sub fastcall printFzxAt(row as Ubyte, col as Ubyte) - ASM - push af - ld a, 22 - call _printFzxChar - pop af - call _printFzxChar - pop hl - ex (sp), hl - ld a, h - jp _printFzxChar - END ASM - DIM dummy as Uinteger = @printFzxChar -end sub - - -sub printFzxStr(s as String) - ASM - PROC - - LOCAL EXIT - LOCAL LOOP - - ld l, (ix + 4) - ld h, (ix + 5) - ld a, h - or l - jr z, EXIT - ld c, (hl) - inc hl - ld b, (hl) - -LOOP: - ld a, b - or c - jr z, EXIT - inc hl - dec bc - ld a, (hl) - push hl - push bc - call _printFzxChar - pop bc - pop hl - jr LOOP - -EXIT: - ENDP - END ASM -end sub - - -sub fastcall printFzxChar(char as ubyte) - ASM -; ----------------------------------------------------------------------------- -; FZX driver - Copyright (c) 2013 Einar Saukas -; FZX format - Copyright (c) 2013 Andrew Owen -; ----------------------------------------------------------------------------- - PROC - LOCAL GET_LIN - LOCAL GET_COL - LOCAL CHK_AT - LOCAL CHK_CR - LOCAL CHK_CHAR - LOCAL UNDEF_CHAR - LOCAL PRINT_CHAR - LOCAL NARROW_CHAR - LOCAL ON_SCREEN - LOCAL MAIN_LOOP - LOCAL SMC - LOCAL ROTATE_PIXELS - LOCAL NO_ROTATE - LOCAL CHK_LOOP - LOCAL WIDTH1 - LOCAL NEWLINE - LOCAL EXIT - - LOCAL P_FLAG - LOCAL P_COL - LOCAL P_LIN - - LOCAL MARGIN -MARGIN EQU 0 ; left margin (in pixels) - -__PRINTFZX_FONT_ADDR EQU CHK_CR + 5 -__PRINTFZX_MARGIN EQU NEWLINE + 1 - - -; ----------------------------------------------------------------------------- -; ENTRY POINT #2 - PROPORTIONAL PRINT ROUTINE -; ----------------------------------------------------------------------------- - ld hl, P_FLAG ; initial address of local variables - dec (hl) ; check P_FLAG value by decrementing it - jp m, CHK_AT ; expecting a regular character? - jr z, GET_COL ; expecting the AT column? -GET_LIN: - cpl - add a, 192 ; now A = 191 - char - inc hl -GET_COL: - inc hl - ld (hl), a - ret -CHK_AT: - cp 22 ; specified keyword 'AT'? - jr nz, CHK_CR - ld (hl), 2 ; change P_FLAG to expect line value next time - ret -CHK_CR: - push ix - inc (hl) ; increment P_FLAG to restore previous value - inc hl - ld bc, 0 ; Points to FONT ADDRESS - push bc - pop ix - cp 13 - jp z, NEWLINE -CHK_CHAR: - dec a ; now A = char - 1 - cp (ix+2) ; compare with lastchar - jr nc, UNDEF_CHAR - sub 31 ; now A = char - 32 - jr nc, PRINT_CHAR -UNDEF_CHAR: - ld a, '?'-32 ; print '?' instead of invalid character -PRINT_CHAR: - inc a ; now A = char - 31 - ld l, a - ld h, 0 - ld d, h - ld e, l - add hl, hl - add hl, de ; now HL = (char - 31) * 3 - add hl, bc ; now HL references offset/kern in char table - ld e, (hl) - inc hl - ld a, (hl) - and 63 - ld d, a ; now DE = offset - - xor (hl) - rlca - rlca - ld c, a ; now C = kern - - push hl - add hl, de - dec hl ; now HL = char definition address - ex (sp), hl ; now HL references offset/kern in char table - inc hl ; now HL references shift/width in char table - xor a - rld ; now A = char shift - push af - rld ; now A = (width - 1) - ld (WIDTH1+1), a - cp 8 ; check if char width is larger than 8 bits - rld ; restore char shift/width - - ld de, $000e ; same as "LD C,0" - jr c, NARROW_CHAR - ld de, $234e ; same as "LD C,(HL)" and "INC HL" -NARROW_CHAR: - ld (SMC), de ; self-modify code to handle narrow/large chars - - inc hl ; now HL references next char offset - ld a, (hl) ; now A = LSB of next char offset - add a, l - ld e, a ; now E = LSB of next char definition address - - ld hl, P_COL - ld a, (hl) - sub c ; move left number of pixels specified by kern - jr nc, ON_SCREEN ; stop moving if it would fall outside screen - xor a -ON_SCREEN: - ld (hl), a - - ld a, (WIDTH1+1) ; now A = (width - 1) - add a, (hl) ; now A = (width - 1) + column - call c, NEWLINE ; if char width won't fit then move to new line - - ld bc, (P_COL) - ld a, 1 - sub (ix+0) ; now A = 1 - height - add a, b ; now A = P_LIN - height + 1 - jp nc, $0c86 ; call routine REPORT-5 ("Out of screen") - - pop af ; now A = shift - - add a, 191 ; range 0-191 - call $22aa + 2 ; call PIXEL-ADD + 2 to calculate screen address - ex af, af' - ; now A' = (col % 8) - jr CHK_LOOP - -MAIN_LOOP: - ld d, (hl) ; now D = 1st byte from char definition grid - inc hl ; next character definition -SMC: - ld c, (hl) ; now C = 2nd byte from char definition or zero - inc hl ; (either "LD C,0" or "LD C,(HL)" + "INC HL") - xor a ; now A = zero (since there's no 3rd byte) - ex (sp), hl ; now HL = screen address - - ex af, af' - ; now A = (col % 8), A' = 0 - jr z, NO_ROTATE - ld b, a ; now B = (col % 8) - ex af, af' - ; now A = 0, A' = (col % 8) -ROTATE_PIXELS: - srl d ; rotate right char definition grid in D,C,A - rr c - rra - djnz ROTATE_PIXELS - -NO_ROTATE: - inc l - inc l - or (hl) - ld (hl), a ; put A on screen - dec l - ld a, c - or (hl) - ld (hl), a ; put C on screen - dec l - ld a, d - or (hl) - ld (hl), a ; put D on screen - - inc h ; move screen address by 1 pixel down - ld a, h - and 7 - jr nz, CHK_LOOP - ld a, l - sub -32 - ld l, a - sbc a, a - and -8 - add a, h - ld h, a -CHK_LOOP: - ex (sp), hl ; now HL = char definition address - ld a, l - cp e ; check if reached next char definition address - jr nz, MAIN_LOOP ; loop otherwise - - pop hl ; discard screen address from stack - ld hl, P_COL - ld a, (hl) ; now A = column -WIDTH1: - add a, 0 ; now A = column + (width - 1) - scf - adc a, (ix+1) ; now A = column + width + tracking - jr nc, EXIT ; if didn't fall outside the screen then exit -NEWLINE: - ld (hl), MARGIN ; move to initial column at left margin - inc hl - ld a, (hl) ; now A = line - sub (ix+0) ; now A = line - height -EXIT: - ld (hl), a ; move down a few pixels specified by height - pop ix - ret - -P_FLAG: - defb 0 -P_COL: - defb MARGIN -P_LIN: - defb 191 - ENDP -; ----------------------------------------------------------------------------- - END ASM -end sub - -#pragma pop(case_insensitive) - -#endif +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/putchars.bas b/src/lib/arch/zxnext/stdlib/putchars.bas index a89aca160..a8c8a8385 100644 --- a/src/lib/arch/zxnext/stdlib/putchars.bas +++ b/src/lib/arch/zxnext/stdlib/putchars.bas @@ -1,667 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License ' -' Copyleft (k) 2008-2023 -' by Paul Fisher (a.k.a. BritLion) +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#ifndef __LIBRARY_PUTCHARS__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_PUTCHARS__ - -#pragma push(case_insensitive) -#pragma case_insensitive = True - - -' ---------------------------------------------------------------- -' SUB putChars -' -' Fills a rectangle region of the screen width a char -' -' Parameters: -' x - x coordinate (cell column) -' y - y coordinate (cell row) -' width - width (number of columns) -' height - height (number of rows) -' dataAddress - Chars bytes address -' -' ---------------------------------------------------------------- -SUB putChars(x as uByte,y as uByte, width as uByte, height as uByte, dataAddress as uInteger) -' Copyleft Britlion. Feel free to use as you will. Please attribute me if you use this, however! - - Asm - PROC - LOCAL BLPutChar, BLPutCharColumnLoop, BLPutCharInColumnLoop, BLPutCharSameThird - LOCAL BLPutCharNextThird, BLPutCharNextColumn, BLPutCharsEnd - -BLPutChar: - ld a,(ix+5) - ld l,a - ld a,(ix+7) ; Y value - ld d,a - and 24 - ld h,a - ld a,d - and 7 - rrca - rrca - rrca - or l - ld l,a - - push hl ; save our address - ld e,(ix+12) ; data address - ld d,(ix+13) - ld b,(ix+9) ; width - push bc ; save our column count - -BLPutCharColumnLoop: - ld b, (ix+11) ; height - -BLPutCharInColumnLoop: - - push hl - push de - ld de, (.core.SCREEN_ADDR) - add hl, de ;Adds the offset to the screen att address - pop de - - ; gets screen address in HL, and bytes address in DE. Copies the 8 bytes to the screen - ld a,(de) ; First Row - ld (hl),a - - inc de - inc h - ld a,(de) - ld (hl),a ; second Row - - inc de - inc h - ld a,(de) - ld (hl),a ; Third Row - - inc de - inc h - ld a,(de) - ld (hl),a ; Fourth Row - - inc de - inc h - ld a,(de) - ld (hl),a ; Fifth Row - - inc de - inc h - ld a,(de) - ld (hl),a ; Sixth Row - - inc de - inc h - ld a,(de) - ld (hl),a ; Seventh Row - - inc de - inc h - ld a,(de) - ld (hl),a ; Eighth Row - - pop hl - inc de ; Move to next data item. - dec b - jr z, BLPutCharNextColumn - - ;The following code calculates the address of the next line down below current HL address. - push de ; save DE - ld a,l - and 224 - cp 224 - jr z,BLPutCharNextThird - -BLPutCharSameThird: - ld de,32 - add hl,de - pop de ; get our data point back. - jp BLPutCharInColumnLoop - -BLPutCharNextThird: - ld de,1824 - add hl,de - pop de ; get our data point back. - jp BLPutCharInColumnLoop - -BLPutCharNextColumn: - pop bc - pop hl - dec b - jr z, BLPutCharsEnd - - inc l ; Note this would normally be Increase HL - but block painting should never need to increase H, since that would wrap around. - push hl - push bc - jp BLPutCharColumnLoop - -BLPutCharsEnd: - ENDP - End Asm -END SUB - - -' ---------------------------------------------------------------- -' SUB getChars -' -' Gets a rectangle region of the screen into many chars (opposite of putChars) -' -' Parameters: -' x - x coordinate (cell column) -' y - y coordinate (cell row) -' width - width (number of columns) -' height - height (number of rows) -' dataAddress - Chars bytes address -' -' ---------------------------------------------------------------- -SUB getChars(x as uByte,y as uByte, width as uByte, height as uByte, dataAddress as uInteger) -' Copyleft Britlion Feel free to use as you will. Please attribute me if you use this, however! - - Asm - PROC - LOCAL BLGetChar, BLGetCharColumnLoop, BLGetCharInColumnLoop, BLGetCharSameThird - LOCAL BLGetCharNextThird, BLGetCharNextColumn, BLGetCharsEnd - -BLGetChar: - ld a,(ix+5) - ld l,a - ld a,(ix+7) ; Y value - ld d,a - and 24 - ld h,a - ld a,d - and 7 - rrca - rrca - rrca - or l - ld l,a - - push hl ; save our address - ld e,(ix+12) ; data address - ld d,(ix+13) - ld b,(ix+9) ; width - push bc ; save our column count - -BLGetCharColumnLoop: - ld b, (ix+11) ; height - -BLGetCharInColumnLoop: - - push hl - push de - ld de, (.core.SCREEN_ADDR) - add hl, de ;Adds the offset to the screen att address - pop de - - ; gets screen address in HL, and bytes address in DE. Copies the 8 bytes from the screen - ld a,(hl) ; First Row - ld (de),a - - inc de - inc h - ld a,(hl) - ld (de),a ; second Row - - inc de - inc h - ld a,(hl) - ld (de),a ; Third Row - - inc de - inc h - ld a,(hl) - ld (de),a ; Fourth Row - - inc de - inc h - ld a,(hl) - ld (de),a ; Fifth Row - - inc de - inc h - ld a,(hl) - ld (de),a ; Sixth Row - - inc de - inc h - ld a,(hl) - ld (de),a ; Seventh Row - - inc de - inc h - ld a,(hl) - ld (de),a ; Eighth Row - - pop hl - inc de ; Move to next data item. - dec b - jr z, BLGetCharNextColumn - - ;The following code calculates the address of the next line down below current HL address. - push de ; save DE - ld a,l - and 224 - cp 224 - jr z,BLGetCharNextThird - -BLGetCharSameThird: - ld de,32 - add hl,de - pop de ; get our data point back. - jp BLGetCharInColumnLoop - -BLGetCharNextThird: - ld de,1824 - add hl,de - pop de ; get our data point back. - jp BLGetCharInColumnLoop - -BLGetCharNextColumn: - pop bc - pop hl - dec b - jr z, BLGetCharsEnd - - inc l ; Note this would normally be Increase HL - but block painting should never need to increase H, since that would wrap around. - push hl - push bc - jp BLGetCharColumnLoop - -BLGetCharsEnd: - ENDP - End Asm -END SUB - - -' ---------------------------------------------------------------- -' SUB Paint -' -' Fills a rectangle region of the screen width a color -' -' Parameters: -' x - x coordinate (cell column) -' y - y coordinate (cell row) -' width - width (number of columns) -' height - height (number of rows) -' attribute - byte-encoded attr -' -' ---------------------------------------------------------------- -SUB paint (x as uByte,y as uByte, width as uByte, height as uByte, attribute as ubyte) -REM Copyleft Britlion. Feel free to use as you will. Please attribute me if you use this, however! - - Asm - PROC - LOCAL BLPaintHeightLoop, BLPaintWidthLoop, BLPaintWidthExitLoop, BLPaintHeightExitLoop - - ld a,(ix+7) ;ypos - rrca - rrca - rrca ; Multiply by 32 - ld l,a ; Pass to L - and 3 ; Mask with 00000011 - ld h,a ; Put it in the High Byte - ld a,l ; We get y value *32 - and 224 ; Mask with 11100000 - ld l,a ; Put it in L - ld a,(ix+5) ; xpos - add a,l ; Add it to the Low byte - ld l,a ; Put it back in L, and we're done. HL=Address. - ld de,(.core.SCREEN_ATTR_ADDR) - add hl, de ;Adds the offset to the screen att address - - push hl ; save address - ld a, (ix+13) ; attribute - ld de,32 - ld c,(ix+11) ; height - -BLPaintHeightLoop: - ld b,(ix+9) ; width - -BLPaintWidthLoop: - ld (hl),a ; paint a character - inc hl - djnz BLPaintWidthLoop - -BLPaintWidthExitLoop: - pop hl ; recover our left edge - dec c - jr z, BLPaintHeightExitLoop - - add hl,de ; move 32 down - push hl ; save it again - jp BLPaintHeightLoop - -BLPaintHeightExitLoop: - ENDP - End Asm -END SUB - - -' ---------------------------------------------------------------- -' SUB PaintData -' -' Fills a rectangle region of the screen width a colors from a -' stored at a memory address. -' -' Parameters: -' x - x coordinate (cell column) -' y - y coordinate (cell row) -' width - width (number of columns) -' height - height (number of rows) -' address - address of the byte-encoded attr sequence -' -' ---------------------------------------------------------------- -SUB paintData(x as uByte,y as uByte, width as uByte, height as uByte, address as uInteger) -REM Copyleft Britlion. Feel free to use as you will. Please attribute me if you use this, however! - - Asm - PROC - LOCAL BLPaintDataHeightLoop, BLPaintDataWidthLoop, BLPaintDataWidthExitLoop, BLPaintDataHeightExitLoop - - ld a,(ix+7) ;ypos - rrca - rrca - rrca ; Multiply by 32 - ld l,a ; Pass to L - and 3 ; Mask with 00000011 - ld h,a ; Put it in the High Byte - ld a,l ; We get y value *32 - and 224 ; Mask with 11100000 - ld l,a ; Put it in L - ld a,(ix+5) ; xpos - add a,l ; Add it to the Low byte - ld l,a ; Put it back in L, and we're done. HL=Address. - ld de,(.core.SCREEN_ATTR_ADDR) - add hl, de ;Adds the offset to the screen att address - - push hl ; save address - ld d,(ix+13) - ld e,(ix+12) - ld c,(ix+11) ; height - -BLPaintDataHeightLoop: - ld b,(ix+9) ; width - -BLPaintDataWidthLoop: - ld a,(de) - ld (hl),a ; paint a character - inc hl - inc de - djnz BLPaintDataWidthLoop - -BLPaintDataWidthExitLoop: - pop hl ; recover our left edge - dec c - jr z, BLPaintDataHeightExitLoop - push de - ld de,32 - add hl,de ; move 32 down - pop de - push hl ; save it again - jp BLPaintDataHeightLoop - -BLPaintDataHeightExitLoop: - ENDP - End Asm -END SUB - - -' ---------------------------------------------------------------- -' SUB getPaintData -' -' Gets the colors of a rectangle region of the screen into memory (opposite of paintData) -' -' Parameters: -' x - x coordinate (cell column) -' y - y coordinate (cell row) -' width - width (number of columns) -' height - height (number of rows) -' address - address of the byte-encoded attr sequence -' -' ---------------------------------------------------------------- -SUB getPaintData(x as uByte,y as uByte, width as uByte, height as uByte, address as uInteger) -REM Copyleft Britlion. Feel free to use as you will. Please attribute me if you use this, however! - - Asm - PROC - LOCAL BLGetPaintDataHeightLoop, BLGetPaintDataWidthLoop, BLGetPaintDataWidthExitLoop, BLGetPaintDataHeightExitLoop - - ld a,(ix+7) ;ypos - rrca - rrca - rrca ; Multiply by 32 - ld l,a ; Pass to L - and 3 ; Mask with 00000011 - ld h,a ; Put it in the High Byte - ld a,l ; We get y value *32 - and 224 ; Mask with 11100000 - ld l,a ; Put it in L - ld a,(ix+5) ; xpos - add a,l ; Add it to the Low byte - ld l,a ; Put it back in L, and we're done. HL=Address. - ld de,(.core.SCREEN_ATTR_ADDR) - add hl, de ;Adds the offset to the screen att address - - push hl ; save address - ld d,(ix+13) - ld e,(ix+12) - ld c,(ix+11) ; height - -BLGetPaintDataHeightLoop: - ld b,(ix+9) ; width - -BLGetPaintDataWidthLoop: - ld a,(hl) - ld (de),a ; paint a character - inc hl - inc de - djnz BLGetPaintDataWidthLoop - -BLGetPaintDataWidthExitLoop: - pop hl ; recover our left edge - dec c - jr z, BLGetPaintDataHeightExitLoop - push de - ld de,32 - add hl,de ; move 32 down - pop de - push hl ; save it again - jp BLGetPaintDataHeightLoop - -BLGetPaintDataHeightExitLoop: - ENDP - End Asm -END SUB - - -' ---------------------------------------------------------------- -' SUB putCharsOverMode -' -' Fills a rectangle region of the screen width a char -' -' Parameters: -' x - x coordinate (cell column) -' y - y coordinate (cell row) -' width - width (number of columns) -' height - height (number of rows) -' overMode- the way the characters are combined with the background. -' matches the values of the OVER command: -' 0 - the characters are simply replaced. -' 1 - the characters are combined with an Exclusive OR (XOR). -' 2 - the characters are combined using an AND function. -' 3 - the characters are combined using an OR function. -' dataAddress - Chars bytes address -' -' ---------------------------------------------------------------- -SUB putCharsOverMode(x as uByte,y as uByte, width as uByte, height as uByte, _ - overMode as uByte, dataAddress as uInteger) - - Asm - PROC - LOCAL BLPutChar, BLPutCharColumnLoop, BLPutCharInColumnLoop, BLPutCharSameThird - LOCAL BLPutCharNextThird, BLPutCharNextColumn, BLPutCharsEnd - LOCAL op1, op2, op3, op4, op5, op6, op7, op8, opTable, noCarry - - ld a,(ix+13) - and 3 - ld hl, opTable - add a, l - jp nc, noCarry - inc h -noCarry: - ld l, a - ld a, (hl) - ld (op1), a - ld (op2), a - ld (op3), a - ld (op4), a - ld (op5), a - ld (op6), a - ld (op7), a - ld (op8), a - jp BLPutChar - -opTable: - DEFB $00 ; 00 - NOP - $00 - DEFB $AE ; 01 - XOR (HL) - $AE - DEFB $A6 ; 02 - AND (HL) - $A6 - DEFB $B6 ; 03 - OR (HL) - $B6 - -BLPutChar: - ld a,(ix+5) - ld l,a - ld a,(ix+7) ; Y value - ld d,a - and 24 - ld h,a - ld a,d - and 7 - rrca - rrca - rrca - or l - ld l,a - - push hl ; save our address - ld e,(ix+14) ; data address - ld d,(ix+15) - ld b,(ix+9) ; width - push bc ; save our column count - -BLPutCharColumnLoop: - ld b, (ix+11) ; height - -BLPutCharInColumnLoop: - push hl - push de - ld de, (.core.SCREEN_ADDR) - add hl, de ;Adds the offset to the screen att address - pop de - ; gets screen address in HL, and bytes address in DE. Copies the 8 bytes to the screen - ld a,(de) ; First Row -op1: - nop - ld (hl),a - - inc de - inc h - ld a,(de) -op2: - nop - ld (hl),a ; second Row - - inc de - inc h - ld a,(de) -op3: - nop - ld (hl),a ; Third Row - - inc de - inc h - ld a,(de) -op4: - nop - ld (hl),a ; Fourth Row - - inc de - inc h - ld a,(de) -op5: - nop - ld (hl),a ; Fifth Row - - inc de - inc h - ld a,(de) -op6: - nop - ld (hl),a ; Sixth Row - - inc de - inc h - ld a,(de) -op7: - nop - ld (hl),a ; Seventh Row - - inc de - inc h - ld a,(de) -op8: - nop - ld (hl),a ; Eighth Row - - pop hl - inc de ; Move to next data item. - dec b - jr z, BLPutCharNextColumn - - ;The following code calculates the address of the next line down below current HL address. - push de ; save DE - ld a,l - and 224 - cp 224 - jr z,BLPutCharNextThird - -BLPutCharSameThird: - ld de,32 - add hl,de - pop de ; get our data point back. - jp BLPutCharInColumnLoop - -BLPutCharNextThird: - ld de,1824 - add hl,de - pop de ; get our data point back. - jp BLPutCharInColumnLoop - -BLPutCharNextColumn: - pop bc - pop hl - dec b - jr z, BLPutCharsEnd - - inc l ; Note this would normally be Increase HL - but block painting should never need to increase H, since that would wrap around. - push hl - push bc - jp BLPutCharColumnLoop - -BLPutCharsEnd: - ENDP - End Asm -END SUB - -#require "sysvars.asm" - -#pragma pop(case_insensitive) - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/puttile.bas b/src/lib/arch/zxnext/stdlib/puttile.bas index 166332aef..94a7af573 100644 --- a/src/lib/arch/zxnext/stdlib/puttile.bas +++ b/src/lib/arch/zxnext/stdlib/puttile.bas @@ -1,298 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License ' -' Copyleft (k) 2008-2023 -' by Paul Fisher (a.k.a. BritLion) +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#ifndef __LIBRARY_PUTTILE__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_PUTTILE__ - -#pragma push(case_insensitive) -#pragma case_insensitive = True - - -' ---------------------------------------------------------------- -' SUB putTile -' -' Routine to place a 16 pixel by 16 pixel "Tile" onto the screen at character position x,y from adddress given. -' Data must be in the format of 16 bit rows, followed by attribute data. -' (c) 2010 Britlion, donated to the ZX BASIC project. -' Thanks to Boriel, LCD and Na_than for inspiration behind this. - -' This routine could be used as the basis for a fast sprite system, provided all sprites can be in 4 character blocks. -' It can also be used to clean up dirty background (erase sprites), or put backgrounds from tiled blocks onto a screen. -' -' Note the comments about Self Modifying code should be ignored. This has been updated with IX+n methods, which overall -' are faster than accessing and changing the code. -' (They would have to be accessed to change the memory anyway - may as well just access them directly.) - -' Parameters: -' x - x coordinate (cell column) -' y - y coordinate (cell row) -' graphicsAddr - Memory address of the graphicc -' -' ---------------------------------------------------------------- - -SUB putTile(x as uByte, y as uByte, graphicsAddr as uInteger) - ASM - PROC - LOCAL ptstackSave, pt_start, ptNextThird3, ptSameThird3 - LOCAL ptAddrDone3, ptNextSprite2, pt_nointerrupts - - jp pt_start - -ptstackSave: - defb 0,0 - -pt_start: - ld a,i - push af ; Save interrupt status. - - ; Routine to save the background to the buffer - di ; we REALLY can NOT be having interrupts while the stack and IX and IY are pointed elsewhere. - - push ix - push iy - ld d,(ix+9) - ld e,(ix+8) - ex de,hl - - ;; Print sprites routine - ld (ptstackSave), sp ; Save Stack Pointer - ld sp,hl ; now SP points at the start of the graphics. - - ; This function returns the address into HL of the screen address - ld a,(IX+5) ; Load in x - note the Self Modifying value - ld IYH, a ; save it - ld l,a - ld a,(IX+7) ; Load in y - note the Self Modifying value - ld IYL, a ; save it - ld d,a - and 24 - add a,64 - ld h,a - ld a,d - and 7 - rrca - rrca - rrca - or l - add a,2 ; Need to be to the right so backwards writing pushes land properly. - ld l,a - - ; SO now, HL -> Screen address, and SP -> Graphics. Time to start loading. - - pop bc ; row 0 - pop de ; row 1 - ex af,af' - pop af ; row 2 - ex af,af' - exx - pop bc ; row 3 - pop de ; row 4 - pop hl ; row 5 - exx - - ; All right. We're loaded. Time to dump! - - ld ix,0 - add ix,sp ; Save our stack pointer into ix - - ld sp,hl ; point at the screen. - push bc ; row 0 - - inc h - ld sp,hl - push de ; row 1 - - inc h - ld sp,hl - ex af,af' - push af ; row 2 - - inc h - ld sp,hl - exx - push bc ; row 3 - exx - - inc h - ld sp,hl - exx - push de ; row 4 - exx - - inc h - ld sp,hl - exx - push hl ; row 5 - exx - - ; We're empty. Time to load up again. - - ld sp,ix - pop bc ; row 6 - pop de ; row 7 - ex af,af' - pop af ; row 8 - ex af,af' - exx - pop bc ; row 9 - pop de ; row 10 - pop hl ; row 11 - exx - - ; and we're loaded up again! Time to dump this graphic on the screen. - - ld ix,0 - add ix,sp ; save SP in IX - - inc h - ld sp,hl - push bc ; row 6 - - inc h - ld sp,hl - push de ; row 7 - - dec hl - dec hl - - ; Aha. Snag. We're at the bottom of a character. What's the next address down? - ld a,l - and 224 - cp 224 - jr nz, ptNextThird3 - -ptSameThird3: - ld de,32 - add hl,de - jp ptAddrDone3 - -ptNextThird3: - ld de,-1760 - add hl,de - -ptAddrDone3: - inc hl - inc hl - - ld sp,hl - ex af,af' - push af ; row 8 - - inc h - ld sp,hl - exx - push bc ; row 9 - exx - - inc h - ld sp,hl - exx - push de ; row 10 - exx - - inc h - ld sp,hl - exx - push hl ; row 11 - exx - - ; Okay. Registers empty. Reload time! - ld sp,ix - pop bc ; row 12 - pop de ; row 13 - - exx - pop bc ; row 14 - pop de ; row 15 - pop hl ; top attrs - exx - - ex af,af' - pop af ; bottom attrs - ex af,af' - - ; and the last dump to screen - - inc h - ld sp,hl - push bc - - inc h - ld sp,hl - push de - - inc h - ld sp,hl - exx - push bc - exx - - inc h - ld sp,hl - exx - push de - exx - - ; Pixels done. Just need to do the attributes. - ; So set HL to the attr address: - - ld a,iyl ;ypos - - rrca - rrca - rrca ; Multiply by 32 - ld l,a ; Pass to L - and 3 ; Mask with 00000011 - add a,88 ; 88 * 256 = 22528 - start of attributes. - ld h,a ; Put it in the High Byte - ld a,l ; We get y value *32 - and 224 ; Mask with 11100000 - ld l,a ; Put it in L - ld a,iyh ; xpos - adc a,l ; Add it to the Low byte - ld l,a ; Put it back in L, and we're done. HL=Address. - inc hl ; we need to be to the right of the attr point as pushes write backwards. - inc hl - - ; attr - ld sp,hl - exx - push hl ; top row - exx - - ld hl,34 ; we need to move down to the next row. We already backed up 2, so we add 34. - add hl,sp - ld sp,hl - ex af,af' ; bottom row - push af - -ptNextSprite2: - ; done. Cleanup. - ld sp,(ptstackSave) ; put our stack back together. - - ; done all 4 final clean up - - pop iy - pop ix - - pop af ; recover interrupt status - jp po, pt_nointerrupts - ei ; Okay. We put everything back. If you need interrupts, you can go with em. - -pt_nointerrupts: - ENDP - END ASM -END SUB - - -#pragma pop(case_insensitive) - -#endif - +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/random.bas b/src/lib/arch/zxnext/stdlib/random.bas index 38232361e..ac922bb84 100644 --- a/src/lib/arch/zxnext/stdlib/random.bas +++ b/src/lib/arch/zxnext/stdlib/random.bas @@ -1,55 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License ' -' Copyleft (k) 2025 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#pragma once -#pragma push(case_insensitive) -#pragma case_insensitive = TRUE - -function fastcall randInt() as ULong ' Returns a random ULong - asm - push namespace core - call RAND - pop namespace - end asm -end function - - -Function fastcall randFixed() as Fixed ' Returns a random Fixed - Asm - push namespace core - call RAND - pop namespace - End Asm -End Function - - -Function fastcall randomLimit(limit as uByte) as uByte - Asm - push namespace core - PROC - and a - ret z ; Input zero, output zero. - ld b, a ; Save A - ld c,255 - 1: - rla - jr c, 2f - rr c - jp 1b ; loop back until we find a bit. - 2: - call RAND - and c - cp b - ret z - jr nc, 2b - ENDP - pop namespace - End Asm -End Function - -#pragma pop(case_insensitive) -#require "random.asm" +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/retrace.bas b/src/lib/arch/zxnext/stdlib/retrace.bas index 5b9f20c64..16cd76934 100644 --- a/src/lib/arch/zxnext/stdlib/retrace.bas +++ b/src/lib/arch/zxnext/stdlib/retrace.bas @@ -1,10 +1,8 @@ -REM simple WaitRetrace macro - -#ifndef waitretrace -#define waitretrace 'REM Retrace \ - asm \ - halt \ - end asm - -#endif +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' ---------------------------------------------------------------- +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/round.bas b/src/lib/arch/zxnext/stdlib/round.bas index 05a0284e6..045897f14 100644 --- a/src/lib/arch/zxnext/stdlib/round.bas +++ b/src/lib/arch/zxnext/stdlib/round.bas @@ -1,35 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License -' -' Copyleft (k) 2024 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- - -#pragma once -#pragma push(case_insensitive) -#pragma case_insensitive = True - -' ---------------------------------------------------------------- -' function ROUND -' -' Parameters: -' n as Float: number to be rounded -' y as Ubyte: number of decimals ' -' Returns: -' A float rounded towards +/- infinity with the numbers of decimals -' requested. +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -FUNCTION Round(n as Float, decimals as UByte = 0) AS Float - DIM tmp as Float - DIM d10 as Float = 10^decimals - IF n >= 0 THEN - LET tmp = INT(n * d10 + 0.5) - ELSE - LET tmp = INT(n * d10 - 0.5) - END IF - RETURN tmp / d10 -END FUNCTION - -#pragma pop(case_insensitive) +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/scrbuffer.bas b/src/lib/arch/zxnext/stdlib/scrbuffer.bas index c92b2d3c2..ec8d0da81 100644 --- a/src/lib/arch/zxnext/stdlib/scrbuffer.bas +++ b/src/lib/arch/zxnext/stdlib/scrbuffer.bas @@ -1,85 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- - -#ifndef __LIBRARY_SCRBUFFER__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_SCRBUFFER__ - -#pragma push(case_insensitive) -#pragma case_insensitive = True - -REM Put your code here. - -' ---------------------------------------------------------------- -' function GetScreenBufferAddr -' -' Returns: -' The address of the Screen Buffer -' ---------------------------------------------------------------- -Function Fastcall GetScreenBufferAddr As UInteger -Asm - ld hl, (.core.SCREEN_ADDR) -End Asm -End Function - - -' ---------------------------------------------------------------- -' function SetScreenBufferAddr -' -' Sets the address of the screen buffer and returns it -' -' Parameters: -' - Address: Uinteger value with the new address ' -' Returns: -' The address of the screen buffer -' ---------------------------------------------------------------- -Function Fastcall SetScreenBufferAddr(addr As UInteger) As UInteger -Asm - ld (.core.SCREEN_ADDR), hl -End Asm -End Function - - -' ---------------------------------------------------------------- -' function GetAttrBufferAddr -' -' Returns: -' The address of the attribute buffer -' ---------------------------------------------------------------- -Function Fastcall GetAttrBufferAddr As UInteger -Asm - ld hl, (.core.SCREEN_ATTR_ADDR) -End Asm -End Function - - -' ---------------------------------------------------------------- -' function SetScreenBufferAddr -' -' Sets the address of the attribute buffer and returns it -' -' Parameters: -' - Address: UInteger value with the new address -' -' Returns: -' The address of the attribute buffer +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -Function Fastcall SetAttrBufferAddr(addr As UInteger) As UInteger -Asm - ld (.core.SCREEN_ATTR_ADDR), hl -End Asm -End Function - - -#require "sysvars.asm" -#pragma pop(case_insensitive) - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/screen.bas b/src/lib/arch/zxnext/stdlib/screen.bas index 686671c83..23c03c82a 100644 --- a/src/lib/arch/zxnext/stdlib/screen.bas +++ b/src/lib/arch/zxnext/stdlib/screen.bas @@ -2,104 +2,7 @@ ' This file is released under the MIT License ' ' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' -' ZX SCREEN$ function contributed by mcleod_idafix +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#ifndef __LIBRARY_SCREEN__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_SCREEN__ - -#pragma push(case_insensitive) -#pragma case_insensitive = TRUE - -' ---------------------------------------------------------------- -' function fastcall SCREEN -' -' Parameters: -' row: screen row -' col: screen column -' -' Returns: -' a string containing the screen char value -' ---------------------------------------------------------------- -function screen(byval row as ubyte, byval col as ubyte) as string - - ' fastcall functions always receive the 1st parameter - ' in accumulator (if byte) - Dim result as String - - asm - push namespace core - - PROC - LOCAL __SCREEN_END - - LOCAL __S_SCRNS_BC - LOCAL STK_END - LOCAL RECLAIM2 - -__S_SCRNS_BC EQU 2538h -STK_END EQU 5C65h -RECLAIM2 EQU 19E8h - - ld bc, 4 - call __MEM_ALLOC - push hl ; Saves memory pointer - - ld a, h - or l - jr z, __SCREEN_END ; Return NULL if no memory - - ld hl, (STK_END) - push hl - - ld b, (ix+7) ; row - ld c, (ix+5) ; column - - call __S_SCRNS_BC - call __FPSTACK_POP - - pop hl - ld (STK_END), hl - - pop hl - push hl - - ld (hl), c - inc hl - ld (hl), b - inc hl - ld a, (de) - ld (hl), a - - ex de, hl - call RECLAIM2 - -__SCREEN_END: - pop hl - ld (ix-2), l - ld (ix-1), h - - ENDP - - pop namespace - end asm - - return result - -end function - -#pragma pop(case_insensitive) - - -' The following is required to allocate dynamic memory for strings -#require "mem/alloc.asm" - -' The following is required to manipulate the FP-CALC stack -#require "stackf.asm" - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/scroll.bas b/src/lib/arch/zxnext/stdlib/scroll.bas index 33cfa9919..3c9b6803a 100644 --- a/src/lib/arch/zxnext/stdlib/scroll.bas +++ b/src/lib/arch/zxnext/stdlib/scroll.bas @@ -2,332 +2,7 @@ ' This file is released under the MIT License ' ' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' -' Use this file as a template to develop your own library file -' ---------------------------------------------------------------- - -#ifndef __LIBRARY_SCROLL__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_SCROLL__ - -#pragma push(case_insensitive) -#pragma case_insensitive = True - -' ---------------------------------------------------------------- -' sub ScrollRight -' pixel by pixel right scroll -' scrolls 1 pixel right the window defined by (x1, y1, x2, y2) -' ---------------------------------------------------------------- -sub fastcall ScrollRight(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) - asm - push namespace core - - PROC - LOCAL LOOP1 - LOCAL LOOP2 - - ; a = x1 - pop hl ; RET address - pop bc ; b = y1 - pop de ; d = x2 - ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now - - ld c, a ; BC = y1x1 - ld a, d - sub c - ret c ; x1 > x2 - - srl a - srl a - srl a - inc a - ld e, a ; e = (x2 - x1) / 8 + 1 - - ld a, h - sub b - ret c ; y1 > y2 - - inc a - ld d, a ; d = y2 - y1 + 1 - - ld b, h ; BC = y2x1 - ld a, 191 - LOCAL __PIXEL_ADDR -__PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR - res 6, h ; Starts from 0 - ld bc, (SCREEN_ADDR) - add hl, bc ; Now current offset - -LOOP1: - push hl - ld b, e ; C cols - or a ; clear carry flag -LOOP2: - rr (hl) - inc hl - djnz LOOP2 - pop hl - - dec d - ret z - call SP.PixelDown - jp LOOP1 - ENDP - - pop namespace - end asm -end sub - - -' ---------------------------------------------------------------- -' sub ScrolLeft -' pixel by pixel left scroll -' scrolls 1 pixel left the window defined by (x1, y1, x2, y2) -' ---------------------------------------------------------------- -sub fastcall ScrollLeft(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) - asm - push namespace core - - PROC - LOCAL LOOP1 - LOCAL LOOP2 - - ; a = x1 - pop hl ; RET address - pop bc ; b = y1 - pop de ; d = x2 - ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now - - ld c, a ; BC = y1x1 - ld a, d - sub c - ret c ; x1 > x2 - - srl a - srl a - srl a - inc a - ld e, a ; e = (x2 - x1) / 8 + 1 - - ld a, h - sub b - ret c ; y1 > y2 - - ld c, d - inc a - ld d, a ; d = y2 - y1 + 1 - - ld b, h ; BC = y2x1 - ld a, 191 - LOCAL __PIXEL_ADDR -__PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR - res 6, h ; Starts from 0 - ld bc, (SCREEN_ADDR) - add hl, bc ; Now current offset - -LOOP1: - push hl - ld b, e ; C cols - or a ; clear carry flag -LOOP2: - rl (hl) - dec hl - djnz LOOP2 - pop hl - - dec d - ret z - call SP.PixelDown - jp LOOP1 - ENDP - - pop namespace - end asm -end sub - - -' ---------------------------------------------------------------- -' sub ScrolUp -' pixel by pixel up scroll -' scrolls 1 pixel up the window defined by (x1, y1, x2, y2) -' ---------------------------------------------------------------- -sub fastcall ScrollUp(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) - asm - push namespace core - - PROC - LOCAL LOOP1 - - ; a = x1 - pop hl ; RET address - pop bc ; b = y1 - pop de ; d = x2 - ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now - - ld c, a ; BC = y1x1 - ld a, d - sub c - ret c ; x1 > x2 - - srl a - srl a - srl a - inc a - ld e, a ; e = (x2 - x1) / 8 + 1 - ex af, af' ; save it for later - - ld a, h - sub b - ret c ; y1 > y2 - - inc a - ld d, a ; d = y2 - y1 + 1 - - ld b, h ; BC = y2x1 - ld a, 191 - LOCAL __PIXEL_ADDR -__PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR - res 6, h ; Starts from 0 - ld bc, (SCREEN_ADDR) - add hl, bc ; Now current offset - - ld a, d ; Num. of scan lines - ld b, 0 - exx - ld b, a ; Scan lines counter - ex af, af' ; Recovers cols - ld c, a - jp LOOP_START - -LOOP1: - exx - ld d, h - ld e, l - ld c, a ; C cols - call SP.PixelDown - push hl - ldir - pop hl - exx - ld a, c ; Recovers C Cols - LOCAL LOOP_START -LOOP_START: - djnz LOOP1 - - ; Clears bottom line - exx - ld (hl), 0 - ld d, h - ld e, l - inc de - ld c, a - dec c - ret z - ldir - ENDP - - pop namespace - end asm -end sub - - +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -' sub ScrolDown -' pixel by pixel down scroll -' scrolls 1 pixel down the window defined by (x1, y1, x2, y2) -' ---------------------------------------------------------------- -sub fastcall ScrollDown(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) - asm - push namespace core - - PROC - LOCAL LOOP1 - - ; a = x1 - pop hl ; RET address - pop bc ; b = y1 - pop de ; d = x2 - ex (sp), hl ; h = y2, (sp) = RET address. Stack ok now - - ld c, a ; BC = y1x1 - ld a, d - sub c - ret c ; x1 > x2 - - srl a - srl a - srl a - inc a - ld e, a ; e = (x2 - x1) / 8 + 1 - ex af, af' ; save it for later - - ld a, h - sub b - ret c ; y1 > y2 - - inc a - ld d, a ; d = y2 - y1 + 1 - - ld a, 191 - LOCAL __PIXEL_ADDR -__PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR - res 6, h ; Starts from 0 - ld bc, (SCREEN_ADDR) - add hl, bc ; Now current offset - - ld a, d ; Num. of scan lines - ld b, 0 - exx - ld b, a ; Scan lines counter - ex af, af' ; Recovers cols - ld c, a - jp LOOP_START - -LOOP1: - exx - ld d, h - ld e, l - ld c, a ; C cols - call SP.PixelUp - push hl - ldir - pop hl - exx - ld a, c ; Recovers C Cols - LOCAL LOOP_START -LOOP_START: - djnz LOOP1 - - ; Clears top line - exx - ld (hl), 0 - ld d, h - ld e, l - inc de - ld c, a - dec c - ret z - ldir - - ENDP - - pop namespace - end asm -end sub - -#pragma pop(case_insensitive) - -REM the following is required, because it defines screen start addr -#require "cls.asm" -#require "SP/PixelDown.asm" -#require "SP/PixelUp.asm" - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/sinclair.bas b/src/lib/arch/zxnext/stdlib/sinclair.bas index 4b1e76f83..755cdbab4 100644 --- a/src/lib/arch/zxnext/stdlib/sinclair.bas +++ b/src/lib/arch/zxnext/stdlib/sinclair.bas @@ -1,39 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License ' -' This library is to be included automatically if the user -' specifies -Z or --sinclair parameter. -' ' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#ifndef __LIBRARY_SINCLAIR__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_SINCLAIR__ - -REM This library includes other classic Sinclair ZX Spectrum Routines - -REM Shift the screen 16 pixels UP to match that of the original BASIC -REM ... unless the user has specified otherwise - -#ifndef SCREEN_Y_OFFSET -# define SCREEN_Y_OFFSET 16 -#endif - -#include once -#include once -#include once - -REM This is not the original Sinclair INPUT, but better than nothing -#include once - -REM This needed to initialize USR "a" to a memory heap space -#include once - -REM Now updates UDG system var to new UDG memory zone -POKE Uinteger 23675, allocate(21 * 8) : REM 8 bytes per UDG (21 total) - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/spectranet.bas b/src/lib/arch/zxnext/stdlib/spectranet.bas index f8a67dab7..b25e2f485 100644 --- a/src/lib/arch/zxnext/stdlib/spectranet.bas +++ b/src/lib/arch/zxnext/stdlib/spectranet.bas @@ -2,432 +2,7 @@ ' This file is released under the MIT License ' ' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' Contributed by @Winston, @Guesser, @Ardentcrest -' -' Thanks a lot for your help! :)) +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#ifndef __LIBRARY_SPECTRANET__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_SPECTRANET__ -#pragma push(case_insensitive) -#pragma case_insensitive = TRUE - -#include - - -' POSIX file flags -#define O_RDONLY $0001 'Open read only -#define O_WRONLY $0002 'Open write only -#define O_RDWR $0003 'Open read/write -#define O_APPEND $0008 'Append to the file, if it exists (write only) -#define O_CREAT $0100 'Create the file if it doesn't exist (write only) -#define O_TRUNC $0200 'Truncate the file on open for writing -#define O_EXCL $0400 'With O_CREAT, returns an error if the file exists - - -' CHMOD POSIX file Mode -#define SISUID 04000o 'set user ID on execution -#define SISGID 02000o 'set group ID on execution -#define SISVTX 01000o 'sticky bit -#define SIRUSR 00400o 'read by owner -#define SIWUSR 00200o 'write by owner -#define SIXUSR 00100o 'execute/search by owner -#define SIRGRP 00040o 'read by group -#define SIWGRP 00020o 'write by group -#define SIXGRP 00010o 'execute/search by group -#define SIROTH 00004o 'read by others -#define SIWOTH 00002o 'write by others -#define SIXOTH 00001o 'execute/search by others - - -#define SEEK_SET 0x00 -#define SEEK_CUR 0x01 -#define SEEK_END 0x02 - - -' Macro to convert a string to ASCIIZ -#define ASCIIZ(x) (x + CHR$(0)) -' Where to store FILE op errors -#define ERR_NR 23610 - - -Function FASTCALL SNETsocket(stype As ubyte) As byte - Asm - ld c, a ; c = type - ld hl, Spectranet.SOCKET - call Spectranet.HLCALL - End Asm -End Function - - -Function FASTCALL SNETbind(socket as ubyte, port As uinteger) As byte - Asm - pop hl - ex (sp), hl - ex de, hl ; HL = port - ld hl, Spectranet.BIND - call Spectranet.HLCALL - End Asm -End Function - - -Function FASTCALL SNETlisten(socket As ubyte) As byte - Asm - ld hl, Spectranet.LISTEN - call Spectranet.HLCALL - End Asm -End Function - - -Function FASTCALL SNETaccept(socket As ubyte) As byte - Asm - ld hl, Spectranet.ACCEPT - call Spectranet.HLCALL - End Asm -End Function - - -Function FASTCALL SNETconnect(socket As ubyte, ip$, port As uinteger) As byte - Asm - pop hl ; ret address - pop de ; string containing the IP in CODE format. e.g. 10.0.0.1 = chr$(10, 0, 0, 1) - pop bc ; port - push hl ; Ret address restored; hl = port - ld hl, Spectranet.CONNECT - push de ; Used later to free the string - inc de - inc de - call Spectranet.HLCALL - pop hl - ex af, af' - call __MEM_FREE - ex af, af' - End Asm -End Function - - -Function FASTCALL SNETclose(socket As ubyte) As byte - Asm - ld hl, Spectranet.CLOSE - call Spectranet.HLCALL - End Asm -End Function - - -Function FASTCALL SNETrecv(socket As ubyte, addr As uinteger, length as uinteger) as byte - Asm - pop hl ; Ret address - pop de ; address - pop bc ; Length - push hl ; Restore ret address - ld hl, Spectranet.RECV - call Spectranet.HLCALL - End Asm -End Function - - -Function FASTCALL SNETsend(socket As ubyte, addr As uinteger, length as uinteger) as byte - Asm - pop hl ; Ret address - pop de ; address - pop bc ; Length - push hl ; Restore ret address - ld hl, Spectranet.SEND - call Spectranet.HLCALL - End Asm -End Function - - -Function FASTCALL SNETpollfd(socket As ubyte) as ubyte - Asm - ld hl, Spectranet.POLLFD - call Spectranet.HLCALL - ld a, 0 - ret z - ld c, a - End Asm -End Function - - -' ----------------------------------------------------------- -' Reads a memory address (16 bit) from paged memory -' ----------------------------------------------------------- -Function FASTCALL SNETpeekUinteger(addr as Uinteger) As UInteger - Asm - ex de, hl - ld hl, Spectranet.PAGEIN - call Spectranet.HLCALL - ex de, hl - ld e, (hl) - inc hl - ld d, (hl) - ld hl, Spectranet.PAGEOUT - call Spectranet.HLCALL - ex de, hl - End Asm -End Function - - -' ----------------------------------------------------------- -' Filesystem functions (fopen, fclose, fread, fwrite, fseek) -' ----------------------------------------------------------- - -' ----------------------------------------------------------- -' Mounts a remote filesystem. Returns session number. -' -' Example: -' SNETmount(0, "tnfs", "vexed4.alioth.net", "", "", "") -' ----------------------------------------------------------- -Function SNETmount(mpoint as Ubyte, proto$, host$, source$, userid$, passwd$) As Integer - REM Convert the functions to ASCIIZ - proto$ = ASCIIZ(proto$) - host$ = ASCIIZ(host$) - source$ = ASCIIZ(source$) - userid$ = ASCIIZ(userid$) - passwd$ = ASCIIZ(passwd$) - - DIM buffer(4) as Uinteger - DIM ix as UInteger - - buffer(0) = PEEK(Uinteger, @proto) + 2 - buffer(1) = PEEK(Uinteger, @host) + 2 - buffer(2) = PEEK(Uinteger, @source) + 2 - buffer(3) = PEEK(Uinteger, @userid) + 2 - buffer(4) = PEEK(Uinteger, @passwd) + 2 - - ix = @buffer(0) ' Useless, but will allow to calculate HL - Asm - ld a, (ix + 5) - push ix ; Must be restored upon return - push hl - pop ix - ld hl, Spectranet.MOUNT - call Spectranet.HLCALL - pop ix - End Asm -End Function - - -' ----------------------------------------------------------- -' Returns current mount point -' ----------------------------------------------------------- -Function SNETcurrMPoint() as Byte - print inK 7; paper 2; CAST(Ubyte, SNETpeekUinteger(0x1001)) - return SNETpeekUinteger(0x1001) -End Function - - -' ----------------------------------------------------------- -' Changes the current mount point -' ----------------------------------------------------------- -sub FASTCALL SNETsetmountpt(mpoint) - Asm - ld hl, Spectranet.SETMOUNTPOINT - call Spectranet.HLCALL - End Asm -End sub - - -' ----------------------------------------------------------- -' Umounts a previously mounted filesystem -' -' Example: -' SNETumount(0) -' ----------------------------------------------------------- -Function FASTCALL SNETumount(mpoint as UByte) As UInteger - Asm - ld hl, Spectranet.UMOUNT - call Spectranet.HLCALL - End Asm -End Function - - -' ----------------------------------------------------------- -' Opens a file and returns its handle. -1 on Error -' This function will be changed to copycat the C style, -' like: SNETfopen(0, "myfile", "rb"). Not yet. -' Returns the file handle (byte). -1 On error. -' -' Example: -' Opens a file for read. The 1st one is the mount point. -' The last parameter is ignored (0) -' fhandle = SNETfopen(0, "myfile.bin", O_RDONLY, 0) -' -' Opens a file for writing (creates the file). The last parameter -' is the chmod. -' fhandle = SNETopen(0, "newfile.blah", O_CREAT | O_WRONLY, 0666o) -' ----------------------------------------------------------- -Function SNETopen(mpoint as Ubyte, fname$, flags as UInteger, chmod as Uinteger) As Byte - DIM addrOfFname as Uinteger - fname$ = ASCIIZ(fname$) - addrOfFname = PEEK(Uinteger, @fname$) + 2 - Asm - ld a, (ix + 5) ; mount point - ld e, (ix + 8) - ld d, (ix + 9) ; de = flags (ORDONLY, etc...) - ld c, (ix + 10) - ld b, (ix + 11) ; bc = chmod mode - - push ix - ld ix, Spectranet.OPEN - call Spectranet.IXCALL - pop ix - End Asm -End Function - - -' ----------------------------------------------------------- -' Reads content from a file, an places it at a memory address. -' Returns the effectively number of bytes read. -' -' Example: -' Loading a binary screen (no .TAP file, just raw bytes) -' SNETfread(f, 16384, 6912) -' ----------------------------------------------------------- -Function FASTCALL SNETfread(fhandle as Ubyte, addr as Uinteger, size as Uinteger) As Uinteger - Asm - pop hl ; ret address - pop de - pop bc - push hl - ld hl, Spectranet.READ - call Spectranet.HLCALL - ld h, b ; BC = Num. of bytes read if no Carry - ld c, l - ret nc - ld (ERR_NR), a - End Asm -End Function - - -' ----------------------------------------------------------- -' Write content from a memory address into a file -' Returns the effectively number of bytes written. -' -' Example: -' Saving screen into a binary file (no .TAP file, just raw bytes) -' SNETfwrite(f, 16384, 6912) -' ----------------------------------------------------------- -Function FASTCALL SNETfwrite(fhandle as Ubyte, addr as Uinteger, size as Uinteger) As Uinteger - Asm - pop hl ; ret address - pop de ; addr - pop bc ; size - push hl ; ret address - ex de, hl ; HL = address to write - push ix - ld ix, Spectranet.WRITE - call Spectranet.IXCALL - pop ix - ld h, b - ld c, l - ret nc - ld (ERR_NR), a - End Asm -End Function - - - -' ----------------------------------------------------------- -' Mimics C fopen. Opens a file for reading or writing, -' returning its handle on success or -1 on error. -' ----------------------------------------------------------- -Function SNETfopen(mpoint as Ubyte, fname$, mode$) as Byte - dim flags As Uinteger = 0 - if inStr(mode$, "r") - flags = flags | O_RDONLY - end if - if inStr(mode$, "w") - flags = flags | O_CREAT | O_TRUNC | O_WRONLY - end if - if inStr(mode$, "a") - flags = flags | O_APPEND | O_CREAT | O_WRONLY - end if - if inStr(mode$, "+") - flags = flags | O_RDWR - end if - - return SNETopen(mpoint, fname$, flags, 666o) - -End Function - - -' ----------------------------------------------------------- -' Closes an open file. -' -' Example: -' SNETfclose(f) -' ----------------------------------------------------------- -Function FASTCALL SNETfclose(fhandle as Ubyte) As Byte - Asm - ld hl, Spectranet.CLOSE - call Spectranet.HLCALL - ret c - xor a ; Ensures A = 0 on success - End Asm -End Function - - -' ----------------------------------------------------------- -' Changes the current file read/write position. -' -' Example: -' - Jumps to the end of file -' SNETfseek(f, SEEK_END, 0) -' ----------------------------------------------------------- -Function FASTCALL SNETfseek(fhandle as Ubyte, op as Ubyte, pos as ULong) as Byte - Asm - pop hl ; Return address - ; pop af ; Not done. FASTCALL passes always the 1s parameter - pop bc ; Bytes comes in the high part, so B. - ld c, b ; C = operation - pop de ; Low Ulong 32 - ex (sp), hl ; Push ret address back, hl = high part - ex de, hl ; Now DEHL = Ulong 32 - push ix - ld ix, Spectranet.LSEEK - call Spectranet.IXCALL - pop ix - ret c - xor a ; Ensures A = 0 on success - End Asm -End function - - -' ----------------------------------------------------------- -' Deletes a file. -' -' Example: -' SNETunlink("myfile.dat") -' ----------------------------------------------------------- -Function SNETunlink(fname$) AS Byte - fname$ = ASCIIZ(fname$) - DIM addr as Uinteger - addr = PEEK(Uinteger, @fname) + 2 - Asm - PROC - LOCAL CONT - push ix - ld ix, Spectranet.UNLINK - call Spectranet.IXCALL - pop ix - jr c, CONT - xor a ; Ensures A = 0 on success - CONT: - ENDP - End Asm -End Function - - -#undef ASCIIZ -#undef ERR_NR - -#require "spectranet.inc" -#require "mem/free.asm" - -#pragma pop(case_insensitive) -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/template.bas b/src/lib/arch/zxnext/stdlib/template.bas index 3be4540b4..9946a4617 100644 --- a/src/lib/arch/zxnext/stdlib/template.bas +++ b/src/lib/arch/zxnext/stdlib/template.bas @@ -1,60 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' -' Use this file as a template to develop your own library file -' -' Replace with your desired identifier. I.e. for PAUSE -' use __LIBRARY_PAUSE__ (see pause.bas code) -' -' Suggestions: -' * Be methodic. Use something MEANINGFUL -' * Use *long* names to avoid collision with other developers. -' Names can be as long as you want, and are only used 3 times. -' ---------------------------------------------------------------- - -#ifndef __LIBRARY___ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY___ - -REM Put your code here. - -' ---------------------------------------------------------------- -' function NAME -' -' Parameters: -' x -' y ' -' Returns: -' (When nothing to return, use SUB instead) -' ---------------------------------------------------------------- -function NAME - -... -... - -end function - - -' ---------------------------------------------------------------- -' If your function above uses other functions in -' your module, the following name scheme is suggested -' to avoid collision with other developers functions +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -function PRIVATEXXXX - -... -... - -end function - - - -#endif - +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/winscroll.bas b/src/lib/arch/zxnext/stdlib/winscroll.bas index 45e5a9a4e..116072dee 100644 --- a/src/lib/arch/zxnext/stdlib/winscroll.bas +++ b/src/lib/arch/zxnext/stdlib/winscroll.bas @@ -2,546 +2,7 @@ ' This file is released under the MIT License ' ' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' -' Use this file as a template to develop your own library file +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -#ifndef __LIBRARY_WINSCROLL__ - -REM Avoid recursive / multiple inclusion - -#define __LIBRARY_WINSCROLL__ - -#pragma push(case_insensitive) -#pragma case_insensitive = True - -' --------------------------------------------------------------------- -' sub WinScrollRight -' scrolls the window defined by (row, col, width, height) 1 cell right -' --------------------------------------------------------------------- -sub fastcall WinScrollRight(row as uByte, col as uByte, width as Ubyte, height as Ubyte) - asm - push namespace core - LOCAL BucleChars - LOCAL BucleScans - LOCAL BucleAttrs - - PROC - ld b, a - pop hl - pop de - ld c, d - pop de - ex (sp), hl - ld e, h - ld a, e - or a - ret z - or d - ret z - push bc - ld a,b - and 18h - ld h,a - ld a,b - and 07h - add a,a - add a,a - add a,a - add a,a - add a,a - add a,c - add a,d - dec a - ld l,a ;HL=top-left window address in bitmap coord - ld bc, (SCREEN_ADDR) - add hl, bc - ld b,e - -BucleChars: - push bc - ld b,8 - -BucleScans: - push bc - push de - push hl - ld c,d - dec c - ld b,0 - ld d,h - ld e,l - dec l - lddr - ; clean up latest - xor a - ld (de),a - pop hl - pop de - inc h - pop bc - djnz BucleScans - dec h - call SP.PixelDown - pop bc - djnz BucleChars - - pop bc - ld l,b - ld h,0 - add hl,hl - add hl,hl - add hl,hl - add hl,hl - add hl,hl - ld a,l - add a,c - add a,d - dec a - ld l,a - ld a,h - ld h,a ;HL=top-left window address in attr coord - ld bc, (SCREEN_ATTR_ADDR) - add hl, bc - ld b,e - -BucleAttrs: - push bc - push de - push hl - ld c,d - dec c - ld b,0 - ld d,h - ld e,l - dec l - lddr - pop hl - ld de,32 - add hl,de - pop de - pop bc - djnz BucleAttrs - - ENDP - pop namespace - end asm -end sub - - -' --------------------------------------------------------------------- -' sub WinScrollLeft -' scrolls the window defined by (row, col, width, height) 1 cell left -' --------------------------------------------------------------------- -sub fastcall WinScrollLeft(row as uByte, col as uByte, width as Ubyte, height as Ubyte) - asm - push namespace core - PROC - LOCAL BucleChars - LOCAL BucleScans - LOCAL BucleAttrs - - ld b, a - pop hl - pop de - ld c, d - pop de - ex (sp), hl - ld e, h - ld a, e - or a - ret z - or d - ret z - push bc - ld a,b - and 18h - ld h,a - ld a,b - and 07h - add a,a - add a,a - add a,a - add a,a - add a,a - add a,c - ld l,a ;HL=top-left window address in bitmap coord - ld bc, (SCREEN_ADDR) - add hl, bc - ld b,e - -BucleChars: - push bc - ld b,8 - -BucleScans: - push bc - push de - push hl - ld c,d - dec c - ld b,0 - ld d,h - ld e,l - inc e - ex de,hl - ldir - ; clean up latest - xor a - ld (de),a - pop hl - pop de - inc h - pop bc - djnz BucleScans - dec h - call SP.PixelDown - pop bc - djnz BucleChars - - pop bc - ld l,b - ld h,0 - add hl,hl - add hl,hl - add hl,hl - add hl,hl - add hl,hl - ld a,l - add a,c - ld l,a - ld a,h - ld h,a ;HL=top-left address in attr coords - ld bc, (SCREEN_ATTR_ADDR) - add hl, bc - ld b,e - -BucleAttrs: - push bc - push de - push hl - ld c,d - dec c - ld b,0 - ld d,h - ld e,l - inc e - ex de,hl - ldir - pop hl - ld de,32 - add hl,de - pop de - pop bc - djnz BucleAttrs - ENDP - pop namespace - end asm -end sub - - -' --------------------------------------------------------------------- -' sub WinScrollUp -' scrolls the window defined by (row, col, width, height) 1 cell up -' --------------------------------------------------------------------- -sub fastcall WinScrollUp(row as uByte, col as uByte, width as Ubyte, height as Ubyte) - asm - push namespace core - PROC - LOCAL BucleScans, BucleAttrs, ScrollAttrs - LOCAL CleanLast, CleanLastLoop, EndCleanScan - - ld b, a - pop hl - pop de - ld c, d - pop de - ex (sp), hl - ld e, h - ld a, e - or a - ret z - or d - ret z - - push bc - push de - - ld a,b - and 18h - ld h,a - ld a,b - and 07h - add a,a - add a,a - add a,a - add a,a - add a,a - add a,c - ld l,a ;HL=top-left window address in bitmap coord - ld bc, (SCREEN_ADDR) - add hl, bc - ld a,e - ld c, d ; c = width - ld d, h - ld e, l - dec a - jr z, CleanLast - add a,a - add a,a - add a,a - ld b, a ; b = 8 * (height - 1) - - inc h - inc h - inc h - inc h - inc h - inc h - inc h - call SP.PixelDown - -BucleScans: - push bc - push de - push hl - ld b, 0 - ldir - pop hl - pop de - pop bc - call SP.PixelDown - ex de, hl - call SP.PixelDown - ex de, hl - djnz BucleScans - -CleanLast: - ex de,hl - pop de - ld b, 8 - ld c, d - push de - -CleanLastLoop: - push bc - push hl - ld (hl), 0 - dec c - jr z, EndCleanScan - ld d, h - ld e, l - inc de - ld b, 0 - ldir - -EndCleanScan: - pop hl - pop bc - inc h - djnz CleanLastLoop - -ScrollAttrs: - pop de - pop bc - ld l,b - ld h,0 - add hl,hl - add hl,hl - add hl,hl - add hl,hl - add hl,hl - ld a,l - add a,c - ld l,a - ld a,h - ld h,a ;HL=top-left address in attr coords - ld bc, (SCREEN_ATTR_ADDR) - add hl, bc - ld b,e - dec b - ret z - -BucleAttrs: - push bc - push de - push hl - ld b,0 - ld c,d - ex de,hl - ld hl,32 - add hl,de - ldir - pop hl - ld de,32 - add hl,de - pop de - pop bc - djnz BucleAttrs - ENDP - pop namespace - end asm - -end sub - - -' --------------------------------------------------------------------- -' sub WinScrollDown -' scrolls the window defined by (row, col, width, height) 1 cell down -' --------------------------------------------------------------------- -sub fastcall WinScrollDown(row as uByte, col as uByte, width as Ubyte, height as Ubyte) - asm - push namespace core - PROC - LOCAL BucleScans, BucleAttrs, ScrollAttrs - LOCAL CleanLast, CleanLastLoop, EndCleanScan - - ld b, a - pop hl - pop de - ld c, d - pop de - ex (sp), hl - ld e, h - ld a, e - or a - ret z - or d - ret z - - ld a, b - add a, e - dec a - ld b, a ; b = row + height - 1 = top most row - - push bc - push de - - ld a,b - and 18h - ld h,a - ld a,b - and 07h - add a,a - add a,a - add a,a - add a,a - add a,a - add a,c - ld l,a ;HL=bottom-left window address in bitmap coord - ld bc, (SCREEN_ADDR) - add hl, bc - ld a,e - ld c, d ; c = width - ld d, h - ld e, l - dec a - jr z, CleanLast - add a,a - add a,a - add a,a - ld b, a ; b = 8 * (height - 1) - - inc d - inc d - inc d - inc d - inc d - inc d - inc d - call SP.PixelUp - -BucleScans: - push bc - push de - push hl - ld b, 0 - ldir - pop hl - pop de - pop bc - call SP.PixelUp - ex de, hl - call SP.PixelUp - ex de, hl - djnz BucleScans - -CleanLast: - ex de,hl - pop de - ld b, 8 - ld c, d - push de - -CleanLastLoop: - push bc - push hl - ld (hl), 0 - dec c - jr z, EndCleanScan - ld d, h - ld e, l - inc de - ld b, 0 - ldir - -EndCleanScan: - pop hl - pop bc - dec h - djnz CleanLastLoop - -ScrollAttrs: - pop de - pop bc - ld l,b - ld h,0 - add hl,hl - add hl,hl - add hl,hl - add hl,hl - add hl,hl - ld a,l - add a,c - ld l,a - ld a,h - ld h,a ;HL=top-left address in attr coords - ld bc, (SCREEN_ATTR_ADDR) - add hl, bc - ld b,e - dec b - ret z - -BucleAttrs: - push bc - push de - push hl - ld b,0 - ld c,d - ex de,hl - ld hl,-32 - add hl,de - ldir - pop hl - ld de,-32 - add hl,de - pop de - pop bc - djnz BucleAttrs - ENDP - pop namespace - end asm - -end sub - -#pragma pop(case_insensitive) - -REM the following is required, because it defines screen start addr -#require "cls.asm" -#require "SP/PixelDown.asm" -#require "SP/PixelUp.asm" -#require "sysvars.asm" - - -#endif +#include once [arch:zx48k] diff --git a/src/lib/arch/zxnext/stdlib/zx0.bas b/src/lib/arch/zxnext/stdlib/zx0.bas index 7f508c1f7..c863e84a5 100644 --- a/src/lib/arch/zxnext/stdlib/zx0.bas +++ b/src/lib/arch/zxnext/stdlib/zx0.bas @@ -1,1762 +1,8 @@ -' ----------------------------------------------------------------------------- -' BORIEL LIBRARY FOR ZX0 DECOMPRESSORS -' -' USAGE: -' -' 1. Include this library in your program: -' #include -' -' 2. Choose a ZX0 decompressor to use in your program, for instance: -' dzx0Turbo(51200, 16384) -' -' 3. Compile your program: -' zxb.exe -T -B prog.bas -' -' Original version and further information is available at -' https://github.com/einar-saukas/ZX0 -' -' Copyleft (k) Einar Saukas -' ----------------------------------------------------------------------------- - -#ifndef __LIBRARY_ZX0__ -#define __LIBRARY_ZX0__ -#pragma push(case_insensitive) -#pragma case_insensitive = true - - -' ----------------------------------------------------------------------------- -' Decompress (from source to destination address) data that was previously -' compressed using ZX0. This is the smallest version of the ZX0 decompressor. -' -' Parameters: -' src: source address (compressed data) -' dst: destination address (decompressing) -' ----------------------------------------------------------------------------- -sub FASTCALL dzx0Standard(src as UINTEGER, dst as UINTEGER) - asm - pop bc ; RET address - pop de ; DE=dst - push bc ; restore RET address -; ----------------------------------------------------------------------------- -; ZX0 decoder by Einar Saukas & Urusergi -; "Standard" version (68 bytes only) -; ----------------------------------------------------------------------------- -; Parameters: -; HL: source address (compressed data) -; DE: destination address (decompressing) -; ----------------------------------------------------------------------------- -dzx0_standard: - ld bc, $ffff ; preserve default offset 1 - push bc - inc bc - ld a, $80 -dzx0s_literals: - call dzx0s_elias ; obtain length - ldir ; copy literals - add a, a ; copy from last offset or new offset? - jr c, dzx0s_new_offset - call dzx0s_elias ; obtain length -dzx0s_copy: - ex (sp), hl ; preserve source, restore offset - push hl ; preserve offset - add hl, de ; calculate destination - offset - ldir ; copy from offset - pop hl ; restore offset - ex (sp), hl ; preserve offset, restore source - add a, a ; copy from literals or new offset? - jr nc, dzx0s_literals -dzx0s_new_offset: - pop bc ; discard last offset - ld c, $fe ; prepare negative offset - call dzx0s_elias_loop ; obtain offset MSB - inc c - ret z ; check end marker - ld b, c - ld c, (hl) ; obtain offset LSB - inc hl - rr b ; last offset bit becomes first length bit - rr c - push bc ; preserve new offset - ld bc, 1 ; obtain length - call nc, dzx0s_elias_backtrack - inc bc - jr dzx0s_copy -dzx0s_elias: - inc c ; interlaced Elias gamma coding -dzx0s_elias_loop: - add a, a - jr nz, dzx0s_elias_skip - ld a, (hl) ; load another group of 8 bits - inc hl - rla -dzx0s_elias_skip: - ret c -dzx0s_elias_backtrack: - add a, a - rl c - rl b - jr dzx0s_elias_loop -; ----------------------------------------------------------------------------- - end asm -end sub - - -' ----------------------------------------------------------------------------- -' Decompress (from source to destination address) data that was previously -' compressed backwards using ZX0. This is the smallest version of the ZX0 -' decompressor. -' -' Parameters: -' src: last source address (compressed data) -' dst: last destination address (decompressing) -' ----------------------------------------------------------------------------- -sub FASTCALL dzx0StandardBack(src as UINTEGER, dst as UINTEGER) - asm - pop bc ; RET address - pop de ; DE=dst - push bc ; restore RET address -; ----------------------------------------------------------------------------- -; ZX0 decoder by Einar Saukas -; "Standard" version (69 bytes only) - BACKWARDS VARIANT -; ----------------------------------------------------------------------------- -; Parameters: -; HL: last source address (compressed data) -; DE: last destination address (decompressing) -; ----------------------------------------------------------------------------- -dzx0_standard_back: - ld bc, 1 ; preserve default offset 1 - push bc - ld a, $80 -dzx0sb_literals: - call dzx0sb_elias ; obtain length - lddr ; copy literals - inc c - add a, a ; copy from last offset or new offset? - jr c, dzx0sb_new_offset - call dzx0sb_elias ; obtain length -dzx0sb_copy: - ex (sp), hl ; preserve source, restore offset - push hl ; preserve offset - add hl, de ; calculate destination - offset - lddr ; copy from offset - inc c - pop hl ; restore offset - ex (sp), hl ; preserve offset, restore source - add a, a ; copy from literals or new offset? - jr nc, dzx0sb_literals -dzx0sb_new_offset: - inc sp ; discard last offset - inc sp - call dzx0sb_elias ; obtain offset MSB - dec b - ret z ; check end marker - dec c ; adjust for positive offset - ld b, c - ld c, (hl) ; obtain offset LSB - dec hl - srl b ; last offset bit becomes first length bit - rr c - inc bc - push bc ; preserve new offset - ld bc, 1 ; obtain length - call c, dzx0sb_elias_backtrack - inc bc - jr dzx0sb_copy -dzx0sb_elias_backtrack: - add a, a - rl c - rl b -dzx0sb_elias: - add a, a ; inverted interlaced Elias gamma coding - jr nz, dzx0sb_elias_skip - ld a, (hl) ; load another group of 8 bits - dec hl - rla -dzx0sb_elias_skip: - jr c, dzx0sb_elias_backtrack - ret -; ----------------------------------------------------------------------------- - end asm -end sub - - -' ----------------------------------------------------------------------------- -' Decompress (from source to destination address) data that was previously -' compressed using ZX0. This is the intermediate version of the ZX0 -' decompressor, providing the best balance between speed and size. -' -' Parameters: -' src: source address (compressed data) -' dst: destination address (decompressing) -' ----------------------------------------------------------------------------- -sub FASTCALL dzx0Turbo(src as UINTEGER, dst as UINTEGER) - asm - pop bc ; RET address - pop de ; DE=dst - push bc ; restore RET address -; ----------------------------------------------------------------------------- -; ZX0 decoder by Einar Saukas & introspec -; "Turbo" version (126 bytes, 21% faster) -; ----------------------------------------------------------------------------- -; Parameters: -; HL: source address (compressed data) -; DE: destination address (decompressing) -; ----------------------------------------------------------------------------- -dzx0_turbo: - ld bc, $ffff ; preserve default offset 1 - ld (dzx0t_last_offset+1), bc - inc bc - ld a, $80 - jr dzx0t_literals -dzx0t_new_offset: - ld c, $fe ; prepare negative offset - add a, a - jp nz, dzx0t_new_offset_skip - ld a, (hl) ; load another group of 8 bits - inc hl - rla -dzx0t_new_offset_skip: - call nc, dzx0t_elias ; obtain offset MSB - inc c - ret z ; check end marker - ld b, c - ld c, (hl) ; obtain offset LSB - inc hl - rr b ; last offset bit becomes first length bit - rr c - ld (dzx0t_last_offset+1), bc ; preserve new offset - ld bc, 1 ; obtain length - call nc, dzx0t_elias - inc bc -dzx0t_copy: - push hl ; preserve source -dzx0t_last_offset: - ld hl, 0 ; restore offset - add hl, de ; calculate destination - offset - ldir ; copy from offset - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr c, dzx0t_new_offset -dzx0t_literals: - inc c ; obtain length - add a, a - jp nz, dzx0t_literals_skip - ld a, (hl) ; load another group of 8 bits - inc hl - rla -dzx0t_literals_skip: - call nc, dzx0t_elias - ldir ; copy literals - add a, a ; copy from last offset or new offset? - jr c, dzx0t_new_offset - inc c ; obtain length - add a, a - jp nz, dzx0t_last_offset_skip - ld a, (hl) ; load another group of 8 bits - inc hl - rla -dzx0t_last_offset_skip: - call nc, dzx0t_elias - jp dzx0t_copy -dzx0t_elias: - add a, a ; interlaced Elias gamma coding - rl c - add a, a - jr nc, dzx0t_elias - ret nz - ld a, (hl) ; load another group of 8 bits - inc hl - rla - ret c - add a, a - rl c - add a, a - ret c - add a, a - rl c - add a, a - ret c - add a, a - rl c - add a, a - ret c -dzx0t_elias_loop: - add a, a - rl c - rl b - add a, a - jr nc, dzx0t_elias_loop - ret nz - ld a, (hl) ; load another group of 8 bits - inc hl - rla - jr nc, dzx0t_elias_loop - ret -; ----------------------------------------------------------------------------- - end asm -end sub - - -' ----------------------------------------------------------------------------- -' Decompress (from source to destination address) data that was previously -' compressed backwards using ZX0. This is the intermediate version of the ZX0 -' decompressor, providing the best balance between speed and size. -' -' Parameters: -' src: last source address (compressed data) -' dst: last destination address (decompressing) -' ----------------------------------------------------------------------------- -sub FASTCALL dzx0TurboBack(src as UINTEGER, dst as UINTEGER) - asm - pop bc ; RET address - pop de ; DE=dst - push bc ; restore RET address -; ----------------------------------------------------------------------------- -; ZX0 decoder by Einar Saukas & introspec -; "Turbo" version (126 bytes, 21% faster) - BACKWARDS VARIANT -; ----------------------------------------------------------------------------- -; Parameters: -; HL: last source address (compressed data) -; DE: last destination address (decompressing) -; ----------------------------------------------------------------------------- -dzx0_turbo_back: - ld bc, 1 ; preserve default offset 1 - ld (dzx0tb_last_offset+1), bc - ld a, $80 - jr dzx0tb_literals -dzx0tb_new_offset: - add a, a ; obtain offset MSB - call c, dzx0tb_elias - dec b - ret z ; check end marker - dec c ; adjust for positive offset - ld b, c - ld c, (hl) ; obtain offset LSB - dec hl - srl b ; last offset bit becomes first length bit - rr c - inc bc - ld (dzx0tb_last_offset+1), bc ; preserve new offset - ld bc, 1 ; obtain length - call c, dzx0tb_elias_loop - inc bc -dzx0tb_copy: - push hl ; preserve source -dzx0tb_last_offset: - ld hl, 0 ; restore offset - add hl, de ; calculate destination - offset - lddr ; copy from offset - inc c - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr c, dzx0tb_new_offset -dzx0tb_literals: - add a, a ; obtain length - call c, dzx0tb_elias - lddr ; copy literals - inc c - add a, a ; copy from last offset or new offset? - jr c, dzx0tb_new_offset - add a, a ; obtain length - call c, dzx0tb_elias - jp dzx0tb_copy -dzx0tb_elias_loop: - add a, a - rl c - add a, a - ret nc -dzx0tb_elias: - jp nz, dzx0tb_elias_loop ; inverted interlaced Elias gamma coding - ld a, (hl) ; load another group of 8 bits - dec hl - rla - ret nc - add a, a - rl c - add a, a - ret nc - add a, a - rl c - add a, a - ret nc - add a, a - rl c - add a, a - ret nc -dzx0tb_elias_reload: - add a, a - rl c - rl b - add a, a - ld a, (hl) ; load another group of 8 bits - dec hl - rla - ret nc - add a, a - rl c - rl b - add a, a - ret nc - add a, a - rl c - rl b - add a, a - ret nc - add a, a - rl c - rl b - add a, a - jr c, dzx0tb_elias_reload - ret -; ----------------------------------------------------------------------------- - end asm -end sub - - -' ----------------------------------------------------------------------------- -' Decompress (from source to destination address) data that was previously -' compressed using ZX0. This is the fastest version of the ZX0 decompressor. -' -' Parameters: -' src: source address (compressed data) -' dst: destination address (decompressing) -' ----------------------------------------------------------------------------- -sub FASTCALL dzx0Mega(src as UINTEGER, dst as UINTEGER) - asm - pop bc ; RET address - pop de ; DE=dst - push bc ; restore RET address -; ----------------------------------------------------------------------------- -; ZX0 decoder by Einar Saukas -; "Mega" version (673 bytes, 28% faster) -; ----------------------------------------------------------------------------- -; Parameters: -; HL: source address (compressed data) -; DE: destination address (decompressing) -; ----------------------------------------------------------------------------- -dzx0_mega: - ld bc, $ffff ; preserve default offset 1 - ld (dzx0m_last_offset+1), bc - inc bc - jr dzx0m_literals0 - -dzx0m_new_offset6: - ld c, $fe ; prepare negative offset - add a, a ; obtain offset MSB - jp c, dzx0m_new_offset5 - add a, a - rl c - add a, a - jp c, dzx0m_new_offset3 - add a, a - rl c - add a, a - jp c, dzx0m_new_offset1 -dzx0m_elias_offset1: - add a, a - rl c - rl b - ld a, (hl) ; load another group of 8 bits - inc hl - add a, a - jp nc, dzx0m_elias_offset7 -dzx0m_new_offset7: - inc c - ret z ; check end marker - ld b, c - ld c, (hl) ; obtain offset LSB - inc hl - rr b ; last offset bit becomes first length bit - rr c - ld (dzx0m_last_offset+1), bc ; preserve new offset - ld bc, 1 - jp c, dzx0m_length7 ; obtain length - add a, a - rl c - add a, a - jp c, dzx0m_length5 - add a, a - rl c - add a, a - jp c, dzx0m_length3 -dzx0m_elias_length3: - add a, a - rl c - rl b - add a, a - jp nc, dzx0m_elias_length1 -dzx0m_length1: - push hl ; preserve source - ld hl, (dzx0m_last_offset+1) - add hl, de ; calculate destination - offset - ldir ; copy from offset - inc c - ldi ; copy one more from offset - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr c, dzx0m_new_offset0 -dzx0m_literals0: - inc c - ld a, (hl) ; load another group of 8 bits - inc hl - add a, a ; obtain length - jp c, dzx0m_literals7 - add a, a - rl c - add a, a - jp c, dzx0m_literals5 - add a, a - rl c - add a, a - jp c, dzx0m_literals3 -dzx0m_elias_literals3: - add a, a - rl c - rl b - add a, a - jp nc, dzx0m_elias_literals1 -dzx0m_literals1: - ldir ; copy literals - add a, a ; copy from last offset or new offset? - jr c, dzx0m_new_offset0 - inc c - ld a, (hl) ; load another group of 8 bits - inc hl - add a, a ; obtain length - jp c, dzx0m_reuse7 - add a, a - rl c - add a, a - jp c, dzx0m_reuse5 - add a, a - rl c - add a, a - jp c, dzx0m_reuse3 -dzx0m_elias_reuse3: - add a, a - rl c - rl b - add a, a - jp nc, dzx0m_elias_reuse1 -dzx0m_reuse1: - push hl ; preserve source - ld hl, (dzx0m_last_offset+1) - add hl, de ; calculate destination - offset - ldir ; copy from offset - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr nc, dzx0m_literals0 - -dzx0m_new_offset0: - ld c, $fe ; prepare negative offset - ld a, (hl) ; load another group of 8 bits - inc hl - add a, a ; obtain offset MSB - jp c, dzx0m_new_offset7 - add a, a - rl c - add a, a - jp c, dzx0m_new_offset5 - add a, a - rl c - add a, a - jp c, dzx0m_new_offset3 -dzx0m_elias_offset3: - add a, a - rl c - rl b - add a, a - jp nc, dzx0m_elias_offset1 -dzx0m_new_offset1: - inc c - ret z ; check end marker - ld b, c - ld c, (hl) ; obtain offset LSB - inc hl - rr b ; last offset bit becomes first length bit - rr c - ld (dzx0m_last_offset+1), bc ; preserve new offset - ld bc, 1 - jp c, dzx0m_length1 ; obtain length - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - inc hl - add a, a - jp c, dzx0m_length7 - add a, a - rl c - add a, a - jp c, dzx0m_length5 -dzx0m_elias_length5: - add a, a - rl c - rl b - add a, a - jp nc, dzx0m_elias_length3 -dzx0m_length3: - push hl ; preserve source - ld hl, (dzx0m_last_offset+1) - add hl, de ; calculate destination - offset - ldir ; copy from offset - inc c - ldi ; copy one more from offset - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr c, dzx0m_new_offset2 -dzx0m_literals2: - inc c - add a, a ; obtain length - jp c, dzx0m_literals1 - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - inc hl - add a, a - jp c, dzx0m_literals7 - add a, a - rl c - add a, a - jp c, dzx0m_literals5 -dzx0m_elias_literals5: - add a, a - rl c - rl b - add a, a - jp nc, dzx0m_elias_literals3 -dzx0m_literals3: - ldir ; copy literals - add a, a ; copy from last offset or new offset? - jr c, dzx0m_new_offset2 - inc c - add a, a ; obtain length - jp c, dzx0m_reuse1 - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - inc hl - add a, a - jp c, dzx0m_reuse7 - add a, a - rl c - add a, a - jp c, dzx0m_reuse5 -dzx0m_elias_reuse5: - add a, a - rl c - rl b - add a, a - jp nc, dzx0m_elias_reuse3 -dzx0m_reuse3: - push hl ; preserve source - ld hl, (dzx0m_last_offset+1) - add hl, de ; calculate destination - offset - ldir ; copy from offset - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr nc, dzx0m_literals2 - -dzx0m_new_offset2: - ld c, $fe ; prepare negative offset - add a, a ; obtain offset MSB - jp c, dzx0m_new_offset1 - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - inc hl - add a, a - jp c, dzx0m_new_offset7 - add a, a - rl c - add a, a - jp c, dzx0m_new_offset5 -dzx0m_elias_offset5: - add a, a - rl c - rl b - add a, a - jp nc, dzx0m_elias_offset3 -dzx0m_new_offset3: - inc c - ret z ; check end marker - ld b, c - ld c, (hl) ; obtain offset LSB - inc hl - rr b ; last offset bit becomes first length bit - rr c - ld (dzx0m_last_offset+1), bc ; preserve new offset - ld bc, 1 - jp c, dzx0m_length3 ; obtain length - add a, a - rl c - add a, a - jp c, dzx0m_length1 - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - inc hl - add a, a - jp c, dzx0m_length7 -dzx0m_elias_length7: - add a, a - rl c - rl b - add a, a - jp nc, dzx0m_elias_length5 -dzx0m_length5: - push hl ; preserve source - ld hl, (dzx0m_last_offset+1) - add hl, de ; calculate destination - offset - ldir ; copy from offset - inc c - ldi ; copy one more from offset - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr c, dzx0m_new_offset4 -dzx0m_literals4: - inc c - add a, a ; obtain length - jp c, dzx0m_literals3 - add a, a - rl c - add a, a - jp c, dzx0m_literals1 - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - inc hl - add a, a - jp c, dzx0m_literals7 -dzx0m_elias_literals7: - add a, a - rl c - rl b - add a, a - jp nc, dzx0m_elias_literals5 -dzx0m_literals5: - ldir ; copy literals - add a, a ; copy from last offset or new offset? - jr c, dzx0m_new_offset4 - inc c - add a, a ; obtain length - jp c, dzx0m_reuse3 - add a, a - rl c - add a, a - jp c, dzx0m_reuse1 - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - inc hl - add a, a - jp c, dzx0m_reuse7 -dzx0m_elias_reuse7: - add a, a - rl c - rl b - add a, a - jp nc, dzx0m_elias_reuse5 -dzx0m_reuse5: - push hl ; preserve source - ld hl, (dzx0m_last_offset+1) - add hl, de ; calculate destination - offset - ldir ; copy from offset - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr nc, dzx0m_literals4 - -dzx0m_new_offset4: - ld c, $fe ; prepare negative offset - add a, a ; obtain offset MSB - jp c, dzx0m_new_offset3 - add a, a - rl c - add a, a - jp c, dzx0m_new_offset1 - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - inc hl - add a, a - jp c, dzx0m_new_offset7 -dzx0m_elias_offset7: - add a, a - rl c - rl b - add a, a - jp nc, dzx0m_elias_offset5 -dzx0m_new_offset5: - inc c - ret z ; check end marker - ld b, c - ld c, (hl) ; obtain offset LSB - inc hl - rr b ; last offset bit becomes first length bit - rr c - ld (dzx0m_last_offset+1), bc ; preserve new offset - ld bc, 1 - jp c, dzx0m_length5 ; obtain length - add a, a - rl c - add a, a - jp c, dzx0m_length3 - add a, a - rl c - add a, a - jp c, dzx0m_length1 -dzx0m_elias_length1: - add a, a - rl c - rl b - ld a, (hl) ; load another group of 8 bits - inc hl - add a, a - jp nc, dzx0m_elias_length7 -dzx0m_length7: - push hl ; preserve source - ld hl, (dzx0m_last_offset+1) - add hl, de ; calculate destination - offset - ldir ; copy from offset - inc c - ldi ; copy one more from offset - pop hl ; restore source - add a, a ; copy from literals or new offset? - jp c, dzx0m_new_offset6 -dzx0m_literals6: - inc c - add a, a ; obtain length - jp c, dzx0m_literals5 - add a, a - rl c - add a, a - jp c, dzx0m_literals3 - add a, a - rl c - add a, a - jp c, dzx0m_literals1 -dzx0m_elias_literals1: - add a, a - rl c - rl b - ld a, (hl) ; load another group of 8 bits - inc hl - add a, a - jp nc, dzx0m_elias_literals7 -dzx0m_literals7: - ldir ; copy literals - add a, a ; copy from last offset or new offset? - jp c, dzx0m_new_offset6 - inc c - add a, a ; obtain length - jp c, dzx0m_reuse5 - add a, a - rl c - add a, a - jp c, dzx0m_reuse3 - add a, a - rl c - add a, a - jp c, dzx0m_reuse1 -dzx0m_elias_reuse1: - add a, a - rl c - rl b - ld a, (hl) ; load another group of 8 bits - inc hl - add a, a - jp nc, dzx0m_elias_reuse7 -dzx0m_reuse7: - push hl ; preserve source -dzx0m_last_offset: - ld hl, 0 - add hl, de ; calculate destination - offset - ldir ; copy from offset - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr nc, dzx0m_literals6 - - jp dzx0m_new_offset6 -; ----------------------------------------------------------------------------- - end asm -end sub - - -' ----------------------------------------------------------------------------- -' Decompress (from source to destination address) data that was previously -' compressed backwards using ZX0. This is the fastest version of the ZX0 -' decompressor. -' -' Parameters: -' src: last source address (compressed data) -' dst: last destination address (decompressing) -' ----------------------------------------------------------------------------- -sub FASTCALL dzx0MegaBack(src as UINTEGER, dst as UINTEGER) - asm - pop bc ; RET address - pop de ; DE=dst - push bc ; restore RET address -; ----------------------------------------------------------------------------- -; ZX0 decoder by Einar Saukas & introspec -; "Mega" version (676 bytes, 28% faster) - BACKWARDS VARIANT -; ----------------------------------------------------------------------------- -; Parameters: -; HL: last source address (compressed data) -; DE: last destination address (decompressing) -; ----------------------------------------------------------------------------- -dzx0_mega_back: - ld bc, 1 ; preserve default offset 1 - ld (dzx0mb_last_offset+1), bc - jr dzx0mb_literals0 - -dzx0mb_new_offset6: - add a, a ; obtain offset MSB - jp nc, dzx0mb_new_offset5 - add a, a - rl c - add a, a - jp nc, dzx0mb_new_offset3 - add a, a - rl c - add a, a - jp nc, dzx0mb_new_offset1 -dzx0mb_elias_offset1: - add a, a - rl c - rl b - ld a, (hl) ; load another group of 8 bits - dec hl - add a, a - jp c, dzx0mb_elias_offset7 -dzx0mb_new_offset7: - dec b - ret z ; check end marker - dec c ; adjust for positive offset - ld b, c - ld c, (hl) ; obtain offset LSB - dec hl - srl b ; last offset bit becomes first length bit - rr c - inc bc - ld (dzx0mb_last_offset+1), bc ; preserve new offset - ld bc, 1 - jp nc, dzx0mb_length7 ; obtain length - add a, a - rl c - add a, a - jp nc, dzx0mb_length5 - add a, a - rl c - add a, a - jp nc, dzx0mb_length3 -dzx0mb_elias_length3: - add a, a - rl c - rl b - add a, a - jp c, dzx0mb_elias_length1 -dzx0mb_length1: - push hl ; preserve source - ld hl, (dzx0mb_last_offset+1) - add hl, de ; calculate destination - offset - lddr ; copy from offset - inc c - ldd ; copy one more from offset - inc c - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr c, dzx0mb_new_offset0 -dzx0mb_literals0: - ld a, (hl) ; load another group of 8 bits - dec hl - add a, a ; obtain length - jp nc, dzx0mb_literals7 - add a, a - rl c - add a, a - jp nc, dzx0mb_literals5 - add a, a - rl c - add a, a - jp nc, dzx0mb_literals3 -dzx0mb_elias_literals3: - add a, a - rl c - rl b - add a, a - jp c, dzx0mb_elias_literals1 -dzx0mb_literals1: - lddr ; copy literals - inc c - add a, a ; copy from last offset or new offset? - jr c, dzx0mb_new_offset0 - ld a, (hl) ; load another group of 8 bits - dec hl - add a, a ; obtain length - jp nc, dzx0mb_reuse7 - add a, a - rl c - add a, a - jp nc, dzx0mb_reuse5 - add a, a - rl c - add a, a - jp nc, dzx0mb_reuse3 -dzx0mb_elias_reuse3: - add a, a - rl c - rl b - add a, a - jp c, dzx0mb_elias_reuse1 -dzx0mb_reuse1: - push hl ; preserve source - ld hl, (dzx0mb_last_offset+1) - add hl, de ; calculate destination - offset - lddr ; copy from offset - inc c - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr nc, dzx0mb_literals0 - -dzx0mb_new_offset0: - ld a, (hl) ; load another group of 8 bits - dec hl - add a, a ; obtain offset MSB - jp nc, dzx0mb_new_offset7 - add a, a - rl c - add a, a - jp nc, dzx0mb_new_offset5 - add a, a - rl c - add a, a - jp nc, dzx0mb_new_offset3 -dzx0mb_elias_offset3: - add a, a - rl c - rl b - add a, a - jp c, dzx0mb_elias_offset1 -dzx0mb_new_offset1: - dec b - ret z ; check end marker - dec c ; adjust for positive offset - ld b, c - ld c, (hl) ; obtain offset LSB - dec hl - srl b ; last offset bit becomes first length bit - rr c - inc bc - ld (dzx0mb_last_offset+1), bc ; preserve new offset - ld bc, 1 - jp nc, dzx0mb_length1 ; obtain length - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - dec hl - add a, a - jp nc, dzx0mb_length7 - add a, a - rl c - add a, a - jp nc, dzx0mb_length5 -dzx0mb_elias_length5: - add a, a - rl c - rl b - add a, a - jp c, dzx0mb_elias_length3 -dzx0mb_length3: - push hl ; preserve source - ld hl, (dzx0mb_last_offset+1) - add hl, de ; calculate destination - offset - lddr ; copy from offset - inc c - ldd ; copy one more from offset - inc c - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr c, dzx0mb_new_offset2 -dzx0mb_literals2: - add a, a ; obtain length - jp nc, dzx0mb_literals1 - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - dec hl - add a, a - jp nc, dzx0mb_literals7 - add a, a - rl c - add a, a - jp nc, dzx0mb_literals5 -dzx0mb_elias_literals5: - add a, a - rl c - rl b - add a, a - jp c, dzx0mb_elias_literals3 -dzx0mb_literals3: - lddr ; copy literals - inc c - add a, a ; copy from last offset or new offset? - jr c, dzx0mb_new_offset2 - add a, a ; obtain length - jp nc, dzx0mb_reuse1 - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - dec hl - add a, a - jp nc, dzx0mb_reuse7 - add a, a - rl c - add a, a - jp nc, dzx0mb_reuse5 -dzx0mb_elias_reuse5: - add a, a - rl c - rl b - add a, a - jp c, dzx0mb_elias_reuse3 -dzx0mb_reuse3: - push hl ; preserve source - ld hl, (dzx0mb_last_offset+1) - add hl, de ; calculate destination - offset - lddr ; copy from offset - inc c - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr nc, dzx0mb_literals2 - -dzx0mb_new_offset2: - add a, a ; obtain offset MSB - jp nc, dzx0mb_new_offset1 - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - dec hl - add a, a - jp nc, dzx0mb_new_offset7 - add a, a - rl c - add a, a - jp nc, dzx0mb_new_offset5 -dzx0mb_elias_offset5: - add a, a - rl c - rl b - add a, a - jp c, dzx0mb_elias_offset3 -dzx0mb_new_offset3: - dec b - ret z ; check end marker - dec c ; adjust for positive offset - ld b, c - ld c, (hl) ; obtain offset LSB - dec hl - srl b ; last offset bit becomes first length bit - rr c - inc bc - ld (dzx0mb_last_offset+1), bc ; preserve new offset - ld bc, 1 - jp nc, dzx0mb_length3 ; obtain length - add a, a - rl c - add a, a - jp nc, dzx0mb_length1 - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - dec hl - add a, a - jp nc, dzx0mb_length7 -dzx0mb_elias_length7: - add a, a - rl c - rl b - add a, a - jp c, dzx0mb_elias_length5 -dzx0mb_length5: - push hl ; preserve source - ld hl, (dzx0mb_last_offset+1) - add hl, de ; calculate destination - offset - lddr ; copy from offset - inc c - ldd ; copy one more from offset - inc c - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr c, dzx0mb_new_offset4 -dzx0mb_literals4: - add a, a ; obtain length - jp nc, dzx0mb_literals3 - add a, a - rl c - add a, a - jp nc, dzx0mb_literals1 - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - dec hl - add a, a - jp nc, dzx0mb_literals7 -dzx0mb_elias_literals7: - add a, a - rl c - rl b - add a, a - jp c, dzx0mb_elias_literals5 -dzx0mb_literals5: - lddr ; copy literals - inc c - add a, a ; copy from last offset or new offset? - jr c, dzx0mb_new_offset4 - add a, a ; obtain length - jp nc, dzx0mb_reuse3 - add a, a - rl c - add a, a - jp nc, dzx0mb_reuse1 - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - dec hl - add a, a - jp nc, dzx0mb_reuse7 -dzx0mb_elias_reuse7: - add a, a - rl c - rl b - add a, a - jp c, dzx0mb_elias_reuse5 -dzx0mb_reuse5: - push hl ; preserve source - ld hl, (dzx0mb_last_offset+1) - add hl, de ; calculate destination - offset - lddr ; copy from offset - inc c - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr nc, dzx0mb_literals4 - -dzx0mb_new_offset4: - add a, a ; obtain offset MSB - jp nc, dzx0mb_new_offset3 - add a, a - rl c - add a, a - jp nc, dzx0mb_new_offset1 - add a, a - rl c - ld a, (hl) ; load another group of 8 bits - dec hl - add a, a - jp nc, dzx0mb_new_offset7 -dzx0mb_elias_offset7: - add a, a - rl c - rl b - add a, a - jp c, dzx0mb_elias_offset5 -dzx0mb_new_offset5: - dec b - ret z ; check end marker - dec c ; adjust for positive offset - ld b, c - ld c, (hl) ; obtain offset LSB - dec hl - srl b ; last offset bit becomes first length bit - rr c - inc bc - ld (dzx0mb_last_offset+1), bc ; preserve new offset - ld bc, 1 - jp nc, dzx0mb_length5 ; obtain length - add a, a - rl c - add a, a - jp nc, dzx0mb_length3 - add a, a - rl c - add a, a - jp nc, dzx0mb_length1 -dzx0mb_elias_length1: - add a, a - rl c - rl b - ld a, (hl) ; load another group of 8 bits - dec hl - add a, a - jp c, dzx0mb_elias_length7 -dzx0mb_length7: - push hl ; preserve source - ld hl, (dzx0mb_last_offset+1) - add hl, de ; calculate destination - offset - lddr ; copy from offset - inc c - ldd ; copy one more from offset - inc c - pop hl ; restore source - add a, a ; copy from literals or new offset? - jp c, dzx0mb_new_offset6 -dzx0mb_literals6: - add a, a ; obtain length - jp nc, dzx0mb_literals5 - add a, a - rl c - add a, a - jp nc, dzx0mb_literals3 - add a, a - rl c - add a, a - jp nc, dzx0mb_literals1 -dzx0mb_elias_literals1: - add a, a - rl c - rl b - ld a, (hl) ; load another group of 8 bits - dec hl - add a, a - jp c, dzx0mb_elias_literals7 -dzx0mb_literals7: - lddr ; copy literals - inc c - add a, a ; copy from last offset or new offset? - jp c, dzx0mb_new_offset6 - add a, a ; obtain length - jp nc, dzx0mb_reuse5 - add a, a - rl c - add a, a - jp nc, dzx0mb_reuse3 - add a, a - rl c - add a, a - jp nc, dzx0mb_reuse1 -dzx0mb_elias_reuse1: - add a, a - rl c - rl b - ld a, (hl) ; load another group of 8 bits - dec hl - add a, a - jp c, dzx0mb_elias_reuse7 -dzx0mb_reuse7: - push hl ; preserve source -dzx0mb_last_offset: - ld hl, 0 - add hl, de ; calculate destination - offset - lddr ; copy from offset - inc c - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr nc, dzx0mb_literals6 - - jp dzx0mb_new_offset6 -; ----------------------------------------------------------------------------- - end asm -end sub - - -' ----------------------------------------------------------------------------- -' Decompress (from source to destination address) data that was previously -' compressed using ZX0. This is the smallest version of the integrated RCS+ZX0 -' decompressor. -' -' IMPORTANT: Data decompressed directly to the ZX-Spectrum screen must be both -' RCS encoded and ZX0 compressed, everything else must be ZX0 compressed only. -' -' Parameters: -' src: source address (compressed data) -' dst: destination address (decompressing) -' ----------------------------------------------------------------------------- -sub FASTCALL dzx0SmartRCS(src as UINTEGER, dst as UINTEGER) - asm - pop bc ; RET address - pop de ; DE=dst - push bc ; restore RET address -; ----------------------------------------------------------------------------- -; "Smart" integrated RCS+ZX0 decoder by Einar Saukas (112 bytes) -; ----------------------------------------------------------------------------- -; Parameters: -; HL: source address (compressed data) -; DE: destination address (decompressing) -; ----------------------------------------------------------------------------- -dzx0_smartrcs: - ld bc, $ffff ; preserve default offset 1 - push bc - inc bc - ld a, $80 -dzx0r_literals: - call dzx0r_elias ; obtain length -dzx0r_literals_loop: - call dzx0r_copy_byte ; copy literals - jp pe, dzx0r_literals_loop - add a, a ; copy from last offset or new offset? - jr c, dzx0r_new_offset - call dzx0r_elias ; obtain length -dzx0r_copy: - ex (sp), hl ; preserve source, restore offset - push hl ; preserve offset - add hl, de ; calculate destination - offset -dzx0r_copy_loop: - push hl ; copy from offset - ex de, hl - call dzx0r_convert - ex de, hl - call dzx0r_copy_byte - pop hl - inc hl - jp pe, dzx0r_copy_loop - pop hl ; restore offset - ex (sp), hl ; preserve offset, restore source - add a, a ; copy from literals or new offset? - jr nc, dzx0r_literals -dzx0r_new_offset: - pop bc ; discard last offset - ld c, $fe ; prepare negative offset - call dzx0r_elias_loop ; obtain offset MSB - inc c - ret z ; check end marker - ld b, c - ld c, (hl) ; obtain offset LSB - inc hl - rr b ; last offset bit becomes first length bit - rr c - push bc ; preserve new offset - ld bc, 1 ; obtain length - call nc, dzx0r_elias_backtrack - inc bc - jr dzx0r_copy -dzx0r_elias: - inc c ; interlaced Elias gamma coding -dzx0r_elias_loop: - add a, a - jr nz, dzx0r_elias_skip - ld a, (hl) ; load another group of 8 bits - inc hl - rla -dzx0r_elias_skip: - ret c -dzx0r_elias_backtrack: - add a, a - rl c - rl b - jr dzx0r_elias_loop -dzx0r_copy_byte: - push de ; preserve destination - call dzx0r_convert ; convert destination - ldi ; copy byte - pop de ; restore destination - inc de ; update destination - ret -; Convert an RCS address 010RRccc ccrrrppp to screen address 010RRppp rrrccccc -dzx0r_convert: - ex af, af' - ld a, d ; A = 010RRccc - cp $58 - jr nc, dzx0r_skip - xor e - and $f8 - xor e ; A = 010RRppp - push af - xor d - xor e ; A = ccrrrccc - rlca - rlca ; A = rrrccccc - pop de ; D = 010RRppp - ld e, a ; E = rrrccccc -dzx0r_skip: - ex af, af' - ret -; ----------------------------------------------------------------------------- - end asm -end sub - - -' ----------------------------------------------------------------------------- -' Decompress (from source to destination address) data that was previously -' compressed backwards using ZX0. This is the smallest version of the -' integrated RCS+ZX0 decompressor. -' -' IMPORTANT: Data decompressed directly to the ZX-Spectrum screen must be both -' RCS encoded and ZX0 compressed, everything else must be ZX0 compressed only. -' -' Parameters: -' src: last source address (compressed data) -' dst: last destination address (decompressing) -' ----------------------------------------------------------------------------- -sub FASTCALL dzx0SmartRCSBack(src as UINTEGER, dst as UINTEGER) - asm - pop bc ; RET address - pop de ; DE=dst - push bc ; restore RET address -; ----------------------------------------------------------------------------- -; "Smart" integrated RCS+ZX0 decoder by Einar Saukas (113 bytes) - BACKWARDS -; ----------------------------------------------------------------------------- -; Parameters: -; HL: last source address (compressed data) -; DE: last destination address (decompressing) -; ----------------------------------------------------------------------------- -dzx0_smartrcs_back: - ld bc, 1 ; preserve default offset 1 - push bc - ld a, $80 -dzx0rb_literals: - call dzx0rb_elias ; obtain length -dzx0rb_literals_loop: - call dzx0rb_copy_byte ; copy literals - jp pe, dzx0rb_literals_loop - inc c - add a, a ; copy from last offset or new offset? - jr c, dzx0rb_new_offset - call dzx0rb_elias ; obtain length -dzx0rb_copy: - ex (sp), hl ; preserve source, restore offset - push hl ; preserve offset - add hl, de ; calculate destination - offset -dzx0rb_copy_loop: - push hl ; copy from offset - ex de, hl - call dzx0rb_convert - ex de, hl - call dzx0rb_copy_byte - pop hl - dec hl - jp pe, dzx0rb_copy_loop - inc c - pop hl ; restore offset - ex (sp), hl ; preserve offset, restore source - add a, a ; copy from literals or new offset? - jr nc, dzx0rb_literals -dzx0rb_new_offset: - inc sp ; discard last offset - inc sp - call dzx0rb_elias ; obtain offset MSB - dec b - ret z ; check end marker - dec c ; adjust for positive offset - ld b, c - ld c, (hl) ; obtain offset LSB - dec hl - srl b ; last offset bit becomes first length bit - rr c - inc bc - push bc ; preserve new offset - ld bc, 1 ; obtain length - call c, dzx0rb_elias_backtrack - inc bc - jr dzx0rb_copy -dzx0rb_elias_backtrack: - add a, a - rl c - rl b -dzx0rb_elias: - add a, a ; inverted interlaced Elias gamma coding - jr nz, dzx0rb_elias_skip - ld a, (hl) ; load another group of 8 bits - dec hl - rla -dzx0rb_elias_skip: - jr c, dzx0rb_elias_backtrack - ret -dzx0rb_copy_byte: - push de ; preserve destination - call dzx0rb_convert ; convert destination - ldd ; copy byte - pop de ; restore destination - dec de ; update destination - ret -; Convert an RCS address 010RRccc ccrrrppp to screen address 010RRppp rrrccccc -dzx0rb_convert: - ex af, af' - ld a, d ; A = 010RRccc - cp $58 - jr nc, dzx0rb_skip - xor e - and $f8 - xor e ; A = 010RRppp - push af - xor d - xor e ; A = ccrrrccc - rlca - rlca ; A = rrrccccc - pop de ; D = 010RRppp - ld e, a ; E = rrrccccc -dzx0rb_skip: - ex af, af' - ret -; ----------------------------------------------------------------------------- - end asm -end sub - - -' ----------------------------------------------------------------------------- -' Decompress (from source to destination address) data that was previously -' compressed using ZX0. This is the fastest version of the integrated RCS+ZX0 -' decompressor. -' -' IMPORTANT: Data decompressed directly to the ZX-Spectrum screen must be both -' RCS encoded and ZX0 compressed, everything else must be ZX0 compressed only. -' -' Parameters: -' src: source address (compressed data) -' dst: destination address (decompressing) -' ----------------------------------------------------------------------------- -sub FASTCALL dzx0AgileRCS(src as UINTEGER, dst as UINTEGER) - asm - pop bc ; RET address - pop de ; DE=dst - push bc ; restore RET address -; ----------------------------------------------------------------------------- -; "Agile" integrated RCS+ZX0 decoder by Einar Saukas (187 bytes) -; ----------------------------------------------------------------------------- -; Parameters: -; HL: source address (compressed data) -; DE: destination address (decompressing) -; ----------------------------------------------------------------------------- -dzx0_agilercs: - ld bc, $ffff ; preserve default offset 1 - ld (dzx0a_last_offset+1), bc - inc bc - ld a, $80 - jr dzx0a_literals -dzx0a_new_offset: - ld c, $fe ; prepare negative offset - add a, a - jp nz, dzx0a_new_offset_skip - ld a, (hl) ; load another group of 8 bits - inc hl - rla -dzx0a_new_offset_skip: - call nc, dzx0a_elias ; obtain offset MSB - inc c - ret z ; check end marker - ld b, c - ld c, (hl) ; obtain offset LSB - inc hl - rr b ; last offset bit becomes first length bit - rr c - ld (dzx0a_last_offset+1), bc ; preserve new offset - ld bc, 1 ; obtain length - call nc, dzx0a_elias - inc bc -dzx0a_copy: - push hl ; preserve source -dzx0a_last_offset: - ld hl, 0 ; restore offset - add hl, de ; calculate destination - offset - ex af, af' -dzx0a_copy_loop: - ld a, h ; copy from offset - cp $58 - jr nc, dzx0a_copy_ldir - push hl - ex de, hl - call dzx0a_convert - ex de, hl - push de - ld a, d - cp $58 - call c, dzx0a_convert - ldi - pop de - inc de - pop hl - inc hl - jp pe, dzx0a_copy_loop - db $ea ; skip next instruction -dzx0a_copy_ldir: - ldir - ex af, af' - pop hl ; restore source - add a, a ; copy from literals or new offset? - jr c, dzx0a_new_offset -dzx0a_literals: - inc c ; obtain length - add a, a - jp nz, dzx0a_literals_skip - ld a, (hl) ; load another group of 8 bits - inc hl - rla -dzx0a_literals_skip: - call nc, dzx0a_elias - ex af, af' -dzx0a_literals_loop: - ld a, d ; copy literals - cp $58 - jr nc, dzx0a_literals_ldir - push de - call dzx0a_convert - ldi - pop de - inc de - jp pe, dzx0a_literals_loop - db $ea ; skip next instruction -dzx0a_literals_ldir: - ldir - ex af, af' - add a, a ; copy from last offset or new offset? - jr c, dzx0a_new_offset - inc c ; obtain length - add a, a - jp nz, dzx0a_last_offset_skip - ld a, (hl) ; load another group of 8 bits - inc hl - rla -dzx0a_last_offset_skip: - call nc, dzx0a_elias - jp dzx0a_copy -dzx0a_elias: - add a, a ; interlaced Elias gamma coding - rl c - add a, a - jr nc, dzx0a_elias - ret nz - ld a, (hl) ; load another group of 8 bits - inc hl - rla - ret c - add a, a - rl c - add a, a - ret c - add a, a - rl c - add a, a - ret c - add a, a - rl c - add a, a - ret c -dzx0a_elias_loop: - add a, a - rl c - rl b - add a, a - jr nc, dzx0a_elias_loop - ret nz - ld a, (hl) ; load another group of 8 bits - inc hl - rla - jr nc, dzx0a_elias_loop - ret -; Convert an RCS address 010RRccc ccrrrppp to screen address 010RRppp rrrccccc -dzx0a_convert: ; A = 010RRccc - xor e - and $f8 - xor e ; A = 010RRppp - push af - xor d - xor e ; A = ccrrrccc - rlca - rlca ; A = rrrccccc - pop de ; D = 010RRppp - ld e, a ; E = rrrccccc - ret -; ----------------------------------------------------------------------------- - end asm -end sub - - -#pragma pop(case_insensitive) -#endif +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' ---------------------------------------------------------------- + +#include once [arch:zx48k] diff --git a/src/outfmt/__init__.py b/src/outfmt/__init__.py index 5cdf3c3f6..f8336dbeb 100644 --- a/src/outfmt/__init__.py +++ b/src/outfmt/__init__.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from .binary import BinaryEmitter from .codeemitter import CodeEmitter from .sna import SnaEmitter diff --git a/src/outfmt/binary.py b/src/outfmt/binary.py index 463521ded..fdd127d83 100644 --- a/src/outfmt/binary.py +++ b/src/outfmt/binary.py @@ -1,12 +1,9 @@ -# -------------------------------------------- -# KopyLeft (K) 2008 -# by Jose M. Rodriguez de la Rosa -# -# This program is licensed under the -# GNU Public License v.3.0 -# -# The code emission interface. -# -------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .codeemitter import CodeEmitter @@ -16,14 +13,14 @@ class BinaryEmitter(CodeEmitter): def emit( self, - output_filename, - program_name, - loader_bytes, - entry_point, - program_bytes, - aux_bin_blocks, - aux_headless_bin_blocks, + output_filename: str, + program_name: str, + loader_bytes: bytearray | None, + entry_point: int, + program_bytes: bytearray | bytes | list[int], + aux_bin_blocks: list[tuple[str, list[int]]], + aux_headless_bin_blocks: list[list[int]], ): - """Emits resulting binary file.""" + """Emits the resulting binary file.""" with open(output_filename, "wb") as f: f.write(bytearray(program_bytes)) diff --git a/src/outfmt/codeemitter.py b/src/outfmt/codeemitter.py index aa19ec687..3e1d304e6 100644 --- a/src/outfmt/codeemitter.py +++ b/src/outfmt/codeemitter.py @@ -1,12 +1,9 @@ -# -------------------------------------------- -# KopyLeft (K) 2008 -# by Jose M. Rodriguez de la Rosa -# -# This program is licensed under the -# GNU Public License v.3.0 -# -# The code emission interface. -# -------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from abc import ABC, abstractmethod @@ -19,10 +16,10 @@ def emit( self, output_filename: str, program_name: str, - loader_bytes: bytearray, - entry_point, - program_bytes, - aux_bin_blocks, - aux_headless_bin_blocks, - ): + loader_bytes: bytearray | None, + entry_point: int, + program_bytes: bytearray | bytes | list[int], + aux_bin_blocks: list[tuple[str, list[int]]], + aux_headless_bin_blocks: list[list[int]], + ) -> None: pass diff --git a/src/outfmt/gensnapshot.py b/src/outfmt/gensnapshot.py index ba1e5b605..12999e68a 100644 --- a/src/outfmt/gensnapshot.py +++ b/src/outfmt/gensnapshot.py @@ -1,22 +1,9 @@ -# 48K Snapshot generation module -# +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later # © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. -# See the file AUTHORS for copyright details. -# -# This file is part of Boriel BASIC Compiler. -# -# Boriel BASIC Compiler is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# Boriel BASIC Compiler 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 Affero General Public License -# for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Boriel BASIC Compiler. If not, see . +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- class GenSnapshot: @@ -41,10 +28,10 @@ def patchAddr(self, addr: int, data: bytes): def __init__( self, - loader_bytes, - clear_addr, - mc_addr, - mc_bytes, + loader_bytes: bytearray | None, + clear_addr: int, + mc_addr: int, + mc_bytes: bytearray, ): """ Creates a snapshot object ready to run a BASIC program as if RUN was just executed. @@ -82,9 +69,8 @@ def __init__( eilast: Whether the last instruction prevents an interrupt """ - self.A = self.A2 = self.B = self.B2 = self.C = self.C2 = self.D = self.D2 = self.E = self.E2 = self.H = ( - self.H2 - ) = self.L = self.L2 = self.F = self.F2 = self.R = self.IXL = self.IXH = 0 + self.A = self.A2 = self.B = self.B2 = self.C = self.C2 = self.D = self.D2 = self.E = self.E2 = self.H = 0 + self.H2 = self.L = self.L2 = self.F = self.F2 = self.R = self.IXL = self.IXH = 0 self.IYH = 0x5C self.IYL = 0x3A # 0x5C3A is the normal value of IY for ROM use diff --git a/src/outfmt/sna.py b/src/outfmt/sna.py index b934b79d0..1c26c1544 100644 --- a/src/outfmt/sna.py +++ b/src/outfmt/sna.py @@ -1,23 +1,9 @@ -# 48K .SNA format output module -# +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later # © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. -# See the file AUTHORS for copyright details. -# -# This file is part of Boriel BASIC Compiler. -# -# Boriel BASIC Compiler is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# Boriel BASIC Compiler 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 Affero General Public License -# for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Boriel BASIC Compiler. If not, see . - +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .codeemitter import CodeEmitter from .gensnapshot import GenSnapshot @@ -28,11 +14,11 @@ class SnaEmitter(CodeEmitter): def generate( self, - loader_bytes, - clear_addr, - entry_point, - program_bytes, - ): + loader_bytes: bytearray | None, + clear_addr: int, + entry_point: int, + program_bytes: bytearray, + ) -> bytearray: """ Format of .SNA file: @@ -104,17 +90,17 @@ def generate( def emit( self, - output_filename, - program_name, - loader_bytes, - entry_point, - program_bytes, - aux_bin_blocks, - aux_headless_bin_blocks, - ): + output_filename: str, + program_name: str, + loader_bytes: bytearray | None, + entry_point: int, + program_bytes: bytearray | bytes | list[int], + aux_bin_blocks: list[tuple[str, list[int]]], + aux_headless_bin_blocks: list[list[int]], + ) -> None: """Emit a .SNA file with the compiled bytes; ignores loader_bytes""" - sna_data = self.generate(None, entry_point - 1, entry_point, program_bytes) + sna_data = self.generate(None, entry_point - 1, entry_point, bytearray(program_bytes)) # Write output file with open(output_filename, "wb") as f: diff --git a/src/outfmt/tap.py b/src/outfmt/tap.py index bac9e39a2..e894a9ff3 100755 --- a/src/outfmt/tap.py +++ b/src/outfmt/tap.py @@ -1,15 +1,11 @@ #!/usr/bin/env python3 -# -------------------------------------------- -# KopyLeft (K) 2008 -# by Jose M. Rodriguez de la Rosa -# -# This program is licensed under the -# GNU Public License v.3.0 -# -# Simple .tap file stdlib -# Only supports standard headers by now. -# -------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .tzx import TZX diff --git a/src/outfmt/tzx.py b/src/outfmt/tzx.py index e2c3a4584..4be8c046f 100755 --- a/src/outfmt/tzx.py +++ b/src/outfmt/tzx.py @@ -1,16 +1,11 @@ #!/usr/bin/env python3 -# -------------------------------------------- -# KopyLeft (K) 2008 -# by Jose M. Rodriguez de la Rosa -# -# This program is licensed under the -# GNU Public License v.3.0 -# -# Simple .tzx file stdlib -# Only supports standard headers by now. -# -------------------------------------------- - +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .codeemitter import CodeEmitter @@ -37,7 +32,7 @@ class TZX(CodeEmitter): HEADER_TYPE_CODE = 3 def __init__(self): - """Initializes the object with standard header""" + """Initializes the object with a standard header""" self.output = bytearray(b"ZXTape!") self.out(0x1A) self.out([self.VERSION_MAJOR, self.VERSION_MINOR]) @@ -49,14 +44,14 @@ def LH(self, value): return [valueL, valueH] - def out(self, l): + def out(self, l: int | list[int]) -> None: """Adds a list of bytes to the output string""" if not isinstance(l, list): l = [l] self.output.extend([int(i) & 0xFF for i in l]) - def standard_block(self, _bytes): + def standard_block(self, _bytes: bytearray | bytes | list[int]) -> None: """Adds a standard block of bytes""" self.out(self.BLOCK_STANDARD) # Standard block ID self.out(self.LH(1000)) # 1000 ms standard pause @@ -69,7 +64,7 @@ def standard_block(self, _bytes): self.out(checksum) - def dump(self, fname): + def dump(self, fname: str) -> None: """Saves TZX file to fname""" with open(fname, "wb") as f: f.write(self.output) @@ -127,21 +122,22 @@ def save_program(self, title, bytes, line=32768): def emit( self, - output_filename, - program_name, - loader_bytes, - entry_point, - program_bytes, - aux_bin_blocks, - aux_headless_bin_blocks, - ): - """Emits resulting tape file.""" + output_filename: str, + program_name: str, + loader_bytes: bytearray | None, + entry_point: int, + program_bytes: bytearray | bytes | list[int], + aux_bin_blocks: list[tuple[str, list[int]]], + aux_headless_bin_blocks: list[list[int]], + ) -> None: + """Emits the resulting tape file.""" if loader_bytes is not None: self.save_program("loader", loader_bytes, line=1) # Put line 0 to protect against MERGE self.save_code(program_name, entry_point, program_bytes) for name, block in aux_bin_blocks: self.save_code(name, 0, block) + for block in aux_headless_bin_blocks: self.standard_block(block) diff --git a/src/outfmt/z80.py b/src/outfmt/z80.py index 2cb44ae46..2cb6c7db5 100644 --- a/src/outfmt/z80.py +++ b/src/outfmt/z80.py @@ -1,23 +1,9 @@ -# 48K .Z80 format output module -# +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later # © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. -# See the file AUTHORS for copyright details. -# -# This file is part of Boriel BASIC Compiler. -# -# Boriel BASIC Compiler is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# Boriel BASIC Compiler 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 Affero General Public License -# for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Boriel BASIC Compiler. If not, see . - +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import io @@ -182,13 +168,13 @@ def generate( def emit( self, - output_filename, - program_name, - loader_bytes, - entry_point, - program_bytes, - aux_bin_blocks, - aux_headless_bin_blocks, + output_filename: str, + program_name: str, + loader_bytes: bytearray | None, + entry_point: int, + program_bytes: bytearray | bytes | list[int], + aux_bin_blocks: list[tuple[str, list[int]]], + aux_headless_bin_blocks: list[list[int]], ): """Save a .Z80 file with the compiled bytes; ignores loader_bytes""" diff --git a/src/parsetab/tabs.dbm.bak b/src/parsetab/tabs.dbm.bak deleted file mode 100644 index 9357bb728..000000000 --- a/src/parsetab/tabs.dbm.bak +++ /dev/null @@ -1,4 +0,0 @@ -'zxbpp', (0, 67003) -'asmparse', (67072, 233956) -'zxnext_asmparse', (301056, 259034) -'zxbparser', (560128, 641214) diff --git a/src/parsetab/tabs.dbm.dat b/src/parsetab/tabs.dbm.dat deleted file mode 100644 index 3dd407408..000000000 Binary files a/src/parsetab/tabs.dbm.dat and /dev/null differ diff --git a/src/parsetab/tabs.dbm.dir b/src/parsetab/tabs.dbm.dir deleted file mode 100644 index 9357bb728..000000000 --- a/src/parsetab/tabs.dbm.dir +++ /dev/null @@ -1,4 +0,0 @@ -'zxbpp', (0, 67003) -'asmparse', (67072, 233956) -'zxnext_asmparse', (301056, 259034) -'zxbparser', (560128, 641214) diff --git a/src/ply/__init__.py b/src/ply/__init__.py deleted file mode 100644 index 45f28c5b5..000000000 --- a/src/ply/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# PLY package -# Author: David Beazley (dave@dabeaz.com) -# https://github.com/dabeaz/ply - -__version__ = '2022.10.27' diff --git a/src/ply/lex.py b/src/ply/lex.py deleted file mode 100644 index de011fe13..000000000 --- a/src/ply/lex.py +++ /dev/null @@ -1,901 +0,0 @@ -# ----------------------------------------------------------------------------- -# ply: lex.py -# -# Copyright (C) 2001-2022 -# David M. Beazley (Dabeaz LLC) -# All rights reserved. -# -# Latest version: https://github.com/dabeaz/ply -# -# 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. -# * Neither the name of David Beazley or Dabeaz LLC may be used to -# endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# 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. -# ----------------------------------------------------------------------------- - -import re -import sys -import types -import copy -import os -import inspect - -# This tuple contains acceptable string types -StringTypes = (str, bytes) - -# This regular expression is used to match valid token names -_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$') - -# Exception thrown when invalid token encountered and no default error -# handler is defined. -class LexError(Exception): - def __init__(self, message, s): - self.args = (message,) - self.text = s - -# Token class. This class is used to represent the tokens produced. -class LexToken(object): - def __repr__(self): - return f'LexToken({self.type},{self.value!r},{self.lineno},{self.lexpos})' - -# This object is a stand-in for a logging object created by the -# logging module. - -class PlyLogger(object): - def __init__(self, f): - self.f = f - - def critical(self, msg, *args, **kwargs): - self.f.write((msg % args) + '\n') - - def warning(self, msg, *args, **kwargs): - self.f.write('WARNING: ' + (msg % args) + '\n') - - def error(self, msg, *args, **kwargs): - self.f.write('ERROR: ' + (msg % args) + '\n') - - info = critical - debug = critical - -# ----------------------------------------------------------------------------- -# === Lexing Engine === -# -# The following Lexer class implements the lexer runtime. There are only -# a few public methods and attributes: -# -# input() - Store a new string in the lexer -# token() - Get the next token -# clone() - Clone the lexer -# -# lineno - Current line number -# lexpos - Current position in the input string -# ----------------------------------------------------------------------------- - -class Lexer: - def __init__(self): - self.lexre = None # Master regular expression. This is a list of - # tuples (re, findex) where re is a compiled - # regular expression and findex is a list - # mapping regex group numbers to rules - self.lexretext = None # Current regular expression strings - self.lexstatere = {} # Dictionary mapping lexer states to master regexs - self.lexstateretext = {} # Dictionary mapping lexer states to regex strings - self.lexstaterenames = {} # Dictionary mapping lexer states to symbol names - self.lexstate = 'INITIAL' # Current lexer state - self.lexstatestack = [] # Stack of lexer states - self.lexstateinfo = None # State information - self.lexstateignore = {} # Dictionary of ignored characters for each state - self.lexstateerrorf = {} # Dictionary of error functions for each state - self.lexstateeoff = {} # Dictionary of eof functions for each state - self.lexreflags = 0 # Optional re compile flags - self.lexdata = None # Actual input data (as a string) - self.lexpos = 0 # Current position in input text - self.lexlen = 0 # Length of the input text - self.lexerrorf = None # Error rule (if any) - self.lexeoff = None # EOF rule (if any) - self.lextokens = None # List of valid tokens - self.lexignore = '' # Ignored characters - self.lexliterals = '' # Literal characters that can be passed through - self.lexmodule = None # Module - self.lineno = 1 # Current line number - - def clone(self, object=None): - c = copy.copy(self) - - # If the object parameter has been supplied, it means we are attaching the - # lexer to a new object. In this case, we have to rebind all methods in - # the lexstatere and lexstateerrorf tables. - - if object: - newtab = {} - for key, ritem in self.lexstatere.items(): - newre = [] - for cre, findex in ritem: - newfindex = [] - for f in findex: - if not f or not f[0]: - newfindex.append(f) - continue - newfindex.append((getattr(object, f[0].__name__), f[1])) - newre.append((cre, newfindex)) - newtab[key] = newre - c.lexstatere = newtab - c.lexstateerrorf = {} - for key, ef in self.lexstateerrorf.items(): - c.lexstateerrorf[key] = getattr(object, ef.__name__) - c.lexmodule = object - return c - - # ------------------------------------------------------------ - # input() - Push a new string into the lexer - # ------------------------------------------------------------ - def input(self, s): - self.lexdata = s - self.lexpos = 0 - self.lexlen = len(s) - - # ------------------------------------------------------------ - # begin() - Changes the lexing state - # ------------------------------------------------------------ - def begin(self, state): - if state not in self.lexstatere: - raise ValueError(f'Undefined state {state!r}') - self.lexre = self.lexstatere[state] - self.lexretext = self.lexstateretext[state] - self.lexignore = self.lexstateignore.get(state, '') - self.lexerrorf = self.lexstateerrorf.get(state, None) - self.lexeoff = self.lexstateeoff.get(state, None) - self.lexstate = state - - # ------------------------------------------------------------ - # push_state() - Changes the lexing state and saves old on stack - # ------------------------------------------------------------ - def push_state(self, state): - self.lexstatestack.append(self.lexstate) - self.begin(state) - - # ------------------------------------------------------------ - # pop_state() - Restores the previous state - # ------------------------------------------------------------ - def pop_state(self): - self.begin(self.lexstatestack.pop()) - - # ------------------------------------------------------------ - # current_state() - Returns the current lexing state - # ------------------------------------------------------------ - def current_state(self): - return self.lexstate - - # ------------------------------------------------------------ - # skip() - Skip ahead n characters - # ------------------------------------------------------------ - def skip(self, n): - self.lexpos += n - - # ------------------------------------------------------------ - # token() - Return the next token from the Lexer - # - # Note: This function has been carefully implemented to be as fast - # as possible. Don't make changes unless you really know what - # you are doing - # ------------------------------------------------------------ - def token(self): - # Make local copies of frequently referenced attributes - lexpos = self.lexpos - lexlen = self.lexlen - lexignore = self.lexignore - lexdata = self.lexdata - - while lexpos < lexlen: - # This code provides some short-circuit code for whitespace, tabs, and other ignored characters - if lexdata[lexpos] in lexignore: - lexpos += 1 - continue - - # Look for a regular expression match - for lexre, lexindexfunc in self.lexre: - m = lexre.match(lexdata, lexpos) - if not m: - continue - - # Create a token for return - tok = LexToken() - tok.value = m.group() - tok.lineno = self.lineno - tok.lexpos = lexpos - - i = m.lastindex - func, tok.type = lexindexfunc[i] - - if not func: - # If no token type was set, it's an ignored token - if tok.type: - self.lexpos = m.end() - return tok - else: - lexpos = m.end() - break - - lexpos = m.end() - - # If token is processed by a function, call it - - tok.lexer = self # Set additional attributes useful in token rules - self.lexmatch = m - self.lexpos = lexpos - newtok = func(tok) - del tok.lexer - del self.lexmatch - - # Every function must return a token, if nothing, we just move to next token - if not newtok: - lexpos = self.lexpos # This is here in case user has updated lexpos. - lexignore = self.lexignore # This is here in case there was a state change - break - return newtok - else: - # No match, see if in literals - if lexdata[lexpos] in self.lexliterals: - tok = LexToken() - tok.value = lexdata[lexpos] - tok.lineno = self.lineno - tok.type = tok.value - tok.lexpos = lexpos - self.lexpos = lexpos + 1 - return tok - - # No match. Call t_error() if defined. - if self.lexerrorf: - tok = LexToken() - tok.value = self.lexdata[lexpos:] - tok.lineno = self.lineno - tok.type = 'error' - tok.lexer = self - tok.lexpos = lexpos - self.lexpos = lexpos - newtok = self.lexerrorf(tok) - if lexpos == self.lexpos: - # Error method didn't change text position at all. This is an error. - raise LexError(f"Scanning error. Illegal character {lexdata[lexpos]!r}", - lexdata[lexpos:]) - lexpos = self.lexpos - if not newtok: - continue - return newtok - - self.lexpos = lexpos - raise LexError(f"Illegal character {lexdata[lexpos]!r} at index {lexpos}", - lexdata[lexpos:]) - - if self.lexeoff: - tok = LexToken() - tok.type = 'eof' - tok.value = '' - tok.lineno = self.lineno - tok.lexpos = lexpos - tok.lexer = self - self.lexpos = lexpos - newtok = self.lexeoff(tok) - return newtok - - self.lexpos = lexpos + 1 - if self.lexdata is None: - raise RuntimeError('No input string given with input()') - return None - - # Iterator interface - def __iter__(self): - return self - - def __next__(self): - t = self.token() - if t is None: - raise StopIteration - return t - -# ----------------------------------------------------------------------------- -# ==== Lex Builder === -# -# The functions and classes below are used to collect lexing information -# and build a Lexer object from it. -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# _get_regex(func) -# -# Returns the regular expression assigned to a function either as a doc string -# or as a .regex attribute attached by the @TOKEN decorator. -# ----------------------------------------------------------------------------- -def _get_regex(func): - return getattr(func, 'regex', func.__doc__) - -# ----------------------------------------------------------------------------- -# get_caller_module_dict() -# -# This function returns a dictionary containing all of the symbols defined within -# a caller further down the call stack. This is used to get the environment -# associated with the yacc() call if none was provided. -# ----------------------------------------------------------------------------- -def get_caller_module_dict(levels): - f = sys._getframe(levels) - return { **f.f_globals, **f.f_locals } - -# ----------------------------------------------------------------------------- -# _form_master_re() -# -# This function takes a list of all of the regex components and attempts to -# form the master regular expression. Given limitations in the Python re -# module, it may be necessary to break the master regex into separate expressions. -# ----------------------------------------------------------------------------- -def _form_master_re(relist, reflags, ldict, toknames): - if not relist: - return [], [], [] - regex = '|'.join(relist) - try: - lexre = re.compile(regex, reflags) - - # Build the index to function map for the matching engine - lexindexfunc = [None] * (max(lexre.groupindex.values()) + 1) - lexindexnames = lexindexfunc[:] - - for f, i in lexre.groupindex.items(): - handle = ldict.get(f, None) - if type(handle) in (types.FunctionType, types.MethodType): - lexindexfunc[i] = (handle, toknames[f]) - lexindexnames[i] = f - elif handle is not None: - lexindexnames[i] = f - if f.find('ignore_') > 0: - lexindexfunc[i] = (None, None) - else: - lexindexfunc[i] = (None, toknames[f]) - - return [(lexre, lexindexfunc)], [regex], [lexindexnames] - except Exception: - m = (len(relist) // 2) + 1 - llist, lre, lnames = _form_master_re(relist[:m], reflags, ldict, toknames) - rlist, rre, rnames = _form_master_re(relist[m:], reflags, ldict, toknames) - return (llist+rlist), (lre+rre), (lnames+rnames) - -# ----------------------------------------------------------------------------- -# def _statetoken(s,names) -# -# Given a declaration name s of the form "t_" and a dictionary whose keys are -# state names, this function returns a tuple (states,tokenname) where states -# is a tuple of state names and tokenname is the name of the token. For example, -# calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM') -# ----------------------------------------------------------------------------- -def _statetoken(s, names): - parts = s.split('_') - for i, part in enumerate(parts[1:], 1): - if part not in names and part != 'ANY': - break - - if i > 1: - states = tuple(parts[1:i]) - else: - states = ('INITIAL',) - - if 'ANY' in states: - states = tuple(names) - - tokenname = '_'.join(parts[i:]) - return (states, tokenname) - - -# ----------------------------------------------------------------------------- -# LexerReflect() -# -# This class represents information needed to build a lexer as extracted from a -# user's input file. -# ----------------------------------------------------------------------------- -class LexerReflect(object): - def __init__(self, ldict, log=None, reflags=0): - self.ldict = ldict - self.error_func = None - self.tokens = [] - self.reflags = reflags - self.stateinfo = {'INITIAL': 'inclusive'} - self.modules = set() - self.error = False - self.log = PlyLogger(sys.stderr) if log is None else log - - # Get all of the basic information - def get_all(self): - self.get_tokens() - self.get_literals() - self.get_states() - self.get_rules() - - # Validate all of the information - def validate_all(self): - self.validate_tokens() - self.validate_literals() - self.validate_rules() - return self.error - - # Get the tokens map - def get_tokens(self): - tokens = self.ldict.get('tokens', None) - if not tokens: - self.log.error('No token list is defined') - self.error = True - return - - if not isinstance(tokens, (list, tuple)): - self.log.error('tokens must be a list or tuple') - self.error = True - return - - if not tokens: - self.log.error('tokens is empty') - self.error = True - return - - self.tokens = tokens - - # Validate the tokens - def validate_tokens(self): - terminals = {} - for n in self.tokens: - if not _is_identifier.match(n): - self.log.error(f"Bad token name {n!r}") - self.error = True - if n in terminals: - self.log.warning(f"Token {n!r} multiply defined") - terminals[n] = 1 - - # Get the literals specifier - def get_literals(self): - self.literals = self.ldict.get('literals', '') - if not self.literals: - self.literals = '' - - # Validate literals - def validate_literals(self): - try: - for c in self.literals: - if not isinstance(c, StringTypes) or len(c) > 1: - self.log.error(f'Invalid literal {c!r}. Must be a single character') - self.error = True - - except TypeError: - self.log.error('Invalid literals specification. literals must be a sequence of characters') - self.error = True - - def get_states(self): - self.states = self.ldict.get('states', None) - # Build statemap - if self.states: - if not isinstance(self.states, (tuple, list)): - self.log.error('states must be defined as a tuple or list') - self.error = True - else: - for s in self.states: - if not isinstance(s, tuple) or len(s) != 2: - self.log.error("Invalid state specifier %r. Must be a tuple (statename,'exclusive|inclusive')", s) - self.error = True - continue - name, statetype = s - if not isinstance(name, StringTypes): - self.log.error('State name %r must be a string', name) - self.error = True - continue - if not (statetype == 'inclusive' or statetype == 'exclusive'): - self.log.error("State type for state %r must be 'inclusive' or 'exclusive'", name) - self.error = True - continue - if name in self.stateinfo: - self.log.error("State %r already defined", name) - self.error = True - continue - self.stateinfo[name] = statetype - - # Get all of the symbols with a t_ prefix and sort them into various - # categories (functions, strings, error functions, and ignore characters) - - def get_rules(self): - tsymbols = [f for f in self.ldict if f[:2] == 't_'] - - # Now build up a list of functions and a list of strings - self.toknames = {} # Mapping of symbols to token names - self.funcsym = {} # Symbols defined as functions - self.strsym = {} # Symbols defined as strings - self.ignore = {} # Ignore strings by state - self.errorf = {} # Error functions by state - self.eoff = {} # EOF functions by state - - for s in self.stateinfo: - self.funcsym[s] = [] - self.strsym[s] = [] - - if len(tsymbols) == 0: - self.log.error('No rules of the form t_rulename are defined') - self.error = True - return - - for f in tsymbols: - t = self.ldict[f] - states, tokname = _statetoken(f, self.stateinfo) - self.toknames[f] = tokname - - if hasattr(t, '__call__'): - if tokname == 'error': - for s in states: - self.errorf[s] = t - elif tokname == 'eof': - for s in states: - self.eoff[s] = t - elif tokname == 'ignore': - line = t.__code__.co_firstlineno - file = t.__code__.co_filename - self.log.error("%s:%d: Rule %r must be defined as a string", file, line, t.__name__) - self.error = True - else: - for s in states: - self.funcsym[s].append((f, t)) - elif isinstance(t, StringTypes): - if tokname == 'ignore': - for s in states: - self.ignore[s] = t - if '\\' in t: - self.log.warning("%s contains a literal backslash '\\'", f) - - elif tokname == 'error': - self.log.error("Rule %r must be defined as a function", f) - self.error = True - else: - for s in states: - self.strsym[s].append((f, t)) - else: - self.log.error('%s not defined as a function or string', f) - self.error = True - - # Sort the functions by line number - for f in self.funcsym.values(): - f.sort(key=lambda x: x[1].__code__.co_firstlineno) - - # Sort the strings by regular expression length - for s in self.strsym.values(): - s.sort(key=lambda x: len(x[1]), reverse=True) - - # Validate all of the t_rules collected - def validate_rules(self): - for state in self.stateinfo: - # Validate all rules defined by functions - - for fname, f in self.funcsym[state]: - line = f.__code__.co_firstlineno - file = f.__code__.co_filename - module = inspect.getmodule(f) - self.modules.add(module) - - tokname = self.toknames[fname] - if isinstance(f, types.MethodType): - reqargs = 2 - else: - reqargs = 1 - nargs = f.__code__.co_argcount - if nargs > reqargs: - self.log.error("%s:%d: Rule %r has too many arguments", file, line, f.__name__) - self.error = True - continue - - if nargs < reqargs: - self.log.error("%s:%d: Rule %r requires an argument", file, line, f.__name__) - self.error = True - continue - - if not _get_regex(f): - self.log.error("%s:%d: No regular expression defined for rule %r", file, line, f.__name__) - self.error = True - continue - - try: - c = re.compile('(?P<%s>%s)' % (fname, _get_regex(f)), self.reflags) - if c.match(''): - self.log.error("%s:%d: Regular expression for rule %r matches empty string", file, line, f.__name__) - self.error = True - except re.error as e: - self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file, line, f.__name__, e) - if '#' in _get_regex(f): - self.log.error("%s:%d. Make sure '#' in rule %r is escaped with '\\#'", file, line, f.__name__) - self.error = True - - # Validate all rules defined by strings - for name, r in self.strsym[state]: - tokname = self.toknames[name] - if tokname == 'error': - self.log.error("Rule %r must be defined as a function", name) - self.error = True - continue - - if tokname not in self.tokens and tokname.find('ignore_') < 0: - self.log.error("Rule %r defined for an unspecified token %s", name, tokname) - self.error = True - continue - - try: - c = re.compile('(?P<%s>%s)' % (name, r), self.reflags) - if (c.match('')): - self.log.error("Regular expression for rule %r matches empty string", name) - self.error = True - except re.error as e: - self.log.error("Invalid regular expression for rule %r. %s", name, e) - if '#' in r: - self.log.error("Make sure '#' in rule %r is escaped with '\\#'", name) - self.error = True - - if not self.funcsym[state] and not self.strsym[state]: - self.log.error("No rules defined for state %r", state) - self.error = True - - # Validate the error function - efunc = self.errorf.get(state, None) - if efunc: - f = efunc - line = f.__code__.co_firstlineno - file = f.__code__.co_filename - module = inspect.getmodule(f) - self.modules.add(module) - - if isinstance(f, types.MethodType): - reqargs = 2 - else: - reqargs = 1 - nargs = f.__code__.co_argcount - if nargs > reqargs: - self.log.error("%s:%d: Rule %r has too many arguments", file, line, f.__name__) - self.error = True - - if nargs < reqargs: - self.log.error("%s:%d: Rule %r requires an argument", file, line, f.__name__) - self.error = True - - for module in self.modules: - self.validate_module(module) - - # ----------------------------------------------------------------------------- - # validate_module() - # - # This checks to see if there are duplicated t_rulename() functions or strings - # in the parser input file. This is done using a simple regular expression - # match on each line in the source code of the given module. - # ----------------------------------------------------------------------------- - - def validate_module(self, module): - try: - lines, linen = inspect.getsourcelines(module) - except IOError: - return - - fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(') - sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=') - - counthash = {} - linen += 1 - for line in lines: - m = fre.match(line) - if not m: - m = sre.match(line) - if m: - name = m.group(1) - prev = counthash.get(name) - if not prev: - counthash[name] = linen - else: - filename = inspect.getsourcefile(module) - self.log.error('%s:%d: Rule %s redefined. Previously defined on line %d', filename, linen, name, prev) - self.error = True - linen += 1 - -# ----------------------------------------------------------------------------- -# lex(module) -# -# Build all of the regular expression rules from definitions in the supplied module -# ----------------------------------------------------------------------------- -def lex(*, module=None, object=None, debug=False, - reflags=int(re.VERBOSE), debuglog=None, errorlog=None): - - global lexer - - ldict = None - stateinfo = {'INITIAL': 'inclusive'} - lexobj = Lexer() - global token, input - - if errorlog is None: - errorlog = PlyLogger(sys.stderr) - - if debug: - if debuglog is None: - debuglog = PlyLogger(sys.stderr) - - # Get the module dictionary used for the lexer - if object: - module = object - - # Get the module dictionary used for the parser - if module: - _items = [(k, getattr(module, k)) for k in dir(module)] - ldict = dict(_items) - # If no __file__ attribute is available, try to obtain it from the __module__ instead - if '__file__' not in ldict: - ldict['__file__'] = sys.modules[ldict['__module__']].__file__ - else: - ldict = get_caller_module_dict(2) - - # Collect parser information from the dictionary - linfo = LexerReflect(ldict, log=errorlog, reflags=reflags) - linfo.get_all() - if linfo.validate_all(): - raise SyntaxError("Can't build lexer") - - # Dump some basic debugging information - if debug: - debuglog.info('lex: tokens = %r', linfo.tokens) - debuglog.info('lex: literals = %r', linfo.literals) - debuglog.info('lex: states = %r', linfo.stateinfo) - - # Build a dictionary of valid token names - lexobj.lextokens = set() - for n in linfo.tokens: - lexobj.lextokens.add(n) - - # Get literals specification - if isinstance(linfo.literals, (list, tuple)): - lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals) - else: - lexobj.lexliterals = linfo.literals - - lexobj.lextokens_all = lexobj.lextokens | set(lexobj.lexliterals) - - # Get the stateinfo dictionary - stateinfo = linfo.stateinfo - - regexs = {} - # Build the master regular expressions - for state in stateinfo: - regex_list = [] - - # Add rules defined by functions first - for fname, f in linfo.funcsym[state]: - regex_list.append('(?P<%s>%s)' % (fname, _get_regex(f))) - if debug: - debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", fname, _get_regex(f), state) - - # Now add all of the simple rules - for name, r in linfo.strsym[state]: - regex_list.append('(?P<%s>%s)' % (name, r)) - if debug: - debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", name, r, state) - - regexs[state] = regex_list - - # Build the master regular expressions - - if debug: - debuglog.info('lex: ==== MASTER REGEXS FOLLOW ====') - - for state in regexs: - lexre, re_text, re_names = _form_master_re(regexs[state], reflags, ldict, linfo.toknames) - lexobj.lexstatere[state] = lexre - lexobj.lexstateretext[state] = re_text - lexobj.lexstaterenames[state] = re_names - if debug: - for i, text in enumerate(re_text): - debuglog.info("lex: state '%s' : regex[%d] = '%s'", state, i, text) - - # For inclusive states, we need to add the regular expressions from the INITIAL state - for state, stype in stateinfo.items(): - if state != 'INITIAL' and stype == 'inclusive': - lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL']) - lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL']) - lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL']) - - lexobj.lexstateinfo = stateinfo - lexobj.lexre = lexobj.lexstatere['INITIAL'] - lexobj.lexretext = lexobj.lexstateretext['INITIAL'] - lexobj.lexreflags = reflags - - # Set up ignore variables - lexobj.lexstateignore = linfo.ignore - lexobj.lexignore = lexobj.lexstateignore.get('INITIAL', '') - - # Set up error functions - lexobj.lexstateerrorf = linfo.errorf - lexobj.lexerrorf = linfo.errorf.get('INITIAL', None) - if not lexobj.lexerrorf: - errorlog.warning('No t_error rule is defined') - - # Set up eof functions - lexobj.lexstateeoff = linfo.eoff - lexobj.lexeoff = linfo.eoff.get('INITIAL', None) - - # Check state information for ignore and error rules - for s, stype in stateinfo.items(): - if stype == 'exclusive': - if s not in linfo.errorf: - errorlog.warning("No error rule is defined for exclusive state %r", s) - if s not in linfo.ignore and lexobj.lexignore: - errorlog.warning("No ignore rule is defined for exclusive state %r", s) - elif stype == 'inclusive': - if s not in linfo.errorf: - linfo.errorf[s] = linfo.errorf.get('INITIAL', None) - if s not in linfo.ignore: - linfo.ignore[s] = linfo.ignore.get('INITIAL', '') - - # Create global versions of the token() and input() functions - token = lexobj.token - input = lexobj.input - lexer = lexobj - - return lexobj - -# ----------------------------------------------------------------------------- -# runmain() -# -# This runs the lexer as a main program -# ----------------------------------------------------------------------------- - -def runmain(lexer=None, data=None): - if not data: - try: - filename = sys.argv[1] - with open(filename) as f: - data = f.read() - except IndexError: - sys.stdout.write('Reading from standard input (type EOF to end):\n') - data = sys.stdin.read() - - if lexer: - _input = lexer.input - else: - _input = input - _input(data) - if lexer: - _token = lexer.token - else: - _token = token - - while True: - tok = _token() - if not tok: - break - sys.stdout.write(f'({tok.type},{tok.value!r},{tok.lineno},{tok.lexpos})\n') - -# ----------------------------------------------------------------------------- -# @TOKEN(regex) -# -# This decorator function can be used to set the regex expression on a function -# when its docstring might need to be set in an alternative way -# ----------------------------------------------------------------------------- - -def TOKEN(r): - def set_regex(f): - if hasattr(r, '__call__'): - f.regex = _get_regex(r) - else: - f.regex = r - return f - return set_regex diff --git a/src/ply/yacc.py b/src/ply/yacc.py deleted file mode 100644 index 652879624..000000000 --- a/src/ply/yacc.py +++ /dev/null @@ -1,2482 +0,0 @@ -# ----------------------------------------------------------------------------- -# ply: yacc.py -# -# Copyright (C) 2001-2022 -# David M. Beazley (Dabeaz LLC) -# All rights reserved. -# -# Latest version: https://github.com/dabeaz/ply -# -# 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. -# * Neither the name of David Beazley or Dabeaz LLC may be used to -# endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# 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. -# ----------------------------------------------------------------------------- -# -# This implements an LR parser that is constructed from grammar rules defined -# as Python functions. The grammar is specified by supplying the BNF inside -# Python documentation strings. The inspiration for this technique was borrowed -# from John Aycock's Spark parsing system. PLY might be viewed as cross between -# Spark and the GNU bison utility. -# -# The current implementation is only somewhat object-oriented. The -# LR parser itself is defined in terms of an object (which allows multiple -# parsers to co-exist). However, most of the variables used during table -# construction are defined in terms of global variables. Users shouldn't -# notice unless they are trying to define multiple parsers at the same -# time using threads (in which case they should have their head examined). -# -# This implementation supports both SLR and LALR(1) parsing. LALR(1) -# support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu), -# using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles, -# Techniques, and Tools" (The Dragon Book). LALR(1) has since been replaced -# by the more efficient DeRemer and Pennello algorithm. -# -# :::::::: WARNING ::::::: -# -# Construction of LR parsing tables is fairly complicated and expensive. -# To make this module run fast, a *LOT* of work has been put into -# optimization---often at the expensive of readability and what might -# consider to be good Python "coding style." Modify the code at your -# own risk! -# ---------------------------------------------------------------------------- - -import re -import types -import sys -import inspect - -#----------------------------------------------------------------------------- -# === User configurable parameters === -# -# Change these to modify the default behavior of yacc (if you wish) -#----------------------------------------------------------------------------- - -yaccdebug = False # Debugging mode. If set, yacc generates a - # a 'parser.out' file in the current directory - -debug_file = 'parser.out' # Default name of the debugging file -error_count = 3 # Number of symbols that must be shifted to leave recovery mode -resultlimit = 40 # Size limit of results when running in debug mode. - -MAXINT = sys.maxsize - -# This object is a stand-in for a logging object created by the -# logging module. PLY will use this by default to create things -# such as the parser.out file. If a user wants more detailed -# information, they can create their own logging object and pass -# it into PLY. - -class PlyLogger(object): - def __init__(self, f): - self.f = f - - def debug(self, msg, *args, **kwargs): - self.f.write((msg % args) + '\n') - - info = debug - - def warning(self, msg, *args, **kwargs): - self.f.write('WARNING: ' + (msg % args) + '\n') - - def error(self, msg, *args, **kwargs): - self.f.write('ERROR: ' + (msg % args) + '\n') - - critical = debug - -# Null logger is used when no output is generated. Does nothing. -class NullLogger(object): - def __getattribute__(self, name): - return self - - def __call__(self, *args, **kwargs): - return self - -# Exception raised for yacc-related errors -class YaccError(Exception): - pass - -# Format the result message that the parser produces when running in debug mode. -def format_result(r): - repr_str = repr(r) - if '\n' in repr_str: - repr_str = repr(repr_str) - if len(repr_str) > resultlimit: - repr_str = repr_str[:resultlimit] + ' ...' - result = '<%s @ 0x%x> (%s)' % (type(r).__name__, id(r), repr_str) - return result - -# Format stack entries when the parser is running in debug mode -def format_stack_entry(r): - repr_str = repr(r) - if '\n' in repr_str: - repr_str = repr(repr_str) - if len(repr_str) < 16: - return repr_str - else: - return '<%s @ 0x%x>' % (type(r).__name__, id(r)) - -#----------------------------------------------------------------------------- -# === LR Parsing Engine === -# -# The following classes are used for the LR parser itself. These are not -# used during table construction and are independent of the actual LR -# table generation algorithm -#----------------------------------------------------------------------------- - -# This class is used to hold non-terminal grammar symbols during parsing. -# It normally has the following attributes set: -# .type = Grammar symbol type -# .value = Symbol value -# .lineno = Starting line number -# .endlineno = Ending line number (optional, set automatically) -# .lexpos = Starting lex position -# .endlexpos = Ending lex position (optional, set automatically) - -class YaccSymbol: - def __str__(self): - return self.type - - def __repr__(self): - return str(self) - -# This class is a wrapper around the objects actually passed to each -# grammar rule. Index lookup and assignment actually assign the -# .value attribute of the underlying YaccSymbol object. -# The lineno() method returns the line number of a given -# item (or 0 if not defined). The linespan() method returns -# a tuple of (startline,endline) representing the range of lines -# for a symbol. The lexspan() method returns a tuple (lexpos,endlexpos) -# representing the range of positional information for a symbol. - -class YaccProduction: - def __init__(self, s, stack=None): - self.slice = s - self.stack = stack - self.lexer = None - self.parser = None - - def __getitem__(self, n): - if isinstance(n, slice): - return [s.value for s in self.slice[n]] - elif n >= 0: - return self.slice[n].value - else: - return self.stack[n].value - - def __setitem__(self, n, v): - self.slice[n].value = v - - def __getslice__(self, i, j): - return [s.value for s in self.slice[i:j]] - - def __len__(self): - return len(self.slice) - - def lineno(self, n): - return getattr(self.slice[n], 'lineno', 0) - - def set_lineno(self, n, lineno): - self.slice[n].lineno = lineno - - def linespan(self, n): - startline = getattr(self.slice[n], 'lineno', 0) - endline = getattr(self.slice[n], 'endlineno', startline) - return startline, endline - - def lexpos(self, n): - return getattr(self.slice[n], 'lexpos', 0) - - def set_lexpos(self, n, lexpos): - self.slice[n].lexpos = lexpos - - def lexspan(self, n): - startpos = getattr(self.slice[n], 'lexpos', 0) - endpos = getattr(self.slice[n], 'endlexpos', startpos) - return startpos, endpos - - def error(self): - raise SyntaxError - -# ----------------------------------------------------------------------------- -# == LRParser == -# -# The LR Parsing engine. -# ----------------------------------------------------------------------------- - -class LRParser: - def __init__(self, lrtab, errorf): - self.productions = lrtab.lr_productions - self.action = lrtab.lr_action - self.goto = lrtab.lr_goto - self.errorfunc = errorf - self.set_defaulted_states() - self.errorok = True - - def errok(self): - self.errorok = True - - def restart(self): - del self.statestack[:] - del self.symstack[:] - sym = YaccSymbol() - sym.type = '$end' - self.symstack.append(sym) - self.statestack.append(0) - - # Defaulted state support. - # This method identifies parser states where there is only one possible reduction action. - # For such states, the parser can make a choose to make a rule reduction without consuming - # the next look-ahead token. This delayed invocation of the tokenizer can be useful in - # certain kinds of advanced parsing situations where the lexer and parser interact with - # each other or change states (i.e., manipulation of scope, lexer states, etc.). - # - # See: http://www.gnu.org/software/bison/manual/html_node/Default-Reductions.html#Default-Reductions - def set_defaulted_states(self): - self.defaulted_states = {} - for state, actions in self.action.items(): - rules = list(actions.values()) - if len(rules) == 1 and rules[0] < 0: - self.defaulted_states[state] = rules[0] - - def disable_defaulted_states(self): - self.defaulted_states = {} - - # parse(). - # - # This is the core parsing engine. To operate, it requires a lexer object. - # Two options are provided. The debug flag turns on debugging so that you can - # see the various rule reductions and parsing steps. tracking turns on position - # tracking. In this mode, symbols will record the starting/ending line number and - # character index. - - def parse(self, input=None, lexer=None, debug=False, tracking=False): - # If debugging has been specified as a flag, turn it into a logging object - if isinstance(debug, int) and debug: - debug = PlyLogger(sys.stderr) - - lookahead = None # Current lookahead symbol - lookaheadstack = [] # Stack of lookahead symbols - actions = self.action # Local reference to action table (to avoid lookup on self.) - goto = self.goto # Local reference to goto table (to avoid lookup on self.) - prod = self.productions # Local reference to production list (to avoid lookup on self.) - defaulted_states = self.defaulted_states # Local reference to defaulted states - pslice = YaccProduction(None) # Production object passed to grammar rules - errorcount = 0 # Used during error recovery - - if debug: - debug.info('PLY: PARSE DEBUG START') - - # If no lexer was given, we will try to use the lex module - if not lexer: - from . import lex - lexer = lex.lexer - - # Set up the lexer and parser objects on pslice - pslice.lexer = lexer - pslice.parser = self - - # If input was supplied, pass to lexer - if input is not None: - lexer.input(input) - - # Set the token function - get_token = self.token = lexer.token - - # Set up the state and symbol stacks - statestack = self.statestack = [] # Stack of parsing states - symstack = self.symstack = [] # Stack of grammar symbols - pslice.stack = symstack # Put in the production - errtoken = None # Err token - - # The start state is assumed to be (0,$end) - - statestack.append(0) - sym = YaccSymbol() - sym.type = '$end' - symstack.append(sym) - state = 0 - while True: - # Get the next symbol on the input. If a lookahead symbol - # is already set, we just use that. Otherwise, we'll pull - # the next token off of the lookaheadstack or from the lexer - - if debug: - debug.debug('State : %s', state) - - if state not in defaulted_states: - if not lookahead: - if not lookaheadstack: - lookahead = get_token() # Get the next token - else: - lookahead = lookaheadstack.pop() - if not lookahead: - lookahead = YaccSymbol() - lookahead.type = '$end' - - # Check the action table - ltype = lookahead.type - t = actions[state].get(ltype) - else: - t = defaulted_states[state] - if debug: - debug.debug('Defaulted state %s: Reduce using %d', state, -t) - - if debug: - debug.debug('Stack : %s', - ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) - - if t is not None: - if t > 0: - # shift a symbol on the stack - statestack.append(t) - state = t - - if debug: - debug.debug('Action : Shift and goto state %s', t) - - symstack.append(lookahead) - lookahead = None - - # Decrease error count on successful shift - if errorcount: - errorcount -= 1 - continue - - if t < 0: - # reduce a symbol on the stack, emit a production - p = prod[-t] - pname = p.name - plen = p.len - - # Get production function - sym = YaccSymbol() - sym.type = pname # Production name - sym.value = None - - if debug: - if plen: - debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str, - '['+','.join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+']', - goto[statestack[-1-plen]][pname]) - else: - debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str, [], - goto[statestack[-1]][pname]) - - if plen: - targ = symstack[-plen-1:] - targ[0] = sym - - if tracking: - t1 = targ[1] - sym.lineno = t1.lineno - sym.lexpos = t1.lexpos - t1 = targ[-1] - sym.endlineno = getattr(t1, 'endlineno', t1.lineno) - sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos) - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # The code enclosed in this section is duplicated - # below as a performance optimization. Make sure - # changes get made in both locations. - - pslice.slice = targ - - try: - # Call the grammar rule with our special slice object - del symstack[-plen:] - self.state = state - p.callable(pslice) - del statestack[-plen:] - if debug: - debug.info('Result : %s', format_result(pslice[0])) - symstack.append(sym) - state = goto[statestack[-1]][pname] - statestack.append(state) - except SyntaxError: - # If an error was set. Enter error recovery state - lookaheadstack.append(lookahead) # Save the current lookahead token - symstack.extend(targ[1:-1]) # Put the production slice back on the stack - statestack.pop() # Pop back one state (before the reduce) - state = statestack[-1] - sym.type = 'error' - sym.value = 'error' - lookahead = sym - errorcount = error_count - self.errorok = False - - continue - - else: - - if tracking: - sym.lineno = lexer.lineno - sym.lexpos = lexer.lexpos - - targ = [sym] - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # The code enclosed in this section is duplicated - # above as a performance optimization. Make sure - # changes get made in both locations. - - pslice.slice = targ - - try: - # Call the grammar rule with our special slice object - self.state = state - p.callable(pslice) - if debug: - debug.info('Result : %s', format_result(pslice[0])) - symstack.append(sym) - state = goto[statestack[-1]][pname] - statestack.append(state) - except SyntaxError: - # If an error was set. Enter error recovery state - lookaheadstack.append(lookahead) # Save the current lookahead token - statestack.pop() # Pop back one state (before the reduce) - state = statestack[-1] - sym.type = 'error' - sym.value = 'error' - lookahead = sym - errorcount = error_count - self.errorok = False - - continue - - if t == 0: - n = symstack[-1] - result = getattr(n, 'value', None) - - if debug: - debug.info('Done : Returning %s', format_result(result)) - debug.info('PLY: PARSE DEBUG END') - - return result - - if t is None: - - if debug: - debug.error('Error : %s', - ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) - - # We have some kind of parsing error here. To handle - # this, we are going to push the current token onto - # the tokenstack and replace it with an 'error' token. - # If there are any synchronization rules, they may - # catch it. - # - # In addition to pushing the error token, we call call - # the user defined p_error() function if this is the - # first syntax error. This function is only called if - # errorcount == 0. - if errorcount == 0 or self.errorok: - errorcount = error_count - self.errorok = False - errtoken = lookahead - if errtoken.type == '$end': - errtoken = None # End of file! - if self.errorfunc: - if errtoken and not hasattr(errtoken, 'lexer'): - errtoken.lexer = lexer - self.state = state - tok = self.errorfunc(errtoken) - if self.errorok: - # User must have done some kind of panic - # mode recovery on their own. The - # returned token is the next lookahead - lookahead = tok - errtoken = None - continue - else: - if errtoken: - if hasattr(errtoken, 'lineno'): - lineno = lookahead.lineno - else: - lineno = 0 - if lineno: - sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type)) - else: - sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type) - else: - sys.stderr.write('yacc: Parse error in input. EOF\n') - return - - else: - errorcount = error_count - - # case 1: the statestack only has 1 entry on it. If we're in this state, the - # entire parse has been rolled back and we're completely hosed. The token is - # discarded and we just keep going. - - if len(statestack) <= 1 and lookahead.type != '$end': - lookahead = None - errtoken = None - state = 0 - # Nuke the pushback stack - del lookaheadstack[:] - continue - - # case 2: the statestack has a couple of entries on it, but we're - # at the end of the file. nuke the top entry and generate an error token - - # Start nuking entries on the stack - if lookahead.type == '$end': - # Whoa. We're really hosed here. Bail out - return - - if lookahead.type != 'error': - sym = symstack[-1] - if sym.type == 'error': - # Hmmm. Error is on top of stack, we'll just nuke input - # symbol and continue - if tracking: - sym.endlineno = getattr(lookahead, 'lineno', sym.lineno) - sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos) - lookahead = None - continue - - # Create the error symbol for the first time and make it the new lookahead symbol - t = YaccSymbol() - t.type = 'error' - - if hasattr(lookahead, 'lineno'): - t.lineno = t.endlineno = lookahead.lineno - if hasattr(lookahead, 'lexpos'): - t.lexpos = t.endlexpos = lookahead.lexpos - t.value = lookahead - lookaheadstack.append(lookahead) - lookahead = t - else: - sym = symstack.pop() - if tracking: - lookahead.lineno = sym.lineno - lookahead.lexpos = sym.lexpos - statestack.pop() - state = statestack[-1] - - continue - - # If we'r here, something really bad happened - raise RuntimeError('yacc: internal parser error!!!\n') - -# ----------------------------------------------------------------------------- -# === Grammar Representation === -# -# The following functions, classes, and variables are used to represent and -# manipulate the rules that make up a grammar. -# ----------------------------------------------------------------------------- - -# regex matching identifiers -_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$') - -# ----------------------------------------------------------------------------- -# class Production: -# -# This class stores the raw information about a single production or grammar rule. -# A grammar rule refers to a specification such as this: -# -# expr : expr PLUS term -# -# Here are the basic attributes defined on all productions -# -# name - Name of the production. For example 'expr' -# prod - A list of symbols on the right side ['expr','PLUS','term'] -# prec - Production precedence level -# number - Production number. -# func - Function that executes on reduce -# file - File where production function is defined -# lineno - Line number where production function is defined -# -# The following attributes are defined or optional. -# -# len - Length of the production (number of symbols on right hand side) -# usyms - Set of unique symbols found in the production -# ----------------------------------------------------------------------------- - -class Production(object): - reduced = 0 - def __init__(self, number, name, prod, precedence=('right', 0), func=None, file='', line=0): - self.name = name - self.prod = tuple(prod) - self.number = number - self.func = func - self.callable = None - self.file = file - self.line = line - self.prec = precedence - - # Internal settings used during table construction - - self.len = len(self.prod) # Length of the production - - # Create a list of unique production symbols used in the production - self.usyms = [] - for s in self.prod: - if s not in self.usyms: - self.usyms.append(s) - - # List of all LR items for the production - self.lr_items = [] - self.lr_next = None - - # Create a string representation - if self.prod: - self.str = '%s -> %s' % (self.name, ' '.join(self.prod)) - else: - self.str = '%s -> ' % self.name - - def __str__(self): - return self.str - - def __repr__(self): - return 'Production(' + str(self) + ')' - - def __len__(self): - return len(self.prod) - - def __nonzero__(self): - return 1 - - def __getitem__(self, index): - return self.prod[index] - - # Return the nth lr_item from the production (or None if at the end) - def lr_item(self, n): - if n > len(self.prod): - return None - p = LRItem(self, n) - # Precompute the list of productions immediately following. - try: - p.lr_after = self.Prodnames[p.prod[n+1]] - except (IndexError, KeyError): - p.lr_after = [] - try: - p.lr_before = p.prod[n-1] - except IndexError: - p.lr_before = None - return p - - # Bind the production function name to a callable - def bind(self, pdict): - if self.func: - self.callable = pdict[self.func] - -# ----------------------------------------------------------------------------- -# class LRItem -# -# This class represents a specific stage of parsing a production rule. For -# example: -# -# expr : expr . PLUS term -# -# In the above, the "." represents the current location of the parse. Here -# basic attributes: -# -# name - Name of the production. For example 'expr' -# prod - A list of symbols on the right side ['expr','.', 'PLUS','term'] -# number - Production number. -# -# lr_next Next LR item. Example, if we are ' expr -> expr . PLUS term' -# then lr_next refers to 'expr -> expr PLUS . term' -# lr_index - LR item index (location of the ".") in the prod list. -# lookaheads - LALR lookahead symbols for this item -# len - Length of the production (number of symbols on right hand side) -# lr_after - List of all productions that immediately follow -# lr_before - Grammar symbol immediately before -# ----------------------------------------------------------------------------- - -class LRItem(object): - def __init__(self, p, n): - self.name = p.name - self.prod = list(p.prod) - self.number = p.number - self.lr_index = n - self.lookaheads = {} - self.prod.insert(n, '.') - self.prod = tuple(self.prod) - self.len = len(self.prod) - self.usyms = p.usyms - - def __str__(self): - if self.prod: - s = '%s -> %s' % (self.name, ' '.join(self.prod)) - else: - s = '%s -> ' % self.name - return s - - def __repr__(self): - return 'LRItem(' + str(self) + ')' - -# ----------------------------------------------------------------------------- -# rightmost_terminal() -# -# Return the rightmost terminal from a list of symbols. Used in add_production() -# ----------------------------------------------------------------------------- -def rightmost_terminal(symbols, terminals): - i = len(symbols) - 1 - while i >= 0: - if symbols[i] in terminals: - return symbols[i] - i -= 1 - return None - -# ----------------------------------------------------------------------------- -# === GRAMMAR CLASS === -# -# The following class represents the contents of the specified grammar along -# with various computed properties such as first sets, follow sets, LR items, etc. -# This data is used for critical parts of the table generation process later. -# ----------------------------------------------------------------------------- - -class GrammarError(YaccError): - pass - -class Grammar(object): - def __init__(self, terminals): - self.Productions = [None] # A list of all of the productions. The first - # entry is always reserved for the purpose of - # building an augmented grammar - - self.Prodnames = {} # A dictionary mapping the names of nonterminals to a list of all - # productions of that nonterminal. - - self.Prodmap = {} # A dictionary that is only used to detect duplicate - # productions. - - self.Terminals = {} # A dictionary mapping the names of terminal symbols to a - # list of the rules where they are used. - - for term in terminals: - self.Terminals[term] = [] - - self.Terminals['error'] = [] - - self.Nonterminals = {} # A dictionary mapping names of nonterminals to a list - # of rule numbers where they are used. - - self.First = {} # A dictionary of precomputed FIRST(x) symbols - - self.Follow = {} # A dictionary of precomputed FOLLOW(x) symbols - - self.Precedence = {} # Precedence rules for each terminal. Contains tuples of the - # form ('right',level) or ('nonassoc', level) or ('left',level) - - self.UsedPrecedence = set() # Precedence rules that were actually used by the grammer. - # This is only used to provide error checking and to generate - # a warning about unused precedence rules. - - self.Start = None # Starting symbol for the grammar - - - def __len__(self): - return len(self.Productions) - - def __getitem__(self, index): - return self.Productions[index] - - # ----------------------------------------------------------------------------- - # set_precedence() - # - # Sets the precedence for a given terminal. assoc is the associativity such as - # 'left','right', or 'nonassoc'. level is a numeric level. - # - # ----------------------------------------------------------------------------- - - def set_precedence(self, term, assoc, level): - assert self.Productions == [None], 'Must call set_precedence() before add_production()' - if term in self.Precedence: - raise GrammarError('Precedence already specified for terminal %r' % term) - if assoc not in ['left', 'right', 'nonassoc']: - raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'") - self.Precedence[term] = (assoc, level) - - # ----------------------------------------------------------------------------- - # add_production() - # - # Given an action function, this function assembles a production rule and - # computes its precedence level. - # - # The production rule is supplied as a list of symbols. For example, - # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and - # symbols ['expr','PLUS','term']. - # - # Precedence is determined by the precedence of the right-most non-terminal - # or the precedence of a terminal specified by %prec. - # - # A variety of error checks are performed to make sure production symbols - # are valid and that %prec is used correctly. - # ----------------------------------------------------------------------------- - - def add_production(self, prodname, syms, func=None, file='', line=0): - - if prodname in self.Terminals: - raise GrammarError('%s:%d: Illegal rule name %r. Already defined as a token' % (file, line, prodname)) - if prodname == 'error': - raise GrammarError('%s:%d: Illegal rule name %r. error is a reserved word' % (file, line, prodname)) - if not _is_identifier.match(prodname): - raise GrammarError('%s:%d: Illegal rule name %r' % (file, line, prodname)) - - # Look for literal tokens - for n, s in enumerate(syms): - if s[0] in "'\"": - try: - c = eval(s) - if (len(c) > 1): - raise GrammarError('%s:%d: Literal token %s in rule %r may only be a single character' % - (file, line, s, prodname)) - if c not in self.Terminals: - self.Terminals[c] = [] - syms[n] = c - continue - except SyntaxError: - pass - if not _is_identifier.match(s) and s != '%prec': - raise GrammarError('%s:%d: Illegal name %r in rule %r' % (file, line, s, prodname)) - - # Determine the precedence level - if '%prec' in syms: - if syms[-1] == '%prec': - raise GrammarError('%s:%d: Syntax error. Nothing follows %%prec' % (file, line)) - if syms[-2] != '%prec': - raise GrammarError('%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule' % - (file, line)) - precname = syms[-1] - prodprec = self.Precedence.get(precname) - if not prodprec: - raise GrammarError('%s:%d: Nothing known about the precedence of %r' % (file, line, precname)) - else: - self.UsedPrecedence.add(precname) - del syms[-2:] # Drop %prec from the rule - else: - # If no %prec, precedence is determined by the rightmost terminal symbol - precname = rightmost_terminal(syms, self.Terminals) - prodprec = self.Precedence.get(precname, ('right', 0)) - - # See if the rule is already in the rulemap - map = '%s -> %s' % (prodname, syms) - if map in self.Prodmap: - m = self.Prodmap[map] - raise GrammarError('%s:%d: Duplicate rule %s. ' % (file, line, m) + - 'Previous definition at %s:%d' % (m.file, m.line)) - - # From this point on, everything is valid. Create a new Production instance - pnumber = len(self.Productions) - if prodname not in self.Nonterminals: - self.Nonterminals[prodname] = [] - - # Add the production number to Terminals and Nonterminals - for t in syms: - if t in self.Terminals: - self.Terminals[t].append(pnumber) - else: - if t not in self.Nonterminals: - self.Nonterminals[t] = [] - self.Nonterminals[t].append(pnumber) - - # Create a production and add it to the list of productions - p = Production(pnumber, prodname, syms, prodprec, func, file, line) - self.Productions.append(p) - self.Prodmap[map] = p - - # Add to the global productions list - try: - self.Prodnames[prodname].append(p) - except KeyError: - self.Prodnames[prodname] = [p] - - # ----------------------------------------------------------------------------- - # set_start() - # - # Sets the starting symbol and creates the augmented grammar. Production - # rule 0 is S' -> start where start is the start symbol. - # ----------------------------------------------------------------------------- - - def set_start(self, start=None): - if not start: - start = self.Productions[1].name - if start not in self.Nonterminals: - raise GrammarError('start symbol %s undefined' % start) - self.Productions[0] = Production(0, "S'", [start]) - self.Nonterminals[start].append(0) - self.Start = start - - # ----------------------------------------------------------------------------- - # find_unreachable() - # - # Find all of the nonterminal symbols that can't be reached from the starting - # symbol. Returns a list of nonterminals that can't be reached. - # ----------------------------------------------------------------------------- - - def find_unreachable(self): - - # Mark all symbols that are reachable from a symbol s - def mark_reachable_from(s): - if s in reachable: - return - reachable.add(s) - for p in self.Prodnames.get(s, []): - for r in p.prod: - mark_reachable_from(r) - - reachable = set() - mark_reachable_from(self.Productions[0].prod[0]) - return [s for s in self.Nonterminals if s not in reachable] - - # ----------------------------------------------------------------------------- - # infinite_cycles() - # - # This function looks at the various parsing rules and tries to detect - # infinite recursion cycles (grammar rules where there is no possible way - # to derive a string of only terminals). - # ----------------------------------------------------------------------------- - - def infinite_cycles(self): - terminates = {} - - # Terminals: - for t in self.Terminals: - terminates[t] = True - - terminates['$end'] = True - - # Nonterminals: - - # Initialize to false: - for n in self.Nonterminals: - terminates[n] = False - - # Then propagate termination until no change: - while True: - some_change = False - for (n, pl) in self.Prodnames.items(): - # Nonterminal n terminates iff any of its productions terminates. - for p in pl: - # Production p terminates iff all of its rhs symbols terminate. - for s in p.prod: - if not terminates[s]: - # The symbol s does not terminate, - # so production p does not terminate. - p_terminates = False - break - else: - # didn't break from the loop, - # so every symbol s terminates - # so production p terminates. - p_terminates = True - - if p_terminates: - # symbol n terminates! - if not terminates[n]: - terminates[n] = True - some_change = True - # Don't need to consider any more productions for this n. - break - - if not some_change: - break - - infinite = [] - for (s, term) in terminates.items(): - if not term: - if s not in self.Prodnames and s not in self.Terminals and s != 'error': - # s is used-but-not-defined, and we've already warned of that, - # so it would be overkill to say that it's also non-terminating. - pass - else: - infinite.append(s) - - return infinite - - # ----------------------------------------------------------------------------- - # undefined_symbols() - # - # Find all symbols that were used the grammar, but not defined as tokens or - # grammar rules. Returns a list of tuples (sym, prod) where sym in the symbol - # and prod is the production where the symbol was used. - # ----------------------------------------------------------------------------- - def undefined_symbols(self): - result = [] - for p in self.Productions: - if not p: - continue - - for s in p.prod: - if s not in self.Prodnames and s not in self.Terminals and s != 'error': - result.append((s, p)) - return result - - # ----------------------------------------------------------------------------- - # unused_terminals() - # - # Find all terminals that were defined, but not used by the grammar. Returns - # a list of all symbols. - # ----------------------------------------------------------------------------- - def unused_terminals(self): - unused_tok = [] - for s, v in self.Terminals.items(): - if s != 'error' and not v: - unused_tok.append(s) - - return unused_tok - - # ------------------------------------------------------------------------------ - # unused_rules() - # - # Find all grammar rules that were defined, but not used (maybe not reachable) - # Returns a list of productions. - # ------------------------------------------------------------------------------ - - def unused_rules(self): - unused_prod = [] - for s, v in self.Nonterminals.items(): - if not v: - p = self.Prodnames[s][0] - unused_prod.append(p) - return unused_prod - - # ----------------------------------------------------------------------------- - # unused_precedence() - # - # Returns a list of tuples (term,precedence) corresponding to precedence - # rules that were never used by the grammar. term is the name of the terminal - # on which precedence was applied and precedence is a string such as 'left' or - # 'right' corresponding to the type of precedence. - # ----------------------------------------------------------------------------- - - def unused_precedence(self): - unused = [] - for termname in self.Precedence: - if not (termname in self.Terminals or termname in self.UsedPrecedence): - unused.append((termname, self.Precedence[termname][0])) - - return unused - - # ------------------------------------------------------------------------- - # _first() - # - # Compute the value of FIRST1(beta) where beta is a tuple of symbols. - # - # During execution of compute_first1, the result may be incomplete. - # Afterward (e.g., when called from compute_follow()), it will be complete. - # ------------------------------------------------------------------------- - def _first(self, beta): - - # We are computing First(x1,x2,x3,...,xn) - result = [] - for x in beta: - x_produces_empty = False - - # Add all the non- symbols of First[x] to the result. - for f in self.First[x]: - if f == '': - x_produces_empty = True - else: - if f not in result: - result.append(f) - - if x_produces_empty: - # We have to consider the next x in beta, - # i.e. stay in the loop. - pass - else: - # We don't have to consider any further symbols in beta. - break - else: - # There was no 'break' from the loop, - # so x_produces_empty was true for all x in beta, - # so beta produces empty as well. - result.append('') - - return result - - # ------------------------------------------------------------------------- - # compute_first() - # - # Compute the value of FIRST1(X) for all symbols - # ------------------------------------------------------------------------- - def compute_first(self): - if self.First: - return self.First - - # Terminals: - for t in self.Terminals: - self.First[t] = [t] - - self.First['$end'] = ['$end'] - - # Nonterminals: - - # Initialize to the empty set: - for n in self.Nonterminals: - self.First[n] = [] - - # Then propagate symbols until no change: - while True: - some_change = False - for n in self.Nonterminals: - for p in self.Prodnames[n]: - for f in self._first(p.prod): - if f not in self.First[n]: - self.First[n].append(f) - some_change = True - if not some_change: - break - - return self.First - - # --------------------------------------------------------------------- - # compute_follow() - # - # Computes all of the follow sets for every non-terminal symbol. The - # follow set is the set of all symbols that might follow a given - # non-terminal. See the Dragon book, 2nd Ed. p. 189. - # --------------------------------------------------------------------- - def compute_follow(self, start=None): - # If already computed, return the result - if self.Follow: - return self.Follow - - # If first sets not computed yet, do that first. - if not self.First: - self.compute_first() - - # Add '$end' to the follow list of the start symbol - for k in self.Nonterminals: - self.Follow[k] = [] - - if not start: - start = self.Productions[1].name - - self.Follow[start] = ['$end'] - - while True: - didadd = False - for p in self.Productions[1:]: - # Here is the production set - for i, B in enumerate(p.prod): - if B in self.Nonterminals: - # Okay. We got a non-terminal in a production - fst = self._first(p.prod[i+1:]) - hasempty = False - for f in fst: - if f != '' and f not in self.Follow[B]: - self.Follow[B].append(f) - didadd = True - if f == '': - hasempty = True - if hasempty or i == (len(p.prod)-1): - # Add elements of follow(a) to follow(b) - for f in self.Follow[p.name]: - if f not in self.Follow[B]: - self.Follow[B].append(f) - didadd = True - if not didadd: - break - return self.Follow - - - # ----------------------------------------------------------------------------- - # build_lritems() - # - # This function walks the list of productions and builds a complete set of the - # LR items. The LR items are stored in two ways: First, they are uniquely - # numbered and placed in the list _lritems. Second, a linked list of LR items - # is built for each production. For example: - # - # E -> E PLUS E - # - # Creates the list - # - # [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ] - # ----------------------------------------------------------------------------- - - def build_lritems(self): - for p in self.Productions: - lastlri = p - i = 0 - lr_items = [] - while True: - if i > len(p): - lri = None - else: - lri = LRItem(p, i) - # Precompute the list of productions immediately following - try: - lri.lr_after = self.Prodnames[lri.prod[i+1]] - except (IndexError, KeyError): - lri.lr_after = [] - try: - lri.lr_before = lri.prod[i-1] - except IndexError: - lri.lr_before = None - - lastlri.lr_next = lri - if not lri: - break - lr_items.append(lri) - lastlri = lri - i += 1 - p.lr_items = lr_items - -# ----------------------------------------------------------------------------- -# === LR Generator === -# -# The following classes and functions are used to generate LR parsing tables on -# a grammar. -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# digraph() -# traverse() -# -# The following two functions are used to compute set valued functions -# of the form: -# -# F(x) = F'(x) U U{F(y) | x R y} -# -# This is used to compute the values of Read() sets as well as FOLLOW sets -# in LALR(1) generation. -# -# Inputs: X - An input set -# R - A relation -# FP - Set-valued function -# ------------------------------------------------------------------------------ - -def digraph(X, R, FP): - N = {} - for x in X: - N[x] = 0 - stack = [] - F = {} - for x in X: - if N[x] == 0: - traverse(x, N, stack, F, X, R, FP) - return F - -def traverse(x, N, stack, F, X, R, FP): - stack.append(x) - d = len(stack) - N[x] = d - F[x] = FP(x) # F(X) <- F'(x) - - rel = R(x) # Get y's related to x - for y in rel: - if N[y] == 0: - traverse(y, N, stack, F, X, R, FP) - N[x] = min(N[x], N[y]) - for a in F.get(y, []): - if a not in F[x]: - F[x].append(a) - if N[x] == d: - N[stack[-1]] = MAXINT - F[stack[-1]] = F[x] - element = stack.pop() - while element != x: - N[stack[-1]] = MAXINT - F[stack[-1]] = F[x] - element = stack.pop() - -class LALRError(YaccError): - pass - - -# ----------------------------------------------------------------------------- -# == LRTable == -# -# This class implements the LR table generation algorithm. There are no -# public methods. -# ----------------------------------------------------------------------------- - -class LRTable: - def __init__(self, grammar, log=None): - self.grammar = grammar - - # Set up the logger - if not log: - log = NullLogger() - self.log = log - - # Internal attributes - self.lr_action = {} # Action table - self.lr_goto = {} # Goto table - self.lr_productions = grammar.Productions # Copy of grammar Production array - self.lr_goto_cache = {} # Cache of computed gotos - self.lr0_cidhash = {} # Cache of closures - - self._add_count = 0 # Internal counter used to detect cycles - - # Diagnostic information filled in by the table generator - self.sr_conflict = 0 - self.rr_conflict = 0 - self.conflicts = [] # List of conflicts - - self.sr_conflicts = [] - self.rr_conflicts = [] - - # Build the tables - self.grammar.build_lritems() - self.grammar.compute_first() - self.grammar.compute_follow() - self.lr_parse_table() - - # Bind all production function names to callable objects in pdict - def bind_callables(self, pdict): - for p in self.lr_productions: - p.bind(pdict) - - # Compute the LR(0) closure operation on I, where I is a set of LR(0) items. - - def lr0_closure(self, I): - self._add_count += 1 - - # Add everything in I to J - J = I[:] - didadd = True - while didadd: - didadd = False - for j in J: - for x in j.lr_after: - if getattr(x, 'lr0_added', 0) == self._add_count: - continue - # Add B --> .G to J - J.append(x.lr_next) - x.lr0_added = self._add_count - didadd = True - - return J - - # Compute the LR(0) goto function goto(I,X) where I is a set - # of LR(0) items and X is a grammar symbol. This function is written - # in a way that guarantees uniqueness of the generated goto sets - # (i.e. the same goto set will never be returned as two different Python - # objects). With uniqueness, we can later do fast set comparisons using - # id(obj) instead of element-wise comparison. - - def lr0_goto(self, I, x): - # First we look for a previously cached entry - g = self.lr_goto_cache.get((id(I), x)) - if g: - return g - - # Now we generate the goto set in a way that guarantees uniqueness - # of the result - - s = self.lr_goto_cache.get(x) - if not s: - s = {} - self.lr_goto_cache[x] = s - - gs = [] - for p in I: - n = p.lr_next - if n and n.lr_before == x: - s1 = s.get(id(n)) - if not s1: - s1 = {} - s[id(n)] = s1 - gs.append(n) - s = s1 - g = s.get('$end') - if not g: - if gs: - g = self.lr0_closure(gs) - s['$end'] = g - else: - s['$end'] = gs - self.lr_goto_cache[(id(I), x)] = g - return g - - # Compute the LR(0) sets of item function - def lr0_items(self): - C = [self.lr0_closure([self.grammar.Productions[0].lr_next])] - i = 0 - for I in C: - self.lr0_cidhash[id(I)] = i - i += 1 - - # Loop over the items in C and each grammar symbols - i = 0 - while i < len(C): - I = C[i] - i += 1 - - # Collect all of the symbols that could possibly be in the goto(I,X) sets - asyms = {} - for ii in I: - for s in ii.usyms: - asyms[s] = None - - for x in asyms: - g = self.lr0_goto(I, x) - if not g or id(g) in self.lr0_cidhash: - continue - self.lr0_cidhash[id(g)] = len(C) - C.append(g) - - return C - - # ----------------------------------------------------------------------------- - # ==== LALR(1) Parsing ==== - # - # LALR(1) parsing is almost exactly the same as SLR except that instead of - # relying upon Follow() sets when performing reductions, a more selective - # lookahead set that incorporates the state of the LR(0) machine is utilized. - # Thus, we mainly just have to focus on calculating the lookahead sets. - # - # The method used here is due to DeRemer and Pennelo (1982). - # - # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1) - # Lookahead Sets", ACM Transactions on Programming Languages and Systems, - # Vol. 4, No. 4, Oct. 1982, pp. 615-649 - # - # Further details can also be found in: - # - # J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing", - # McGraw-Hill Book Company, (1985). - # - # ----------------------------------------------------------------------------- - - # ----------------------------------------------------------------------------- - # compute_nullable_nonterminals() - # - # Creates a dictionary containing all of the non-terminals that might produce - # an empty production. - # ----------------------------------------------------------------------------- - - def compute_nullable_nonterminals(self): - nullable = set() - num_nullable = 0 - while True: - for p in self.grammar.Productions[1:]: - if p.len == 0: - nullable.add(p.name) - continue - for t in p.prod: - if t not in nullable: - break - else: - nullable.add(p.name) - if len(nullable) == num_nullable: - break - num_nullable = len(nullable) - return nullable - - # ----------------------------------------------------------------------------- - # find_nonterminal_trans(C) - # - # Given a set of LR(0) items, this functions finds all of the non-terminal - # transitions. These are transitions in which a dot appears immediately before - # a non-terminal. Returns a list of tuples of the form (state,N) where state - # is the state number and N is the nonterminal symbol. - # - # The input C is the set of LR(0) items. - # ----------------------------------------------------------------------------- - - def find_nonterminal_transitions(self, C): - trans = [] - for stateno, state in enumerate(C): - for p in state: - if p.lr_index < p.len - 1: - t = (stateno, p.prod[p.lr_index+1]) - if t[1] in self.grammar.Nonterminals: - if t not in trans: - trans.append(t) - return trans - - # ----------------------------------------------------------------------------- - # dr_relation() - # - # Computes the DR(p,A) relationships for non-terminal transitions. The input - # is a tuple (state,N) where state is a number and N is a nonterminal symbol. - # - # Returns a list of terminals. - # ----------------------------------------------------------------------------- - - def dr_relation(self, C, trans, nullable): - state, N = trans - terms = [] - - g = self.lr0_goto(C[state], N) - for p in g: - if p.lr_index < p.len - 1: - a = p.prod[p.lr_index+1] - if a in self.grammar.Terminals: - if a not in terms: - terms.append(a) - - # This extra bit is to handle the start state - if state == 0 and N == self.grammar.Productions[0].prod[0]: - terms.append('$end') - - return terms - - # ----------------------------------------------------------------------------- - # reads_relation() - # - # Computes the READS() relation (p,A) READS (t,C). - # ----------------------------------------------------------------------------- - - def reads_relation(self, C, trans, empty): - # Look for empty transitions - rel = [] - state, N = trans - - g = self.lr0_goto(C[state], N) - j = self.lr0_cidhash.get(id(g), -1) - for p in g: - if p.lr_index < p.len - 1: - a = p.prod[p.lr_index + 1] - if a in empty: - rel.append((j, a)) - - return rel - - # ----------------------------------------------------------------------------- - # compute_lookback_includes() - # - # Determines the lookback and includes relations - # - # LOOKBACK: - # - # This relation is determined by running the LR(0) state machine forward. - # For example, starting with a production "N : . A B C", we run it forward - # to obtain "N : A B C ." We then build a relationship between this final - # state and the starting state. These relationships are stored in a dictionary - # lookdict. - # - # INCLUDES: - # - # Computes the INCLUDE() relation (p,A) INCLUDES (p',B). - # - # This relation is used to determine non-terminal transitions that occur - # inside of other non-terminal transition states. (p,A) INCLUDES (p', B) - # if the following holds: - # - # B -> LAT, where T -> epsilon and p' -L-> p - # - # L is essentially a prefix (which may be empty), T is a suffix that must be - # able to derive an empty string. State p' must lead to state p with the string L. - # - # ----------------------------------------------------------------------------- - - def compute_lookback_includes(self, C, trans, nullable): - lookdict = {} # Dictionary of lookback relations - includedict = {} # Dictionary of include relations - - # Make a dictionary of non-terminal transitions - dtrans = {} - for t in trans: - dtrans[t] = 1 - - # Loop over all transitions and compute lookbacks and includes - for state, N in trans: - lookb = [] - includes = [] - for p in C[state]: - if p.name != N: - continue - - # Okay, we have a name match. We now follow the production all the way - # through the state machine until we get the . on the right hand side - - lr_index = p.lr_index - j = state - while lr_index < p.len - 1: - lr_index = lr_index + 1 - t = p.prod[lr_index] - - # Check to see if this symbol and state are a non-terminal transition - if (j, t) in dtrans: - # Yes. Okay, there is some chance that this is an includes relation - # the only way to know for certain is whether the rest of the - # production derives empty - - li = lr_index + 1 - while li < p.len: - if p.prod[li] in self.grammar.Terminals: - break # No forget it - if p.prod[li] not in nullable: - break - li = li + 1 - else: - # Appears to be a relation between (j,t) and (state,N) - includes.append((j, t)) - - g = self.lr0_goto(C[j], t) # Go to next set - j = self.lr0_cidhash.get(id(g), -1) # Go to next state - - # When we get here, j is the final state, now we have to locate the production - for r in C[j]: - if r.name != p.name: - continue - if r.len != p.len: - continue - i = 0 - # This look is comparing a production ". A B C" with "A B C ." - while i < r.lr_index: - if r.prod[i] != p.prod[i+1]: - break - i = i + 1 - else: - lookb.append((j, r)) - for i in includes: - if i not in includedict: - includedict[i] = [] - includedict[i].append((state, N)) - lookdict[(state, N)] = lookb - - return lookdict, includedict - - # ----------------------------------------------------------------------------- - # compute_read_sets() - # - # Given a set of LR(0) items, this function computes the read sets. - # - # Inputs: C = Set of LR(0) items - # ntrans = Set of nonterminal transitions - # nullable = Set of empty transitions - # - # Returns a set containing the read sets - # ----------------------------------------------------------------------------- - - def compute_read_sets(self, C, ntrans, nullable): - FP = lambda x: self.dr_relation(C, x, nullable) - R = lambda x: self.reads_relation(C, x, nullable) - F = digraph(ntrans, R, FP) - return F - - # ----------------------------------------------------------------------------- - # compute_follow_sets() - # - # Given a set of LR(0) items, a set of non-terminal transitions, a readset, - # and an include set, this function computes the follow sets - # - # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)} - # - # Inputs: - # ntrans = Set of nonterminal transitions - # readsets = Readset (previously computed) - # inclsets = Include sets (previously computed) - # - # Returns a set containing the follow sets - # ----------------------------------------------------------------------------- - - def compute_follow_sets(self, ntrans, readsets, inclsets): - FP = lambda x: readsets[x] - R = lambda x: inclsets.get(x, []) - F = digraph(ntrans, R, FP) - return F - - # ----------------------------------------------------------------------------- - # add_lookaheads() - # - # Attaches the lookahead symbols to grammar rules. - # - # Inputs: lookbacks - Set of lookback relations - # followset - Computed follow set - # - # This function directly attaches the lookaheads to productions contained - # in the lookbacks set - # ----------------------------------------------------------------------------- - - def add_lookaheads(self, lookbacks, followset): - for trans, lb in lookbacks.items(): - # Loop over productions in lookback - for state, p in lb: - if state not in p.lookaheads: - p.lookaheads[state] = [] - f = followset.get(trans, []) - for a in f: - if a not in p.lookaheads[state]: - p.lookaheads[state].append(a) - - # ----------------------------------------------------------------------------- - # add_lalr_lookaheads() - # - # This function does all of the work of adding lookahead information for use - # with LALR parsing - # ----------------------------------------------------------------------------- - - def add_lalr_lookaheads(self, C): - # Determine all of the nullable nonterminals - nullable = self.compute_nullable_nonterminals() - - # Find all non-terminal transitions - trans = self.find_nonterminal_transitions(C) - - # Compute read sets - readsets = self.compute_read_sets(C, trans, nullable) - - # Compute lookback/includes relations - lookd, included = self.compute_lookback_includes(C, trans, nullable) - - # Compute LALR FOLLOW sets - followsets = self.compute_follow_sets(trans, readsets, included) - - # Add all of the lookaheads - self.add_lookaheads(lookd, followsets) - - # ----------------------------------------------------------------------------- - # lr_parse_table() - # - # This function constructs the parse tables for SLR or LALR - # ----------------------------------------------------------------------------- - def lr_parse_table(self): - Productions = self.grammar.Productions - Precedence = self.grammar.Precedence - goto = self.lr_goto # Goto array - action = self.lr_action # Action array - log = self.log # Logger for output - - actionp = {} # Action production array (temporary) - - # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items - # This determines the number of states - - C = self.lr0_items() - self.add_lalr_lookaheads(C) - - # Build the parser table, state by state - st = 0 - for I in C: - # Loop over each production in I - actlist = [] # List of actions - st_action = {} - st_actionp = {} - st_goto = {} - log.info('') - log.info('state %d', st) - log.info('') - for p in I: - log.info(' (%d) %s', p.number, p) - log.info('') - - for p in I: - if p.len == p.lr_index + 1: - if p.name == "S'": - # Start symbol. Accept! - st_action['$end'] = 0 - st_actionp['$end'] = p - else: - # We are at the end of a production. Reduce! - laheads = p.lookaheads[st] - for a in laheads: - actlist.append((a, p, 'reduce using rule %d (%s)' % (p.number, p))) - r = st_action.get(a) - if r is not None: - # Whoa. Have a shift/reduce or reduce/reduce conflict - if r > 0: - # Need to decide on shift or reduce here - # By default we favor shifting. Need to add - # some precedence rules here. - - # Shift precedence comes from the token - sprec, slevel = Precedence.get(a, ('right', 0)) - - # Reduce precedence comes from rule being reduced (p) - rprec, rlevel = Productions[p.number].prec - - if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')): - # We really need to reduce here. - st_action[a] = -p.number - st_actionp[a] = p - if not slevel and not rlevel: - log.info(' ! shift/reduce conflict for %s resolved as reduce', a) - self.sr_conflicts.append((st, a, 'reduce')) - Productions[p.number].reduced += 1 - elif (slevel == rlevel) and (rprec == 'nonassoc'): - st_action[a] = None - else: - # Hmmm. Guess we'll keep the shift - if not rlevel: - log.info(' ! shift/reduce conflict for %s resolved as shift', a) - self.sr_conflicts.append((st, a, 'shift')) - elif r < 0: - # Reduce/reduce conflict. In this case, we favor the rule - # that was defined first in the grammar file - oldp = Productions[-r] - pp = Productions[p.number] - if oldp.line > pp.line: - st_action[a] = -p.number - st_actionp[a] = p - chosenp, rejectp = pp, oldp - Productions[p.number].reduced += 1 - Productions[oldp.number].reduced -= 1 - else: - chosenp, rejectp = oldp, pp - self.rr_conflicts.append((st, chosenp, rejectp)) - log.info(' ! reduce/reduce conflict for %s resolved using rule %d (%s)', - a, st_actionp[a].number, st_actionp[a]) - else: - raise LALRError('Unknown conflict in state %d' % st) - else: - st_action[a] = -p.number - st_actionp[a] = p - Productions[p.number].reduced += 1 - else: - i = p.lr_index - a = p.prod[i+1] # Get symbol right after the "." - if a in self.grammar.Terminals: - g = self.lr0_goto(I, a) - j = self.lr0_cidhash.get(id(g), -1) - if j >= 0: - # We are in a shift state - actlist.append((a, p, 'shift and go to state %d' % j)) - r = st_action.get(a) - if r is not None: - # Whoa have a shift/reduce or shift/shift conflict - if r > 0: - if r != j: - raise LALRError('Shift/shift conflict in state %d' % st) - elif r < 0: - # Do a precedence check. - # - if precedence of reduce rule is higher, we reduce. - # - if precedence of reduce is same and left assoc, we reduce. - # - otherwise we shift - - # Shift precedence comes from the token - sprec, slevel = Precedence.get(a, ('right', 0)) - - # Reduce precedence comes from the rule that could have been reduced - rprec, rlevel = Productions[st_actionp[a].number].prec - - if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')): - # We decide to shift here... highest precedence to shift - Productions[st_actionp[a].number].reduced -= 1 - st_action[a] = j - st_actionp[a] = p - if not rlevel: - log.info(' ! shift/reduce conflict for %s resolved as shift', a) - self.sr_conflicts.append((st, a, 'shift')) - elif (slevel == rlevel) and (rprec == 'nonassoc'): - st_action[a] = None - else: - # Hmmm. Guess we'll keep the reduce - if not slevel and not rlevel: - log.info(' ! shift/reduce conflict for %s resolved as reduce', a) - self.sr_conflicts.append((st, a, 'reduce')) - - else: - raise LALRError('Unknown conflict in state %d' % st) - else: - st_action[a] = j - st_actionp[a] = p - - # Print the actions associated with each terminal - _actprint = {} - for a, p, m in actlist: - if a in st_action: - if p is st_actionp[a]: - log.info(' %-15s %s', a, m) - _actprint[(a, m)] = 1 - log.info('') - # Print the actions that were not used. (debugging) - not_used = 0 - for a, p, m in actlist: - if a in st_action: - if p is not st_actionp[a]: - if not (a, m) in _actprint: - log.debug(' ! %-15s [ %s ]', a, m) - not_used = 1 - _actprint[(a, m)] = 1 - if not_used: - log.debug('') - - # Construct the goto table for this state - - nkeys = {} - for ii in I: - for s in ii.usyms: - if s in self.grammar.Nonterminals: - nkeys[s] = None - for n in nkeys: - g = self.lr0_goto(I, n) - j = self.lr0_cidhash.get(id(g), -1) - if j >= 0: - st_goto[n] = j - log.info(' %-30s shift and go to state %d', n, j) - - action[st] = st_action - actionp[st] = st_actionp - goto[st] = st_goto - st += 1 - -# ----------------------------------------------------------------------------- -# === INTROSPECTION === -# -# The following functions and classes are used to implement the PLY -# introspection features followed by the yacc() function itself. -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# get_caller_module_dict() -# -# This function returns a dictionary containing all of the symbols defined within -# a caller further down the call stack. This is used to get the environment -# associated with the yacc() call if none was provided. -# ----------------------------------------------------------------------------- - -def get_caller_module_dict(levels): - f = sys._getframe(levels) - ldict = f.f_globals.copy() - if f.f_globals != f.f_locals: - ldict.update(f.f_locals) - return ldict - -# ----------------------------------------------------------------------------- -# parse_grammar() -# -# This takes a raw grammar rule string and parses it into production data -# ----------------------------------------------------------------------------- -def parse_grammar(doc, file, line): - grammar = [] - # Split the doc string into lines - pstrings = doc.splitlines() - lastp = None - dline = line - for ps in pstrings: - dline += 1 - p = ps.split() - if not p: - continue - try: - if p[0] == '|': - # This is a continuation of a previous rule - if not lastp: - raise SyntaxError("%s:%d: Misplaced '|'" % (file, dline)) - prodname = lastp - syms = p[1:] - else: - prodname = p[0] - lastp = prodname - syms = p[2:] - assign = p[1] - if assign != ':' and assign != '::=': - raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file, dline)) - - grammar.append((file, dline, prodname, syms)) - except SyntaxError: - raise - except Exception: - raise SyntaxError('%s:%d: Syntax error in rule %r' % (file, dline, ps.strip())) - - return grammar - -# ----------------------------------------------------------------------------- -# ParserReflect() -# -# This class represents information extracted for building a parser including -# start symbol, error function, tokens, precedence list, action functions, -# etc. -# ----------------------------------------------------------------------------- -class ParserReflect(object): - def __init__(self, pdict, log=None): - self.pdict = pdict - self.start = None - self.error_func = None - self.tokens = None - self.modules = set() - self.grammar = [] - self.error = False - - if log is None: - self.log = PlyLogger(sys.stderr) - else: - self.log = log - - # Get all of the basic information - def get_all(self): - self.get_start() - self.get_error_func() - self.get_tokens() - self.get_precedence() - self.get_pfunctions() - - # Validate all of the information - def validate_all(self): - self.validate_start() - self.validate_error_func() - self.validate_tokens() - self.validate_precedence() - self.validate_pfunctions() - self.validate_modules() - return self.error - - # Compute a signature over the grammar - def signature(self): - parts = [] - try: - if self.start: - parts.append(self.start) - if self.prec: - parts.append(''.join([''.join(p) for p in self.prec])) - if self.tokens: - parts.append(' '.join(self.tokens)) - for f in self.pfuncs: - if f[3]: - parts.append(f[3]) - except (TypeError, ValueError): - pass - return ''.join(parts) - - # ----------------------------------------------------------------------------- - # validate_modules() - # - # This method checks to see if there are duplicated p_rulename() functions - # in the parser module file. Without this function, it is really easy for - # users to make mistakes by cutting and pasting code fragments (and it's a real - # bugger to try and figure out why the resulting parser doesn't work). Therefore, - # we just do a little regular expression pattern matching of def statements - # to try and detect duplicates. - # ----------------------------------------------------------------------------- - - def validate_modules(self): - # Match def p_funcname( - fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(') - - for module in self.modules: - try: - lines, linen = inspect.getsourcelines(module) - except IOError: - continue - - counthash = {} - for linen, line in enumerate(lines): - linen += 1 - m = fre.match(line) - if m: - name = m.group(1) - prev = counthash.get(name) - if not prev: - counthash[name] = linen - else: - filename = inspect.getsourcefile(module) - self.log.warning('%s:%d: Function %s redefined. Previously defined on line %d', - filename, linen, name, prev) - - # Get the start symbol - def get_start(self): - self.start = self.pdict.get('start') - - # Validate the start symbol - def validate_start(self): - if self.start is not None: - if not isinstance(self.start, str): - self.log.error("'start' must be a string") - - # Look for error handler - def get_error_func(self): - self.error_func = self.pdict.get('p_error') - - # Validate the error function - def validate_error_func(self): - if self.error_func: - if isinstance(self.error_func, types.FunctionType): - ismethod = 0 - elif isinstance(self.error_func, types.MethodType): - ismethod = 1 - else: - self.log.error("'p_error' defined, but is not a function or method") - self.error = True - return - - eline = self.error_func.__code__.co_firstlineno - efile = self.error_func.__code__.co_filename - module = inspect.getmodule(self.error_func) - self.modules.add(module) - - argcount = self.error_func.__code__.co_argcount - ismethod - if argcount != 1: - self.log.error('%s:%d: p_error() requires 1 argument', efile, eline) - self.error = True - - # Get the tokens map - def get_tokens(self): - tokens = self.pdict.get('tokens') - if not tokens: - self.log.error('No token list is defined') - self.error = True - return - - if not isinstance(tokens, (list, tuple)): - self.log.error('tokens must be a list or tuple') - self.error = True - return - - if not tokens: - self.log.error('tokens is empty') - self.error = True - return - - self.tokens = sorted(tokens) - - # Validate the tokens - def validate_tokens(self): - # Validate the tokens. - if 'error' in self.tokens: - self.log.error("Illegal token name 'error'. Is a reserved word") - self.error = True - return - - terminals = set() - for n in self.tokens: - if n in terminals: - self.log.warning('Token %r multiply defined', n) - terminals.add(n) - - # Get the precedence map (if any) - def get_precedence(self): - self.prec = self.pdict.get('precedence') - - # Validate and parse the precedence map - def validate_precedence(self): - preclist = [] - if self.prec: - if not isinstance(self.prec, (list, tuple)): - self.log.error('precedence must be a list or tuple') - self.error = True - return - for level, p in enumerate(self.prec): - if not isinstance(p, (list, tuple)): - self.log.error('Bad precedence table') - self.error = True - return - - if len(p) < 2: - self.log.error('Malformed precedence entry %s. Must be (assoc, term, ..., term)', p) - self.error = True - return - assoc = p[0] - if not isinstance(assoc, str): - self.log.error('precedence associativity must be a string') - self.error = True - return - for term in p[1:]: - if not isinstance(term, str): - self.log.error('precedence items must be strings') - self.error = True - return - preclist.append((term, assoc, level+1)) - self.preclist = preclist - - # Get all p_functions from the grammar - def get_pfunctions(self): - p_functions = [] - for name, item in self.pdict.items(): - if not name.startswith('p_') or name == 'p_error': - continue - if isinstance(item, (types.FunctionType, types.MethodType)): - line = getattr(item, 'co_firstlineno', item.__code__.co_firstlineno) - module = inspect.getmodule(item) - p_functions.append((line, module, name, item.__doc__)) - - # Sort all of the actions by line number; make sure to stringify - # modules to make them sortable, since `line` may not uniquely sort all - # p functions - p_functions.sort(key=lambda p_function: ( - p_function[0], - str(p_function[1]), - p_function[2], - p_function[3])) - self.pfuncs = p_functions - - # Validate all of the p_functions - def validate_pfunctions(self): - grammar = [] - # Check for non-empty symbols - if len(self.pfuncs) == 0: - self.log.error('no rules of the form p_rulename are defined') - self.error = True - return - - for line, module, name, doc in self.pfuncs: - file = inspect.getsourcefile(module) - func = self.pdict[name] - if isinstance(func, types.MethodType): - reqargs = 2 - else: - reqargs = 1 - if func.__code__.co_argcount > reqargs: - self.log.error('%s:%d: Rule %r has too many arguments', file, line, func.__name__) - self.error = True - elif func.__code__.co_argcount < reqargs: - self.log.error('%s:%d: Rule %r requires an argument', file, line, func.__name__) - self.error = True - elif not func.__doc__: - self.log.warning('%s:%d: No documentation string specified in function %r (ignored)', - file, line, func.__name__) - else: - try: - parsed_g = parse_grammar(doc, file, line) - for g in parsed_g: - grammar.append((name, g)) - except SyntaxError as e: - self.log.error(str(e)) - self.error = True - - # Looks like a valid grammar rule - # Mark the file in which defined. - self.modules.add(module) - - # Secondary validation step that looks for p_ definitions that are not functions - # or functions that look like they might be grammar rules. - - for n, v in self.pdict.items(): - if n.startswith('p_') and isinstance(v, (types.FunctionType, types.MethodType)): - continue - if n.startswith('t_'): - continue - if n.startswith('p_') and n != 'p_error': - self.log.warning('%r not defined as a function', n) - if ((isinstance(v, types.FunctionType) and v.__code__.co_argcount == 1) or - (isinstance(v, types.MethodType) and v.__func__.__code__.co_argcount == 2)): - if v.__doc__: - try: - doc = v.__doc__.split(' ') - if doc[1] == ':': - self.log.warning('%s:%d: Possible grammar rule %r defined without p_ prefix', - v.__code__.co_filename, v.__code__.co_firstlineno, n) - except IndexError: - pass - - self.grammar = grammar - -# ----------------------------------------------------------------------------- -# yacc(module) -# -# Build a parser -# ----------------------------------------------------------------------------- - -def yacc(*, debug=yaccdebug, module=None, start=None, - check_recursion=True, optimize=False, debugfile=debug_file, - debuglog=None, errorlog=None): - - # Reference to the parsing method of the last built parser - global parse - - if errorlog is None: - errorlog = PlyLogger(sys.stderr) - - # Get the module dictionary used for the parser - if module: - _items = [(k, getattr(module, k)) for k in dir(module)] - pdict = dict(_items) - # If no __file__ or __package__ attributes are available, try to obtain them - # from the __module__ instead - if '__file__' not in pdict: - pdict['__file__'] = sys.modules[pdict['__module__']].__file__ - if '__package__' not in pdict and '__module__' in pdict: - if hasattr(sys.modules[pdict['__module__']], '__package__'): - pdict['__package__'] = sys.modules[pdict['__module__']].__package__ - else: - pdict = get_caller_module_dict(2) - - # Set start symbol if it's specified directly using an argument - if start is not None: - pdict['start'] = start - - # Collect parser information from the dictionary - pinfo = ParserReflect(pdict, log=errorlog) - pinfo.get_all() - - if pinfo.error: - raise YaccError('Unable to build parser') - - if debuglog is None: - if debug: - try: - debuglog = PlyLogger(open(debugfile, 'w')) - except IOError as e: - errorlog.warning("Couldn't open %r. %s" % (debugfile, e)) - debuglog = NullLogger() - else: - debuglog = NullLogger() - - debuglog.info('Created by PLY (http://www.dabeaz.com/ply)') - - errors = False - - # Validate the parser information - if pinfo.validate_all(): - raise YaccError('Unable to build parser') - - if not pinfo.error_func: - errorlog.warning('no p_error() function is defined') - - # Create a grammar object - grammar = Grammar(pinfo.tokens) - - # Set precedence level for terminals - for term, assoc, level in pinfo.preclist: - try: - grammar.set_precedence(term, assoc, level) - except GrammarError as e: - errorlog.warning('%s', e) - - # Add productions to the grammar - for funcname, gram in pinfo.grammar: - file, line, prodname, syms = gram - try: - grammar.add_production(prodname, syms, funcname, file, line) - except GrammarError as e: - errorlog.error('%s', e) - errors = True - - # Set the grammar start symbols - try: - if start is None: - grammar.set_start(pinfo.start) - else: - grammar.set_start(start) - except GrammarError as e: - errorlog.error(str(e)) - errors = True - - if errors: - raise YaccError('Unable to build parser') - - # Verify the grammar structure - undefined_symbols = grammar.undefined_symbols() - for sym, prod in undefined_symbols: - errorlog.error('%s:%d: Symbol %r used, but not defined as a token or a rule', prod.file, prod.line, sym) - errors = True - - unused_terminals = grammar.unused_terminals() - if unused_terminals: - debuglog.info('') - debuglog.info('Unused terminals:') - debuglog.info('') - for term in unused_terminals: - errorlog.warning('Token %r defined, but not used', term) - debuglog.info(' %s', term) - - # Print out all productions to the debug log - if debug: - debuglog.info('') - debuglog.info('Grammar') - debuglog.info('') - for n, p in enumerate(grammar.Productions): - debuglog.info('Rule %-5d %s', n, p) - - # Find unused non-terminals - unused_rules = grammar.unused_rules() - for prod in unused_rules: - errorlog.warning('%s:%d: Rule %r defined, but not used', prod.file, prod.line, prod.name) - - if len(unused_terminals) == 1: - errorlog.warning('There is 1 unused token') - if len(unused_terminals) > 1: - errorlog.warning('There are %d unused tokens', len(unused_terminals)) - - if len(unused_rules) == 1: - errorlog.warning('There is 1 unused rule') - if len(unused_rules) > 1: - errorlog.warning('There are %d unused rules', len(unused_rules)) - - if debug: - debuglog.info('') - debuglog.info('Terminals, with rules where they appear') - debuglog.info('') - terms = list(grammar.Terminals) - terms.sort() - for term in terms: - debuglog.info('%-20s : %s', term, ' '.join([str(s) for s in grammar.Terminals[term]])) - - debuglog.info('') - debuglog.info('Nonterminals, with rules where they appear') - debuglog.info('') - nonterms = list(grammar.Nonterminals) - nonterms.sort() - for nonterm in nonterms: - debuglog.info('%-20s : %s', nonterm, ' '.join([str(s) for s in grammar.Nonterminals[nonterm]])) - debuglog.info('') - - if check_recursion: - unreachable = grammar.find_unreachable() - for u in unreachable: - errorlog.warning('Symbol %r is unreachable', u) - - infinite = grammar.infinite_cycles() - for inf in infinite: - errorlog.error('Infinite recursion detected for symbol %r', inf) - errors = True - - unused_prec = grammar.unused_precedence() - for term, assoc in unused_prec: - errorlog.error('Precedence rule %r defined for unknown symbol %r', assoc, term) - errors = True - - if errors: - raise YaccError('Unable to build parser') - - # Run the LRTable on the grammar - lr = LRTable(grammar, debuglog) - - if debug: - num_sr = len(lr.sr_conflicts) - - # Report shift/reduce and reduce/reduce conflicts - if num_sr == 1: - errorlog.warning('1 shift/reduce conflict') - elif num_sr > 1: - errorlog.warning('%d shift/reduce conflicts', num_sr) - - num_rr = len(lr.rr_conflicts) - if num_rr == 1: - errorlog.warning('1 reduce/reduce conflict') - elif num_rr > 1: - errorlog.warning('%d reduce/reduce conflicts', num_rr) - - # Write out conflicts to the output file - if debug and (lr.sr_conflicts or lr.rr_conflicts): - debuglog.warning('') - debuglog.warning('Conflicts:') - debuglog.warning('') - - for state, tok, resolution in lr.sr_conflicts: - debuglog.warning('shift/reduce conflict for %s in state %d resolved as %s', tok, state, resolution) - - already_reported = set() - for state, rule, rejected in lr.rr_conflicts: - if (state, id(rule), id(rejected)) in already_reported: - continue - debuglog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule) - debuglog.warning('rejected rule (%s) in state %d', rejected, state) - errorlog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule) - errorlog.warning('rejected rule (%s) in state %d', rejected, state) - already_reported.add((state, id(rule), id(rejected))) - - warned_never = [] - for state, rule, rejected in lr.rr_conflicts: - if not rejected.reduced and (rejected not in warned_never): - debuglog.warning('Rule (%s) is never reduced', rejected) - errorlog.warning('Rule (%s) is never reduced', rejected) - warned_never.append(rejected) - - # Build the parser - lr.bind_callables(pinfo.pdict) - parser = LRParser(lr, pinfo.error_func) - - parse = parser.parse - return parser diff --git a/src/symbols/__init__.py b/src/symbols/__init__.py index e69de29bb..906667a8d 100644 --- a/src/symbols/__init__.py +++ b/src/symbols/__init__.py @@ -0,0 +1,6 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- diff --git a/src/symbols/arglist.py b/src/symbols/arglist.py index a7195e165..fd291b468 100644 --- a/src/symbols/arglist.py +++ b/src/symbols/arglist.py @@ -1,9 +1,10 @@ -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from __future__ import annotations from collections.abc import Iterable diff --git a/src/symbols/argument.py b/src/symbols/argument.py index 88be760d9..28e0ca47b 100644 --- a/src/symbols/argument.py +++ b/src/symbols/argument.py @@ -1,17 +1,15 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- - +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.api import global_ as gl from src.api.config import OPTIONS from src.api.constants import CLASS, SCOPE from src.symbols.symbol_ import Symbol +from src.symbols.type_ import SymbolTYPE, SymbolTYPEREF from src.symbols.typecast import SymbolTYPECAST @@ -83,10 +81,13 @@ def __eq__(self, other): return self.value == other.value return self.value == other - def typecast(self, type_): + def typecast(self, type_: SymbolTYPE | SymbolTYPEREF): """Test type casting to the argument expression. On success changes the node value to the new typecast, and returns True. On failure, returns False, and the node value is set to None. """ + if isinstance(type_, SymbolTYPEREF): + type_ = type_.type_ + self.value = SymbolTYPECAST.make_node(type_, self.value, self.lineno) return self.value is not None diff --git a/src/symbols/arrayaccess.py b/src/symbols/arrayaccess.py index f95806e06..d8c12e9e7 100644 --- a/src/symbols/arrayaccess.py +++ b/src/symbols/arrayaccess.py @@ -1,9 +1,9 @@ -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from functools import cached_property from typing import Self @@ -14,6 +14,7 @@ from src.symbols.arglist import SymbolARGLIST from src.symbols.call import SymbolCALL from src.symbols.id_ import SymbolID +from src.symbols.type_ import Type from src.symbols.typecast import SymbolTYPECAST as TYPECAST @@ -31,9 +32,10 @@ class SymbolARRAYACCESS(SymbolCALL): Arglist a SymbolARGLIST instance. """ - def __init__(self, entry, arglist: SymbolARGLIST, lineno: int, filename: str): + def __init__(self, entry: SymbolID, arglist: SymbolARGLIST, lineno: int, filename: str): super().__init__(entry, arglist, lineno, filename) - assert all(gl.BOUND_TYPE == x.type_.type_ for x in arglist), "Invalid type for array index" + bound_type = Type.by_name(gl.BOUND_TYPE.name) + assert all(bound_type == x.type_.final for x in arglist), "Invalid type for array index" self.entry.ref.is_dynamically_accessed = True @property @@ -52,15 +54,6 @@ def entry(self, value: SymbolID): def type_(self): return self.entry.type_ - @property - def arglist(self) -> SymbolARGLIST: - return self.children[1] - - @arglist.setter - def arglist(self, value: SymbolARGLIST): - assert isinstance(value, SymbolARGLIST) - self.children[1] = value - @property def scope(self): return self.entry.scope @@ -80,7 +73,7 @@ def offset(self) -> int | None: offset = 0 # Now we must typecast each argument to a u16 (POINTER) type # i is the dimension ith index, b is the bound - for i, b in zip(self.arglist, self.entry.bounds): + for i, b in zip(self.args, self.entry.bounds): tmp = i.children[0] if check.is_number(tmp) or check.is_const(tmp): offset = offset * b.count + (tmp.value - b.lower) @@ -131,4 +124,4 @@ def make_node(cls, id_: str, arglist: SymbolARGLIST, lineno: int, filename: str) @classmethod def copy_from(cls, other: Self) -> Self | None: - return cls(entry=other.entry, arglist=other.arglist, lineno=other.lineno, filename=other.filename) + return cls(entry=other.entry, arglist=other.args, lineno=other.lineno, filename=other.filename) diff --git a/src/symbols/arraydecl.py b/src/symbols/arraydecl.py index 3aff49d26..5ea367244 100644 --- a/src/symbols/arraydecl.py +++ b/src/symbols/arraydecl.py @@ -1,18 +1,18 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + +from .id_.interface import SymbolIdABC as SymbolID from .symbol_ import Symbol class SymbolARRAYDECL(Symbol): """Defines an Array declaration""" - def __init__(self, entry): + def __init__(self, entry: SymbolID): super().__init__(entry) @property diff --git a/src/symbols/arrayload.py b/src/symbols/arrayload.py index d6c7f0738..222413fcc 100644 --- a/src/symbols/arrayload.py +++ b/src/symbols/arrayload.py @@ -1,11 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.symbols.arrayaccess import SymbolARRAYACCESS @@ -13,7 +11,6 @@ class SymbolARRAYLOAD(SymbolARRAYACCESS): """This class is the same as SymbolARRAYACCESS, we just declare it to make a distinction. (e.g. the Token is gotten from the class name). - """ pass diff --git a/src/symbols/asm.py b/src/symbols/asm.py index 130fc9eb5..fae9fe901 100644 --- a/src/symbols/asm.py +++ b/src/symbols/asm.py @@ -1,11 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .symbol_ import Symbol diff --git a/src/symbols/binary.py b/src/symbols/binary.py index 3d6191e6f..624b5f83f 100644 --- a/src/symbols/binary.py +++ b/src/symbols/binary.py @@ -1,9 +1,9 @@ -# ---------------------------------------------------------------------- -# Copyleft, Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the Affero GNU General License v3 -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.api import check, errmsg from src.symbols.constexpr import SymbolCONSTEXPR @@ -23,7 +23,7 @@ def __init__(self, operator: str, left: Symbol, right: Symbol, lineno: int, type super().__init__(left, right) self.lineno = lineno self.operator = operator - self.type_ = type_ if type_ is not None else check.common_type(left, right) + self.type_ = type_ if type_ is not None else check.common_type(left.type_, right.type_) self.func = func # Will be used for constant folding at later stages if not None @property @@ -98,7 +98,7 @@ def make_node(cls, operator, left, right, lineno, func=None, type_=None): b = SymbolTYPECAST.make_node(TYPE.ubyte, b, lineno) # Check for constant non-numeric operations - c_type = check.common_type(a, b) # Resulting operation type or None + c_type = check.common_type(a.type_, b.type_) # Resulting operation type or None if TYPE.is_numeric(c_type): # there must be a common type for a and b if ( check.is_numeric(a, b) @@ -129,8 +129,8 @@ def make_node(cls, operator, left, right, lineno, func=None, type_=None): c_type = a.type_ # Will give an error based on the fist operand if operator not in ("SHR", "SHL"): - a = SymbolTYPECAST.make_node(c_type, a, lineno) - b = SymbolTYPECAST.make_node(c_type, b, lineno) + a = SymbolTYPECAST.make_node(c_type.final, a, lineno) + b = SymbolTYPECAST.make_node(c_type.final, b, lineno) if a is None or b is None: return None diff --git a/src/symbols/block.py b/src/symbols/block.py index e573ba1f1..f2a4d752c 100644 --- a/src/symbols/block.py +++ b/src/symbols/block.py @@ -1,11 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License v3 -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.api import check diff --git a/src/symbols/bound.py b/src/symbols/bound.py index e313e9bd4..69ea27a3b 100644 --- a/src/symbols/bound.py +++ b/src/symbols/bound.py @@ -1,11 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.api import check from src.api.errmsg import error diff --git a/src/symbols/boundlist.py b/src/symbols/boundlist.py index 38ea04d4b..a6ac2c561 100644 --- a/src/symbols/boundlist.py +++ b/src/symbols/boundlist.py @@ -1,12 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- - +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.symbols.bound import SymbolBOUND from src.symbols.symbol_ import Symbol diff --git a/src/symbols/builtin.py b/src/symbols/builtin.py index 54565d6cc..592cf16d7 100644 --- a/src/symbols/builtin.py +++ b/src/symbols/builtin.py @@ -1,9 +1,11 @@ -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- +from collections.abc import Callable +from typing import Self from src.api import check @@ -13,7 +15,7 @@ class SymbolBUILTIN(Symbol): - """Defines an BUILTIN function e.g. INKEY$(), RND() or LEN""" + """Defines a BUILTIN function e.g. INKEY$(), RND() or LEN""" def __init__(self, lineno, fname, type_=None, *operands): assert isinstance(lineno, int) @@ -22,6 +24,7 @@ def __init__(self, lineno, fname, type_=None, *operands): self.lineno = lineno self.fname = fname self.type_ = type_ + self.discard_result = False # Whether to discard the return value of the function @property def type_(self): @@ -61,7 +64,14 @@ def size(self): return self.type_.size @classmethod - def make_node(cls, lineno, fname, func=None, type_=None, *operands): + def make_node( + cls, + lineno: int, + fname: str, + func: Callable | None = None, + type_: SymbolTYPE | None = None, + *operands, + ) -> Self | SymbolNUMBER: """Creates a node for a unary operation. E.g. -x or LEN(a$) Parameters: diff --git a/src/symbols/call.py b/src/symbols/call.py index ff7d9f13a..9cb789e20 100644 --- a/src/symbols/call.py +++ b/src/symbols/call.py @@ -1,14 +1,12 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from collections.abc import Iterable -from typing import Optional +from typing import Self import src.api.global_ as gl from src.api import check, errmsg @@ -35,7 +33,7 @@ class SymbolCALL(Symbol): def __init__(self, entry: SymbolID, arglist: Iterable[SymbolARGUMENT], lineno: int, filename: str): assert isinstance(entry, SymbolID) assert all(isinstance(x, SymbolARGUMENT) for x in arglist) - assert entry.class_ in (CLASS.array, CLASS.function, CLASS.sub, CLASS.unknown) + assert entry.class_ in {CLASS.array, CLASS.function, CLASS.sub, CLASS.unknown} super().__init__() self.entry = entry @@ -48,9 +46,16 @@ def __init__(self, entry: SymbolID, arglist: Iterable[SymbolARGUMENT], lineno: i for arg, param in zip(arglist, ref.params): # Sets dependency graph for each argument -> parameter if arg.value is not None: arg.value.add_required_symbol(param) + if ( + isinstance(arg.value, SymbolID) + and arg.value.class_ == CLASS.array + and param.class_ == CLASS.array + and param.ref.is_dynamically_accessed is True + ): + arg.value.ref.is_dynamically_accessed = True @property - def entry(self): + def entry(self) -> SymbolID: return self.children[0] @entry.setter @@ -62,13 +67,13 @@ def entry(self, value: SymbolID): self.children[0] = value @property - def args(self): + def args(self) -> SymbolARGLIST: return self.children[1] @args.setter - def args(self, value): + def args(self, value: SymbolARGLIST): assert isinstance(value, SymbolARGLIST) - if self.children is None or not self.children: + if not self.children: self.children = [None] if len(self.children) < 2: @@ -82,7 +87,7 @@ def type_(self): return self.entry.type_ @classmethod - def make_node(cls, id_: str, params, lineno: int, filename: str) -> Optional["SymbolCALL"]: + def make_node(cls, id_: str, params, lineno: int, filename: str) -> Self | None: """This will return an AST node for a function/procedure call.""" assert isinstance(params, SymbolARGLIST) entry = gl.SYMBOL_TABLE.access_func(id_, lineno) @@ -96,9 +101,10 @@ def make_node(cls, id_: str, params, lineno: int, filename: str) -> Optional["Sy if entry.declared and not entry.forwarded: check.check_call_arguments(lineno, id_, params, filename) - else: # All functions goes to global scope by default + else: # All functions go to global scope by default if entry.token != "FUNCTION": entry = entry.to_function(lineno) + gl.SYMBOL_TABLE.move_to_global_scope(id_) result = cls(entry, params, lineno, filename) gl.FUNCTION_CALLS.append(result) diff --git a/src/symbols/constexpr.py b/src/symbols/constexpr.py index 123abdbe6..c94dbde6d 100644 --- a/src/symbols/constexpr.py +++ b/src/symbols/constexpr.py @@ -1,11 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import src import src.api.global_ as gl diff --git a/src/symbols/funccall.py b/src/symbols/funccall.py index fc973fb42..8fa204778 100644 --- a/src/symbols/funccall.py +++ b/src/symbols/funccall.py @@ -1,11 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .call import SymbolCALL diff --git a/src/symbols/funcdecl.py b/src/symbols/funcdecl.py index 7517fca16..4a7d763e1 100644 --- a/src/symbols/funcdecl.py +++ b/src/symbols/funcdecl.py @@ -1,16 +1,17 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- +from typing import Self + import src.api.symboltable.scope from src.api import global_ from src.api.constants import CLASS from src.symbols.id_ import SymbolID from src.symbols.symbol_ import Symbol +from src.symbols.type_ import SymbolTYPEREF class SymbolFUNCDECL(Symbol): @@ -69,7 +70,7 @@ def mangled(self): return self.entry.mangled @classmethod - def make_node(cls, func_name: str, lineno: int, class_: CLASS, type_=None): + def make_node(cls, func_name: str, lineno: int, class_: CLASS, type_: SymbolTYPEREF | None = None) -> Self | None: """This will return a node with the symbol as a function.""" assert class_ in (CLASS.sub, CLASS.function) entry = global_.SYMBOL_TABLE.declare_func(func_name, lineno, type_=type_, class_=class_) diff --git a/src/symbols/id_/__init__.py b/src/symbols/id_/__init__.py index d53bdad37..6cd5e0ac6 100644 --- a/src/symbols/id_/__init__.py +++ b/src/symbols/id_/__init__.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.symbols.id_._id import SymbolID __all__ = ["SymbolID"] diff --git a/src/symbols/id_/_id.py b/src/symbols/id_/_id.py index 0fce2076c..e233a9851 100644 --- a/src/symbols/id_/_id.py +++ b/src/symbols/id_/_id.py @@ -1,11 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from __future__ import annotations @@ -18,7 +16,7 @@ from src.symbols.id_ import ref from src.symbols.id_.interface import SymbolIdABC from src.symbols.symbol_ import Symbol -from src.symbols.type_ import SymbolTYPE +from src.symbols.type_ import SymbolTYPEREF, Type # ---------------------------------------------------------------------- # Identifier Symbol object @@ -49,23 +47,23 @@ def __init__( name: str, lineno: int, filename: str = None, - type_: SymbolTYPE | None = None, + type_ref: SymbolTYPEREF | None = None, class_: CLASS = CLASS.unknown, ): - super().__init__(name=name, lineno=lineno, filename=filename, type_=type_, class_=class_) + super().__init__(name=name, lineno=lineno, filename=filename, type_=type_ref, class_=class_) assert class_ in (CLASS.const, CLASS.label, CLASS.var, CLASS.unknown) - self.name = name # This value will be modified later removing the trailing sigil ($) if used. + self.name = name # This value will be modified later, removing the trailing sigil ($) if used. self.original_name = name # This value will always contain the original name, preserving the sigil if used self.filename = global_.FILENAME if filename is None else filename # In which file was first used self.lineno = lineno # In which line was first used self.mangled = f"{global_.MANGLE_CHR}{name}" # This value will be overridden later self.declared = False # if explicitly declared (DIM var AS ) - self.type_ = type_ # if None => unknown type (yet) + self.type_ = type_ref # Typing annotation. If None => unknown type (yet) self.caseins = OPTIONS.case_insensitive # Whether this ID is case-insensitive or not self.scope = SCOPE.global_ # One of 'global', 'parameter', 'local' self.scope_ref: Any | None = None # TODO: type Scope | None # Scope object this ID lives in - self.addr = None # If not None, the address of this symbol in memory (string, cam be an expr like "_addr1 + 2") + self.addr: str | None = None # If set, the address of this symbol in memory (can be an expr like "_addr1 + 2") self._ref: ref.SymbolRef = ref.SymbolRef(self) self.has_address: bool | None = None # Whether this ID exist in memory or not @@ -97,12 +95,12 @@ def t(self) -> str: return self._ref.t @property - def type_(self): + def type_(self) -> SymbolTYPEREF | None: return self._type @type_.setter - def type_(self, value: SymbolTYPE | None): - assert value is None or isinstance(value, SymbolTYPE) + def type_(self, value: SymbolTYPEREF | None): + assert isinstance(value, SymbolTYPEREF | None) self._type = value @property @@ -124,6 +122,11 @@ def to_var(self, default_value: Symbol | None = None): assert self.has_address or self.has_address is None self.has_address = True + if self.type_ is None: + if default_value is not None and default_value.type_ is not None: + self.type_ = SymbolTYPEREF(default_value.type_.final, lineno=self.lineno) + else: + self.type_ = SymbolTYPEREF(Type.unknown, lineno=self.lineno) if self.class_ == CLASS.var: self._ref.default_value = default_value diff --git a/src/symbols/id_/interface.py b/src/symbols/id_/interface.py index 85983c9ea..fbe9ae467 100644 --- a/src/symbols/id_/interface.py +++ b/src/symbols/id_/interface.py @@ -1,8 +1,15 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from abc import ABC, abstractmethod from src.api.constants import CLASS, SCOPE from src.symbols.symbol_ import Symbol -from src.symbols.type_ import SymbolTYPE +from src.symbols.type_ import SymbolTYPEREF class SymbolIdABC(Symbol, ABC): @@ -19,14 +26,14 @@ def __init__( name: str, lineno: int, filename: str = None, - type_: SymbolTYPE | None = None, + type_: SymbolTYPEREF | None = None, class_: CLASS = CLASS.unknown, ): super().__init__() @property @abstractmethod - def type_(self) -> SymbolTYPE | None: + def type_(self) -> SymbolTYPEREF | None: pass @property diff --git a/src/symbols/id_/ref/__init__.py b/src/symbols/id_/ref/__init__.py index c45fe3a4d..447948be9 100644 --- a/src/symbols/id_/ref/__init__.py +++ b/src/symbols/id_/ref/__init__.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + __all__ = "ArrayRef", "ConstRef", "FuncRef", "LabelRef", "SymbolRef", "VarRef" from src.symbols.id_.ref.arrayref import ArrayRef diff --git a/src/symbols/id_/ref/arrayref.py b/src/symbols/id_/ref/arrayref.py index 1d315ce59..28c76bebd 100644 --- a/src/symbols/id_/ref/arrayref.py +++ b/src/symbols/id_/ref/arrayref.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import functools from src.api import global_ as gl diff --git a/src/symbols/id_/ref/constref.py b/src/symbols/id_/ref/constref.py index c57d50089..087c44aa5 100644 --- a/src/symbols/id_/ref/constref.py +++ b/src/symbols/id_/ref/constref.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api.constants import CLASS from src.symbols.id_.interface import SymbolIdABC as SymbolID from src.symbols.id_.ref.symbolref import SymbolRef diff --git a/src/symbols/id_/ref/funcref.py b/src/symbols/id_/ref/funcref.py index 8d4e3a231..cd26e86e8 100644 --- a/src/symbols/id_/ref/funcref.py +++ b/src/symbols/id_/ref/funcref.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api.constants import CLASS, CONVENTION from src.symbols.block import SymbolBLOCK from src.symbols.id_.interface import SymbolIdABC as SymbolID diff --git a/src/symbols/id_/ref/labelref.py b/src/symbols/id_/ref/labelref.py index 654dfbe23..810deefbb 100644 --- a/src/symbols/id_/ref/labelref.py +++ b/src/symbols/id_/ref/labelref.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api import global_ from src.api.constants import CLASS from src.symbols.id_.interface import SymbolIdABC as SymbolID diff --git a/src/symbols/id_/ref/symbolref.py b/src/symbols/id_/ref/symbolref.py index fd88a0554..588322e6a 100644 --- a/src/symbols/id_/ref/symbolref.py +++ b/src/symbols/id_/ref/symbolref.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api import global_ from src.api.constants import CLASS from src.symbols.id_.interface import SymbolIdABC as SymbolID diff --git a/src/symbols/id_/ref/varref.py b/src/symbols/id_/ref/varref.py index 97cb1cd61..482916945 100644 --- a/src/symbols/id_/ref/varref.py +++ b/src/symbols/id_/ref/varref.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import src.api.global_ as gl from src.api.constants import CLASS, SCOPE from src.symbols.id_.interface import SymbolIdABC as SymbolID @@ -11,7 +18,7 @@ class VarRef(SymbolRef): def __init__(self, parent: SymbolID, default_value: Symbol | None = None): super().__init__(parent) - self.default_value = default_value # If defined, it be initialized with this value (Arrays = List of Bytes) + self.default_value = default_value # If defined, it is initialized with this value (Arrays = List of Bytes) self.offset: str | None = None # If local variable or parameter, +/- offset from top of the stack self.byref = False self.alias = None # If not None, this var is an alias of another @@ -32,7 +39,7 @@ def t(self) -> str: if self.parent.type_ is None or not self.parent.type_.is_dynamic: return self._t - return f"${self._t}" # Local string variables (and parameters) use '$' (see backend) + return f"${self._t}" # Local string variables (and ByVal parameters) use '$' (see backend) @property def size(self) -> int: diff --git a/src/symbols/nop.py b/src/symbols/nop.py index 0b1e02225..e645979ad 100644 --- a/src/symbols/nop.py +++ b/src/symbols/nop.py @@ -1,11 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .block import SymbolBLOCK diff --git a/src/symbols/number.py b/src/symbols/number.py index e4b766ba9..eecf21f0e 100644 --- a/src/symbols/number.py +++ b/src/symbols/number.py @@ -1,12 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- - +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.api.constants import CLASS from src.symbols import sym diff --git a/src/symbols/paramlist.py b/src/symbols/paramlist.py index b3e2b285e..339c129a5 100644 --- a/src/symbols/paramlist.py +++ b/src/symbols/paramlist.py @@ -1,11 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.symbols.id_.interface import SymbolIdABC as SymbolID from src.symbols.symbol_ import Symbol diff --git a/src/symbols/sentence.py b/src/symbols/sentence.py index 5b90fd443..d2153e088 100644 --- a/src/symbols/sentence.py +++ b/src/symbols/sentence.py @@ -1,11 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License v3 -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.symbols.symbol_ import Symbol @@ -31,5 +29,5 @@ def args(self): @property def token(self): - """Sentence takes it's token from the keyword not from it's name""" + """Sentence takes its token from the keyword not from its name""" return self.keyword diff --git a/src/symbols/string_.py b/src/symbols/string_.py index 83b8e9839..0a9c0bc89 100644 --- a/src/symbols/string_.py +++ b/src/symbols/string_.py @@ -1,11 +1,10 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from __future__ import annotations from src.api.constants import CLASS diff --git a/src/symbols/strslice.py b/src/symbols/strslice.py index 8435852bc..ff0f4e331 100644 --- a/src/symbols/strslice.py +++ b/src/symbols/strslice.py @@ -1,11 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import src.api.global_ as gl from src.api import check, config diff --git a/src/symbols/sym.py b/src/symbols/sym.py index 4188bed34..faf9d5776 100644 --- a/src/symbols/sym.py +++ b/src/symbols/sym.py @@ -1,11 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.symbols.arglist import SymbolARGLIST as ARGLIST from src.symbols.argument import SymbolARGUMENT as ARGUMENT @@ -37,6 +35,9 @@ from src.symbols.unary import SymbolUNARY as UNARY from src.symbols.vardecl import SymbolVARDECL as VARDECL +EXPR = CONSTEXPR | FUNCCALL | BINARY | BUILTIN | UNARY | ID | NUMBER | STRING | STRSLICE | ARRAYLOAD +TYPING = TYPE | TYPEREF + __all__ = [ "ARGLIST", "ARGUMENT", @@ -52,6 +53,7 @@ "BUILTIN", "CALL", "CONSTEXPR", + "EXPR", "FUNCCALL", "FUNCDECL", "ID", @@ -65,6 +67,7 @@ "TYPE", "TYPECAST", "TYPEREF", + "TYPING", "UNARY", "VARDECL", ] diff --git a/src/symbols/symbol_.py b/src/symbols/symbol_.py index 7f63a3aae..41c3c2df4 100644 --- a/src/symbols/symbol_.py +++ b/src/symbols/symbol_.py @@ -1,17 +1,16 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from __future__ import annotations from collections import Counter import src.api.global_ -from src.ast import Ast, Tree +from src.ast_ import Ast, Tree class Symbol(Ast): @@ -80,10 +79,9 @@ def t(self) -> str: def is_needed(self) -> bool: return len(self.required_by) > 0 - def get_parent(self, type_) -> Tree | None: - """Traverse parents until finding one - of type type_ or None if not found. - If a cycle is detected an undetermined value is returned as parent. + def get_parent(self, type_: type[Tree]) -> Tree | None: + """Traverse parents until finding one of type type_ or None if not found. + If a cycle is detected, None is returned. """ visited = set() parent = self.parent @@ -91,6 +89,7 @@ def get_parent(self, type_) -> Tree | None: visited.add(parent) if isinstance(parent, type_): return parent + parent = parent.parent - return parent + return None diff --git a/src/symbols/type_.py b/src/symbols/type_.py index 02fbb49fc..a443de414 100644 --- a/src/symbols/type_.py +++ b/src/symbols/type_.py @@ -1,34 +1,39 @@ -# vim: ts=4:et:sw=4: +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- +from __future__ import annotations -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +from typing import Any, Final +from src.api import global_ as gl from src.api.config import OPTIONS from src.api.constants import CLASS, TYPE from src.api.decorator import classproperty from .symbol_ import Symbol +__all__: Final[tuple[str]] = "SymbolTYPE", "SymbolBASICTYPE", "SymbolTYPEALIAS", "SymbolTYPEREF", "Type" + class SymbolTYPE(Symbol): """A Type definition. Defines a type, - both user defined or basic ones. + both user-defined or basic ones. """ - def __init__(self, name: str, lineno: int, *children): + def __init__(self, name: str, namespace: str, size: int, lineno: int, filename: str = ""): # All children (if any) must be SymbolTYPE - assert all(isinstance(x, SymbolTYPE) for x in children) - super().__init__(*children) + super().__init__() self.name = name # typename + self.namespace = namespace # Namespace where this type was defined + self._size = size self.lineno = lineno # The line the type was defined. Line 0 = basic type - self.final = self # self.final always return the original aliased type (if this type is an alias) self.caseins = OPTIONS.case_insensitive # Whether this ID is case-insensitive or not self.class_ = CLASS.type self.accessed = False # Whether this type has been used or not + self.filename = filename or gl.FILENAME # Filename where this type was defined def __repr__(self): return "%s(%s)" % (self.token, str(self)) @@ -36,190 +41,181 @@ def __repr__(self): def __str__(self): return self.name - @property - def size(self): - return sum(x.size for x in self.children) + def __hash__(self): + return id(self) @property - def is_basic(self): - """Whether this is a basic (canonical) type or not.""" - if len(self.children) == 1: - return self.children[0].is_basic - - return False + def final(self) -> SymbolTYPE: + """For any aliased type, return the final type.""" + return self @property - def is_signed(self): - if self is not self.final: - return self.final.is_signed + def canonical_name(self) -> str: + canonical_name = f"{self.namespace}{gl.NAMESPACE_SEPARATOR}{self.name}" + return canonical_name.lower() if self.caseins else canonical_name - if len(self.children) != 1: - return False - - return self.children[0].is_signed + @property + def is_primitive(self) -> bool: + """Whether this is a primitive type or not.""" + return True # Subclasses must override this @property - def is_dynamic(self): - """True if this type uses dynamic (Heap) memory. - e.g. strings or dynamic arrays - """ - if self is not self.final: - return self.final.is_dynamic + def size(self) -> int: + return self._size - return any(x.is_dynamic for x in self.children) + @property + def is_basic(self) -> bool: + """Whether this is a basic (canonical) type or not.""" + return False @property - def is_alias(self): + def is_alias(self) -> bool: """Whether this is an alias of another type or not.""" return False - def __eq__(self, other): - assert isinstance(other, SymbolTYPE), f"Invalid operand '{other}':{type(other)}" - - if self is not self.final: - return self.final == other - - other = other.final # remove alias - - if other.is_basic: - return other == self - - if len(self.children) != len(other.children): - if len(self.children) == 1 and not other.children: - return self.children[0] == other - if len(other.children) == 1 and not self.children: - return other.children[0] == self - return False - - return all(i == j for i, j in zip(self.children, other.children)) - - def __ne__(self, other): - assert isinstance(other, SymbolTYPE) - return not (self == other) - - def __nonzero__(self): - return self.__bool__() + @property + def is_dynamic(self) -> bool: + """Whether this type is dynamic in memory (allocated in the HEAP).""" + return False - def __bool__(self): - if self is not self.final: - return bool(self.final) + def __eq__(self, other: Any) -> bool: + """Nominal equality""" + if not isinstance(other, SymbolTYPE): + return NotImplemented - return any(x for x in self.children) + return self.canonical_name == other.canonical_name class SymbolBASICTYPE(SymbolTYPE): """Defines a basic type (Ubyte, Byte, etc.) Basic (default) types are defined upon start and are case-insensitive. - If name is None or '', default typename from TYPES.to_string will be used. """ - def __init__(self, type_: TYPE, name: str = None): + TYPES: Final[dict[TYPE, SymbolBASICTYPE]] = {} + + def __init__(self, type_: TYPE): """type_ = Internal representation (e.g. TYPE.ubyte)""" assert TYPE.is_valid(type_) - if not name: - name = TYPE.to_string(type_) + name = TYPE.to_string(type_) - super().__init__(name, 0) - self.type_ = type_ + super().__init__(name=name, size=TYPE.size(type_), namespace="", lineno=0, filename="") + self._type = type_ - @property - def size(self): - return TYPE.size(self.type_) + def __new__(cls, *args, **kwargs) -> SymbolBASICTYPE: + """Ensures the same basic type is returned for the same TYPE + if it's already been created.""" + return cls.TYPES.setdefault(args[0], super().__new__(cls)) @property - def is_basic(self): + def is_basic(self) -> bool: """Whether this is a basic (canonical) type or not.""" return True @property - def is_signed(self): - return TYPE.is_signed(self.type_) + def is_signed(self) -> bool: + return TYPE.is_signed(self._type) def to_signed(self): """Returns another instance with the signed equivalent of this type. """ - return SymbolBASICTYPE(TYPE.to_signed(self.type_)) + return SymbolBASICTYPE(TYPE.to_signed(self._type)) @property - def is_dynamic(self): - return self.type_ == TYPE.string - - def __hash__(self): - return hash(self.type_) - - def __eq__(self, other): - if self is not self.final: - return self.final == other - - other = other.final # remove alias - if other.is_basic: # for both basic types, just compare - return self.type_ == other.type_ - - assert other.children # must be not empty - if len(other.children) > 1: # Different size - return False + def is_dynamic(self) -> bool: + return self._type == TYPE.string - return self == other.children[0] - - def __bool__(self): - return self.type_ != TYPE.unknown + def __bool__(self) -> bool: + return self._type != TYPE.unknown class SymbolTYPEALIAS(SymbolTYPE): - """Defines a type which is alias of another""" + """Defines a type which is an alias of another""" - def __init__(self, name, lineno: int, alias: SymbolTYPE): + def __init__(self, name: str, namespace: str, alias: SymbolTYPE, lineno: int, filename: str = ""): assert isinstance(alias, SymbolTYPE) - super().__init__(name, lineno, alias) - self.final = alias.final + filename = filename or gl.FILENAME + super().__init__(name=name, namespace=namespace, lineno=lineno, size=alias.size, filename=filename) + self._base = alias @property - def is_alias(self): + def is_alias(self) -> bool: """Whether this is an alias of another type or not.""" return True @property - def size(self): - return self.final.size + def final(self) -> SymbolTYPE: + return self._base.final @property - def is_basic(self): - return self.final.is_basic + def is_basic(self) -> bool: + return self._base.is_basic @property - def alias(self): - return self.children[0] + def base(self) -> SymbolTYPE: + return self._base @property def to_signed(self): assert self.is_basic - return self.final.to_signed() + return self._base.to_signed() + + @property + def is_dynamic(self) -> bool: + return self._base.is_dynamic -class SymbolTYPEREF(SymbolTYPEALIAS): - """Creates a Type reference or usage. - Eg. DIM a As Integer - In this case, the Integer type is accessed. - It's an alias type, containing just the - original Type definition (SymbolTYPE), the - the lineno it is currently being accessed, - and if it was implicitly inferred or explicitly declared. +class SymbolTYPEREF(Symbol): """ + Describes a Type annotation (file, line number). + Eg. DIM a As Integer + In this case, the variable a is annotated with the type Integer. + """ + + def __init__(self, type_: SymbolTYPE, lineno: int, filename: str = "", *, implicit: bool = False): + assert isinstance(type_, SymbolTYPE | SymbolTYPEREF) + super().__init__(type_) + self.implicit = implicit # Whether this annotation was implicit or not + self.lineno = lineno # Line number where this annotation was defined + self.filename = filename or gl.FILENAME # Filename where this annotation was defined - def __init__(self, type_: SymbolTYPE, lineno: int, implicit: bool = False): - assert isinstance(type_, SymbolTYPE) - super().__init__(type_.name, lineno, type_) - self.implicit = implicit + @property + def type_(self) -> SymbolTYPE: + return self.children[0] + + @property + def name(self) -> str: + return self.type_.name + + @property + def size(self) -> int: + return self.type_.size def to_signed(self): - assert self.is_basic - return self.final.to_signed() + type_ = self.type_ + assert isinstance(type_, SymbolBASICTYPE) + return type_.to_signed() @property - def type_(self): - assert self.is_basic - return self.final.type_ + def is_basic(self) -> bool: + return self.type_.is_basic + + @property + def is_dynamic(self) -> bool: + return self.type_.is_dynamic + + @property + def final(self) -> SymbolTYPE: + return self.type_.final + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, SymbolTYPEREF | SymbolTYPE): + raise NotImplementedError(f"Invalid operand '{other}':{type(other)}") + + if isinstance(other, SymbolTYPEREF): + return self.type_ == other.type_ + + return self.type_ == other class Type: @@ -243,14 +239,12 @@ class Type: _by_name = {x.name: x for x in types} - @staticmethod - def size(t: SymbolTYPE) -> int: - assert isinstance(t, SymbolTYPE) + @classmethod + def size(cls, t: SymbolTYPE) -> int: return t.size - @staticmethod - def to_string(t: SymbolTYPE) -> str: - assert isinstance(t, SymbolTYPE) + @classmethod + def to_string(cls, t: SymbolTYPE) -> str: return t.name @classmethod @@ -281,27 +275,27 @@ def numbers(cls) -> set[SymbolBASICTYPE]: @classmethod def is_numeric(cls, t: SymbolTYPE) -> bool: assert isinstance(t, SymbolTYPE) - return t.final in cls.numbers + return t.is_basic and t.final in cls.numbers @classmethod def is_signed(cls, t: SymbolTYPE) -> bool: assert isinstance(t, SymbolTYPE) - return t.final in cls.signed + return t.is_basic and t.final in cls.signed @classmethod def is_unsigned(cls, t: SymbolTYPE) -> bool: assert isinstance(t, SymbolTYPE) - return t.final in cls.unsigned + return t.is_basic and t.final in cls.unsigned @classmethod def is_integral(cls, t: SymbolTYPE) -> bool: assert isinstance(t, SymbolTYPE) - return t.final in cls.integrals + return t.is_basic and t.final in cls.integrals @classmethod def is_decimal(cls, t: SymbolTYPE) -> bool: assert isinstance(t, SymbolTYPE) - return t.final in cls.decimals + return t.is_basic and t.final in cls.decimals @classmethod def is_string(cls, t: SymbolTYPE) -> bool: @@ -312,11 +306,13 @@ def is_string(cls, t: SymbolTYPE) -> bool: def to_signed(cls, t: SymbolTYPE): """Return signed type or equivalent""" assert isinstance(t, SymbolTYPE) - t = t.final - assert t.is_basic + if not isinstance(t, SymbolBASICTYPE): + return cls.unknown + if cls.is_unsigned(t): - # FIXME - return {cls.boolean: cls.byte_, cls.ubyte: cls.byte_, cls.uinteger: cls.integer, cls.ulong: cls.long_}[t] # type:ignore[index] + return {cls.boolean: cls.byte_, cls.ubyte: cls.byte_, cls.uinteger: cls.integer, cls.ulong: cls.long_}[t] + if cls.is_signed(t) or cls.is_decimal(t): return t + return cls.unknown diff --git a/src/symbols/typecast.py b/src/symbols/typecast.py index db0a98a46..74047c553 100644 --- a/src/symbols/typecast.py +++ b/src/symbols/typecast.py @@ -1,11 +1,10 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- +import math from src.api import check, errmsg from src.api.errmsg import error @@ -36,7 +35,7 @@ def operand(self, operand_): self.children[0] = operand_ @classmethod - def make_node(cls, new_type: SymbolTYPE, node: Symbol, lineno: int): + def make_node(cls, new_type: SymbolTYPE, node: Symbol | None, lineno: int) -> Symbol | None: """Creates a node containing the type cast of the given one. If new_type == node.type, then nothing is done, and the same node is @@ -44,7 +43,7 @@ def make_node(cls, new_type: SymbolTYPE, node: Symbol, lineno: int): Returns None on failure (and calls syntax_error) """ - assert isinstance(new_type, SymbolTYPE) + assert isinstance(new_type, SymbolTYPE), f"{new_type} is not a SymbolTYPE" # None (null) means the given AST node is empty (usually an error) if node is None: @@ -85,7 +84,7 @@ def make_node(cls, new_type: SymbolTYPE, node: Symbol, lineno: int): # It's a number. So let's convert it directly if check.is_const(node): - node = SymbolNUMBER(node.value, node.lineno, node.type_) + node = SymbolNUMBER(node.value, node.lineno, node.type_.final) if new_type == TYPE.boolean: node.value = int(bool(node.value)) @@ -93,7 +92,8 @@ def make_node(cls, new_type: SymbolTYPE, node: Symbol, lineno: int): elif new_type.is_basic and not TYPE.is_integral(new_type): # not an integer node.value = float(node.value) else: # It's an integer - new_val = int(node.value) & ((1 << (8 * new_type.size)) - 1) # Mask it + # ZX Spectrum ROM always truncates to -Infinity, so we do the same using floor() + new_val = math.floor(node.value) & ((1 << (8 * new_type.size)) - 1) # Mask it if node.value >= 0 and node.value != new_val: errmsg.warning_conversion_lose_digits(node.lineno) diff --git a/src/symbols/unary.py b/src/symbols/unary.py index 5021c9d43..a4651cdd0 100644 --- a/src/symbols/unary.py +++ b/src/symbols/unary.py @@ -1,11 +1,11 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- +from collections.abc import Callable +from typing import Self from src.api import check from src.symbols.number import SymbolNUMBER @@ -55,7 +55,14 @@ def __repr__(self): return "(%s: %s)" % (self.operator, self.operand) @classmethod - def make_node(cls, lineno, operator, operand, func=None, type_=None): + def make_node( + cls, + lineno: int, + operator: str, + operand: Symbol | None, + func: Callable | None = None, + type_: SymbolTYPE | None = None, + ) -> Self | SymbolNUMBER | SymbolSTRING: """Creates a node for a unary operation. E.g. -x or LEN(a$) Parameters: @@ -75,6 +82,8 @@ def make_node(cls, lineno, operator, operand, func=None, type_=None): if type_ is None: type_ = operand.type_ + type_ = type_.final + if operator == "MINUS": if not type_.is_signed: type_ = type_.to_signed() diff --git a/src/symbols/vardecl.py b/src/symbols/vardecl.py index d0bb32f6b..30b7e537a 100644 --- a/src/symbols/vardecl.py +++ b/src/symbols/vardecl.py @@ -1,11 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.symbols.symbol_ import Symbol diff --git a/src/zxbasm/__init__.py b/src/zxbasm/__init__.py index 3f371bc09..45476f4fb 100644 --- a/src/zxbasm/__init__.py +++ b/src/zxbasm/__init__.py @@ -1 +1,8 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from .zxbasm import main # noqa diff --git a/src/zxbasm/asm.py b/src/zxbasm/asm.py index eab43e1e6..3782720f4 100644 --- a/src/zxbasm/asm.py +++ b/src/zxbasm/asm.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from typing import Any, NamedTuple from src.api import errmsg diff --git a/src/zxbasm/asm_instruction.py b/src/zxbasm/asm_instruction.py index c880c553c..42e4c4fa2 100644 --- a/src/zxbasm/asm_instruction.py +++ b/src/zxbasm/asm_instruction.py @@ -1,4 +1,9 @@ -# vim: ts=4:et:sw=4 +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import re diff --git a/src/zxbasm/asmlex.py b/src/zxbasm/asmlex.py index 36366ac05..d3a782ef9 100755 --- a/src/zxbasm/asmlex.py +++ b/src/zxbasm/asmlex.py @@ -1,21 +1,17 @@ #!/usr/bin/env python3 -# vim:ts=4:et: -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# -# This is the Lexer for the zxbasm (ZXBasic Assembler) -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import sys -from src.api import global_ +from src.api import global_, lex from src.api.config import OPTIONS from src.api.errmsg import error -from src.ply import lex _tokens: tuple[str, ...] = ( "STRING", diff --git a/src/zxbasm/asmparse.py b/src/zxbasm/asmparse.py index e1f8518b8..58a7ff267 100755 --- a/src/zxbasm/asmparse.py +++ b/src/zxbasm/asmparse.py @@ -1,16 +1,12 @@ -#!/usr/bin/env python -# vim: et:ts=4:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# -# This is the Parser for the ZXBASM (ZXBasic Assembler) -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2026 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import os +from typing import Any import src.api.utils from src import outfmt @@ -18,7 +14,6 @@ from src.api.config import OPTIONS from src.api.debug import __DEBUG__ from src.api.errmsg import error, warning -from src.ply import yacc from src.zxbasm import asmlex, basic from src.zxbasm import global_ as asm_gl from src.zxbasm.asm import Asm, Container @@ -28,6 +23,10 @@ from src.zxbasm.memory import Memory from src.zxbpp import zxbpp +from .asmparse_standalone import Lark_StandAlone as BaseLarkStandAlone +from .asmparse_standalone import Lexer, Token, Transformer, UnexpectedInput +from .asmparse_zxnext_standalone import Lark_StandAlone as ZXNextLarkStandAlone + LEXER = asmlex.Lexer() ORG = 0 # Origin of CODE @@ -37,14 +36,6 @@ REGS16 = {"BC", "DE", "HL", "SP", "IX", "IY"} # 16 Bits registers -precedence = ( - ("left", "RSHIFT", "LSHIFT", "BAND", "BOR", "BXOR"), - ("left", "PLUS", "MINUS"), - ("left", "MUL", "DIV", "MOD"), - ("right", "POW"), - ("right", "UMINUS"), -) - def init(): """Initializes this module""" @@ -65,931 +56,465 @@ def init(): asm_gl.NAMESPACE = asm_gl.GLOBAL_NAMESPACE -# -------- GRAMMAR RULES for the preprocessor --------- - - -def p_start(p): - """start : program - | program endline - """ - - -def p_program_endline(p): - """endline : END NEWLINE - | endline NEWLINE - """ - - -def p_program_endline2(p): - """endline : END expr NEWLINE - | END pexpr NEWLINE - """ - global AUTORUN_ADDR - AUTORUN_ADDR = p[2].eval() - - -def p_program(p): - """program : line""" - - -def p_program_line(p): - """program : program line""" - - -def p_def_label(p): - """line : ID EQU expr NEWLINE - | ID EQU pexpr NEWLINE - """ - p[0] = None - MEMORY.declare_label(p[1], p.lineno(1), p[3]) - - -def p_line_asm(p): - """line : asms NEWLINE - | asms CO NEWLINE - """ - - -def p_asms_empty(p): - """asms :""" - p[0] = MEMORY.org - - -def p_asms_asm(p): - """asms : asm""" - p[0] = MEMORY.org - asm = p[1] - if isinstance(asm, Asm): - MEMORY.add_instruction(asm) - - -def p_asms_asms_asm(p): - """asms : asms CO asm""" - p[0] = p[1] - asm = p[3] - if isinstance(asm, Asm): - MEMORY.add_instruction(asm) +class AsmToken(Token): + pass -def p_asm_label(p): - """asm : ID - | INTEGER - """ - MEMORY.declare_label(str(p[1]), p.lineno(1)) - - -def p_asm_ld8(p): - """asm : LD reg8 COMMA reg8_hl - | LD reg8_hl COMMA reg8 - | LD reg8 COMMA reg8 - | LD SP COMMA HL - | LD SP COMMA reg16i - | LD A COMMA reg8 - | LD reg8 COMMA A - | LD reg8_hl COMMA A - | LD A COMMA reg8_hl - | LD A COMMA A - | LD A COMMA I - | LD I COMMA A - | LD A COMMA R - | LD R COMMA A - | LD A COMMA reg8i - | LD reg8i COMMA A - | LD reg8 COMMA reg8i - | LD reg8i COMMA regBCDE - | LD reg8i COMMA reg8i - """ - if p[2] in ("H", "L") and p[4] in ("IXH", "IXL", "IYH", "IYL"): - p[0] = None - error(p.lineno(0), "Unexpected token '%s'" % p[4]) - else: - p[0] = Asm(p.lineno(1), "LD %s,%s" % (p[2], p[4])) - - -def p_LDa(p): # Remaining LD A,... and LD...,A instructions - """asm : LD A COMMA LP BC RP - | LD A COMMA LB BC RB - | LD A COMMA LP DE RP - | LD A COMMA LB DE RB - | LD LP BC RP COMMA A - | LD LB BC RB COMMA A - | LD LP DE RP COMMA A - | LD LB DE RB COMMA A - """ - p[0] = Asm(p.lineno(1), "LD " + "".join(x.replace("[", "(").replace("]", ")") for x in p[2:])) - +class AsmLarkLexerAdapter(Lexer): + def __init__(self, lexer_conf: Any) -> None: + pass -def p_PROC(p): - """asm : PROC""" - p[0] = None # Start of a PROC scope - MEMORY.enter_proc(p.lineno(1)) + def lex(self, data: Any, parser_state: Any = None) -> Any: # type: ignore[override] + lexer = data + while True: + if lexer.next_token is not None: + tok_type = lexer.next_token + lexer.next_token = None + t = AsmToken(tok_type, "", line=lexer.lineno, column=1) + yield t + continue + tok = lexer.token() + if tok is None: + break -def p_ENDP(p): - """asm : ENDP""" - p[0] = None # End of a PROC scope - MEMORY.exit_proc(p.lineno(1)) + t = AsmToken(tok.type, tok.value, line=tok.lineno, column=lexer.find_column(tok)) + yield t -def p_LOCAL(p): - """asm : LOCAL id_list""" - p[0] = None - for label, line in p[2]: - __DEBUG__("Setting label '%s' as local at line %i" % (label, line)) +class AsmTransformer(Transformer): + def start(self, items): + return items[0] - MEMORY.set_label(label, line, local=True) + def program(self, items): + return items[0] + def empty_program(self, items): + return None -def p_idlist(p): - """id_list : ID""" - p[0] = (Container(p[1], p.lineno(1)),) + def program_endline2(self, items): + global AUTORUN_ADDR + AUTORUN_ADDR = items[1].eval() + return items[0] + def def_label(self, items): + MEMORY.declare_label(items[0], items[0].line, items[2]) -def p_idlist_id(p): - """id_list : id_list COMMA ID""" - p[0] = p[1] + (Container(p[3], p.lineno(3)),) + def line_asm(self, items): + return None + def preprocessor_line(self, items): + return None -def p_DEFB(p): # Define bytes - """asm : DEFB expr_list""" - p[0] = Asm(p.lineno(1), "DEFB", p[2]) + def preproc_line_init(self, items): + global INITS + INITS.append(Container(items[0].strip('"'), items[0].line)) + def asms_empty(self, items): + return MEMORY.org -def p_DEFS(p): # Define bytes - """asm : DEFS number_list""" - if len(p[2]) > 2: - error(p.lineno(1), "too many arguments for DEFS") + def asms_asm(self, items): + asm = items[0] + if isinstance(asm, Asm): + MEMORY.add_instruction(asm) + return MEMORY.org - if len(p[2]) < 2: - num = Expr.makenode(Container(0, p.lineno(1))) # Defaults to 0 - p[2] = p[2] + (num,) + def asms_asms_asm(self, items): + asm = items[2] + if isinstance(asm, Asm): + MEMORY.add_instruction(asm) + return items[0] - p[0] = Asm(p.lineno(1), "DEFS", p[2]) + def asm_label(self, items): + MEMORY.declare_label(str(items[0]), items[0].line) + def asm_ld8(self, items): + if items[1] in ("H", "L") and items[3] in ("IXH", "IXL", "IYH", "IYL"): + error(items[0].line, "Unexpected token '%s'" % items[3]) + return None + return Asm(items[0].line, "LD %s,%s" % (items[1], items[3])) -def p_DEFW(p): # Define words - """asm : DEFW number_list""" - p[0] = Asm(p.lineno(1), "DEFW", p[2]) + def ld_a_instr(self, items): + return Asm(items[0].line, "LD " + "".join(str(x).replace("[", "(").replace("]", ")") for x in items[1:])) + def proc_scope(self, items): + MEMORY.enter_proc(items[0].line) -def p_expr_list_from_string(p): - """expr_list : STRING""" - p[0] = tuple(Expr.makenode(Container(ord(x), p.lineno(1))) for x in p[1]) + def endp_scope(self, items): + MEMORY.exit_proc(items[0].line) + def local_labels(self, items): + for label, line in items[1]: + MEMORY.set_label(label, line, local=True) -def p_expr_list_from_num(p): - """expr_list : expr - | pexpr - """ - p[0] = (p[1],) + def idlist(self, items): + return (Container(items[0], items[0].line),) + def idlist_id(self, items): + return items[0] + (Container(items[2], items[2].line),) -def p_expr_list_plus_expr(p): - """expr_list : expr_list COMMA expr - | expr_list COMMA pexpr - """ - p[0] = p[1] + (p[3],) + def defb_op(self, items): + return Asm(items[0].line, "DEFB", items[1]) + def defs_op(self, items): + num_list = items[1] + if len(num_list) > 2: + error(items[0].line, "too many arguments for DEFS") + if len(num_list) < 2: + num = Expr.makenode(Container(0, items[0].line)) + num_list = num_list + (num,) + return Asm(items[0].line, "DEFS", num_list) -def p_expr_list_plus_string(p): - """expr_list : expr_list COMMA STRING""" - p[0] = p[1] + tuple(Expr.makenode(Container(ord(x), p.lineno(3))) for x in p[3]) + def defw_op(self, items): + return Asm(items[0].line, "DEFW", items[1]) + def expr_list_from_string(self, items): + return tuple(Expr.makenode(Container(ord(x), items[0].line)) for x in items[0]) -def p_number_list(p): - """number_list : expr - | pexpr - """ - p[0] = (p[1],) + def expr_list_from_num(self, items): + return (items[0],) + def expr_list_plus_expr(self, items): + return items[0] + (items[2],) -def p_number_list_number(p): - """number_list : number_list COMMA expr - | number_list COMMA pexpr - """ - p[0] = p[1] + (p[3],) + def expr_list_plus_string(self, items): + return items[0] + tuple(Expr.makenode(Container(ord(x), items[2].line)) for x in items[2]) + def number_list(self, items): + return (items[0],) -def p_asm_ldind_r8(p): - """asm : LD reg8_I COMMA reg8 - | LD reg8_I COMMA A - """ - p[0] = Asm(p.lineno(1), "LD %s,%s" % (p[2][0], p[4]), p[2][1]) + def number_list_number(self, items): + return items[0] + (items[2],) + def asm_ldind_r8(self, items): + return Asm(items[0].line, "LD %s,%s" % (items[1][0], items[3]), items[1][1]) -def p_asm_ldr8_ind(p): - """asm : LD reg8 COMMA reg8_I - | LD A COMMA reg8_I - """ - p[0] = Asm(p.lineno(1), "LD %s,%s" % (p[2], p[4][0]), p[4][1]) + def asm_ldr8_ind(self, items): + return Asm(items[0].line, "LD %s,%s" % (items[1], items[3][0]), items[3][1]) + def reg8_hl(self, items): + return "(HL)" -def p_reg8_hl(p): - """reg8_hl : LP HL RP - | LB HL RB - """ - p[0] = "(HL)" - - -def p_ind8_I(p): - """reg8_I : LP IX expr RP - | LP IY expr RP - | LP IX PLUS pexpr RP - | LP IX MINUS pexpr RP - | LP IY PLUS pexpr RP - | LP IY MINUS pexpr RP - | LB IX expr RB - | LB IY expr RB - | LB IX PLUS pexpr RB - | LB IX MINUS pexpr RB - | LB IY PLUS pexpr RB - | LB IY MINUS pexpr RB - """ - if len(p) == 6: - expr = p[4] - sign = p[3] - else: - expr = p[3] - gen_ = expr.inorder() - first_expr = next(gen_, "") - if first_expr and first_expr.parent: - if len(first_expr.parent.children) == 2: - first_token = first_expr.symbol.item - else: - first_token = first_expr.parent.symbol.item + def ind8_i(self, items): + if len(items) == 5: + expr = items[3] + sign = items[2] else: - first_token = "" - if first_token not in ("-", "+"): - error(p.lineno(2), f"Unexpected token '{first_token}'. Expected '+' or '-'") - sign = "+" - - if sign == "-": - expr = Expr.makenode(Container(sign, p.lineno(2)), expr) - - p[0] = ("(%s+N)" % p[2], expr) - - -def p_ex_af_af(p): - """asm : EX AF COMMA AF APO""" - p[0] = Asm(p.lineno(1), "EX AF,AF'") - - -def p_ex_de_hl(p): - """asm : EX DE COMMA HL""" - p[0] = Asm(p.lineno(1), "EX DE,HL") - - -def p_org(p): - """asm : ORG expr - | ORG pexpr - """ - MEMORY.set_org(p[2].eval(), p.lineno(1)) - + expr = items[2] + gen_ = expr.inorder() + first_expr = next(gen_, "") + if first_expr and first_expr.parent: + if len(first_expr.parent.children) == 2: + first_token = first_expr.symbol.item + else: + first_token = first_expr.parent.symbol.item + else: + first_token = "" + if first_token not in ("-", "+"): + error(items[1].line, f"Unexpected token '{first_token}'. Expected '+' or '-'") + sign = "+" -def p_namespace(p): - """asm : NAMESPACE ID""" + if sign == "-": + expr = Expr.makenode(Container(sign, items[1].line), expr) - asm_gl.NAMESPACE = asm_gl.normalize_namespace(p[2]) - __DEBUG__("Setting namespace to " + (asm_gl.NAMESPACE or DOT), level=1) + return ("(%s+N)" % items[1], expr) + def ex_af_af(self, items): + return Asm(items[0].line, "EX AF,AF'") -def p_push_namespace(p): - """asm : PUSH NAMESPACE - | PUSH NAMESPACE ID - """ + def ex_de_hl(self, items): + return Asm(items[0].line, "EX DE,HL") - asm_gl.NAMESPACE_STACK.append(asm_gl.NAMESPACE) - asm_gl.NAMESPACE = asm_gl.normalize_namespace(p[3] if len(p) == 4 else asm_gl.NAMESPACE) + def org(self, items): + MEMORY.set_org(items[1].eval(), items[0].line) - if asm_gl.NAMESPACE != asm_gl.NAMESPACE_STACK[-1]: + def namespace(self, items): + asm_gl.NAMESPACE = asm_gl.normalize_namespace(items[1]) __DEBUG__("Setting namespace to " + (asm_gl.NAMESPACE or DOT), level=1) + def push_namespace(self, items): + asm_gl.NAMESPACE_STACK.append(asm_gl.NAMESPACE) + asm_gl.NAMESPACE = asm_gl.normalize_namespace(items[2] if len(items) == 3 else asm_gl.NAMESPACE) + if asm_gl.NAMESPACE != asm_gl.NAMESPACE_STACK[-1]: + __DEBUG__("Setting namespace to " + (asm_gl.NAMESPACE or DOT), level=1) -def p_pop_namespace(p): - """asm : POP NAMESPACE""" - - if not asm_gl.NAMESPACE_STACK: - error(p.lineno(2), f"Stack underflow. No more Namespaces to pop. Current namespace is {asm_gl.NAMESPACE}") - else: - asm_gl.NAMESPACE = asm_gl.NAMESPACE_STACK.pop() - - -def p_align(p): - """asm : ALIGN expr - | ALIGN pexpr - """ - align = p[2].eval() - if align < 2: - error(p.lineno(1), "ALIGN value must be greater than 1") - return - - MEMORY.set_org(MEMORY.org + (align - MEMORY.org % align) % align, p.lineno(1)) - - -def p_incbin(p): - """asm : INCBIN STRING - | INCBIN STRING COMMA expr - | INCBIN STRING COMMA expr COMMA expr - """ - - try: - fname = zxbpp.search_filename(p[2], p.lineno(2), local_first=True) - if not fname: - p[0] = None - return - - with src.api.utils.open_file(fname, "rb") as f: - filecontent = f.read() - - except IOError: - error(p.lineno(2), "cannot read file '%s'" % p[2]) - p[0] = None - return - - offset = 0 - length = None - - if len(p) > 4: - offset = p[4].eval() - - if len(p) > 6: - length = p[6].eval() - if length < 1: - error(p.lineno(5), "INCBIN length must be greater than 0") - - if offset < 0: - offset = len(filecontent) + offset - if offset < 0 or offset >= len(filecontent): - error(p.lineno(4), "INCBIN offset is out of range") - - if length is None: - length = len(filecontent) - offset - - if offset + length > len(filecontent): - excess = len(filecontent) - (offset + length) - warning(p.lineno(5), f"INCBIN length if beyond file length by {excess} bytes") - - filecontent = filecontent[offset : offset + length] - p[0] = Asm(p.lineno(1), "DEFB", filecontent) - - -def p_ex_sp_reg8(p): - """asm : EX LP SP RP COMMA reg16i - | EX LB SP RB COMMA reg16i - | EX LP SP RP COMMA HL - | EX LB SP RB COMMA HL - """ - p[0] = Asm(p.lineno(1), "EX (SP)," + p[6]) - - -def p_incdec(p): - """asm : INC inc_reg - | DEC inc_reg - """ - p[0] = Asm(p.lineno(1), "%s %s" % (p[1], p[2])) - - -def p_incdeci(p): - """asm : INC reg8_I - | DEC reg8_I - """ - p[0] = Asm(p.lineno(1), "%s %s" % (p[1], p[2][0]), p[2][1]) - - -def p_LD_reg_val(p): - """asm : LD reg8 COMMA expr - | LD reg8 COMMA pexpr - | LD reg16 COMMA expr - | LD reg8_hl COMMA expr - | LD A COMMA expr - | LD SP COMMA expr - | LD reg8i COMMA expr - """ - s = "LD %s,N" % p[2] - if p[2] in REGS16: - s += "N" - - p[0] = Asm(p.lineno(1), s, p[4]) - - -def p_LD_regI_val(p): - """asm : LD reg8_I COMMA expr""" - p[0] = Asm(p.lineno(1), "LD %s,N" % p[2][0], (p[2][1], p[4])) - - -def p_JP_hl(p): - """asm : JP reg8_hl - | JP LP reg16i RP - | JP LB reg16i RB - """ - s = "JP " - if p[2] == "(HL)": - s += p[2] - else: - s += "(%s)" % p[3] - - p[0] = Asm(p.lineno(1), s) - - -def p_SBCADD(p): - """asm : SBC A COMMA reg8 - | SBC A COMMA reg8i - | SBC A COMMA A - | SBC A COMMA reg8_hl - | SBC HL COMMA SP - | SBC HL COMMA BC - | SBC HL COMMA DE - | SBC HL COMMA HL - | ADD A COMMA reg8 - | ADD A COMMA reg8i - | ADD A COMMA A - | ADD A COMMA reg8_hl - | ADC A COMMA reg8 - | ADC A COMMA reg8i - | ADC A COMMA A - | ADC A COMMA reg8_hl - | ADD HL COMMA BC - | ADD HL COMMA DE - | ADD HL COMMA HL - | ADD HL COMMA SP - | ADC HL COMMA BC - | ADC HL COMMA DE - | ADC HL COMMA HL - | ADC HL COMMA SP - | ADD reg16i COMMA BC - | ADD reg16i COMMA DE - | ADD reg16i COMMA HL - | ADD reg16i COMMA SP - | ADD reg16i COMMA reg16i - """ - p[0] = Asm(p.lineno(1), "%s %s,%s" % (p[1], p[2], p[4])) - - -def p_arith_A_expr(p): - """asm : SBC A COMMA expr - | SBC A COMMA pexpr - | ADD A COMMA expr - | ADD A COMMA pexpr - | ADC A COMMA expr - | ADC A COMMA pexpr - """ - p[0] = Asm(p.lineno(1), "%s A,N" % p[1], p[4]) - - -def p_arith_A_regI(p): - """asm : SBC A COMMA reg8_I - | ADD A COMMA reg8_I - | ADC A COMMA reg8_I - """ - p[0] = Asm(p.lineno(1), "%s A,%s" % (p[1], p[4][0]), p[4][1]) - - -def p_bitwiseop_reg(p): - """asm : bitwiseop reg8 - | bitwiseop reg8i - | bitwiseop A - | bitwiseop reg8_hl - """ - p[0] = Asm(p[1][1], "%s %s" % (p[1][0], p[2])) - - -def p_bitwiseop_regI(p): - """asm : bitwiseop reg8_I""" - p[0] = Asm(p[1][1], "%s %s" % (p[1][0], p[2][0]), p[2][1]) - - -def p_bitwise_expr(p): - """asm : bitwiseop expr - | bitwiseop pexpr - """ - p[0] = Asm(p[1][1], "%s N" % p[1][0], p[2]) - - -def p_bitwise(p): - """bitwiseop : OR - | AND - | XOR - | SUB - | CP - """ - p[0] = (p[1], p.lineno(1)) - - -def p_PUSH_POP(p): - """asm : PUSH AF - | PUSH reg16 - | POP AF - | POP reg16 - """ - p[0] = Asm(p.lineno(1), "%s %s" % (p[1], p[2])) - - -def p_LD_addr_reg(p): # Load address,reg - """asm : LD pexpr COMMA A - | LD pexpr COMMA reg16 - | LD pexpr COMMA SP - | LD mem_indir COMMA A - | LD mem_indir COMMA reg16 - | LD mem_indir COMMA SP - """ - p[0] = Asm(p.lineno(1), "LD (NN),%s" % p[4], p[2]) - - -def p_LD_reg_addr(p): # Load address,reg - """asm : LD A COMMA pexpr - | LD reg16 COMMA pexpr - | LD SP COMMA pexpr - | LD A COMMA mem_indir - | LD reg16 COMMA mem_indir - | LD SP COMMA mem_indir - """ - p[0] = Asm(p.lineno(1), "LD %s,(NN)" % p[2], p[4]) - - -def p_ROTATE(p): - """asm : rotation reg8 - | rotation reg8_hl - | rotation A - """ - p[0] = Asm(p[1][1], "%s %s" % (p[1][0], p[2])) - - -def p_ROTATE_ix(p): - """asm : rotation reg8_I""" - p[0] = Asm(p[1][1], "%s %s" % (p[1][0], p[2][0]), p[2][1]) - - -def p_BIT(p): - """asm : bitop expr COMMA A - | bitop pexpr COMMA A - | bitop expr COMMA reg8 - | bitop pexpr COMMA reg8 - | bitop expr COMMA reg8_hl - | bitop pexpr COMMA reg8_hl - """ - bit = p[2].eval() - if bit < 0 or bit > 7: - error(p.lineno(3), "Invalid bit position %i. Must be in [0..7]" % bit) - p[0] = None - return - - p[0] = Asm(p.lineno(3), "%s %i,%s" % (p[1], bit, p[4])) - - -def p_BIT_ix(p): - """asm : bitop expr COMMA reg8_I - | bitop pexpr COMMA reg8_I - """ - bit = p[2].eval() - if bit < 0 or bit > 7: - error(p.lineno(3), "Invalid bit position %i. Must be in [0..7]" % bit) - p[0] = None - return - - p[0] = Asm(p.lineno(3), "%s %i,%s" % (p[1], bit, p[4][0]), p[4][1]) - - -def p_bitop(p): - """bitop : BIT - | RES - | SET - """ - p[0] = p[1] - - -def p_rotation(p): - """rotation : RR - | RL - | RRC - | RLC - | SLA - | SLL - | SRA - | SRL - """ - p[0] = (p[1], p.lineno(1)) - - -def p_reg_inc(p): # INC/DEC registers and (HL) - """inc_reg : SP - | reg8 - | reg16 - | reg8_hl - | A - | reg8i - """ - p[0] = p[1] - - -def p_reg8(p): - """reg8 : H - | L - | regBCDE - """ - p[0] = p[1] - + def pop_namespace(self, items): + if not asm_gl.NAMESPACE_STACK: + error(items[1].line, f"Stack underflow. No more Namespaces to pop. Current namespace is {asm_gl.NAMESPACE}") + else: + asm_gl.NAMESPACE = asm_gl.NAMESPACE_STACK.pop() + + def align(self, items): + align = items[1].eval() + if align < 2: + error(items[0].line, "ALIGN value must be greater than 1") + return None + MEMORY.set_org(MEMORY.org + (align - MEMORY.org % align) % align, items[0].line) + return None + + def incbin(self, items): + try: + fname = zxbpp.search_filename(items[1], items[1].line, local_first=True) + if not fname: + return None + with src.api.utils.open_file(fname, "rb") as f: + filecontent = f.read() + except IOError: + error(items[1].line, "cannot read file '%s'" % items[1]) + return None + + offset = 0 + length = None + + if len(items) > 3: + offset = items[3].eval() + + if len(items) > 5: + length = items[5].eval() + if length < 1: + error(items[0].line, "INCBIN length must be greater than 0") + + if offset < 0: + offset = len(filecontent) + offset + if offset < 0 or offset >= len(filecontent): + error(items[0].line, "INCBIN offset is out of range") + + if length is None: + length = len(filecontent) - offset + + if offset + length > len(filecontent): + excess = len(filecontent) - (offset + length) + warning(items[0].line, f"INCBIN length if beyond file length by {excess} bytes") + + filecontent = filecontent[offset : offset + length] + return Asm(items[0].line, "DEFB", filecontent) + + def ex_sp_reg8(self, items): + return Asm(items[0].line, "EX (SP)," + items[5]) + + def incdec(self, items): + return Asm(items[0].line, "%s %s" % (items[0], items[1])) + + def incdeci(self, items): + return Asm(items[0].line, "%s %s" % (items[0], items[1][0]), items[1][1]) + + def ld_reg_val(self, items): + s = "LD %s,N" % items[1] + if items[1] in REGS16: + s += "N" + return Asm(items[0].line, s, items[3]) + + def ld_reg_val_i(self, items): + return Asm(items[0].line, "LD %s,N" % items[1][0], (items[1][1], items[3])) + + def jp_hl(self, items): + s = "JP " + if items[1] == "(HL)": + s += items[1] + else: + s += "(%s)" % items[2] + return Asm(items[0].line, s) -def p_regBCDE(p): - """regBCDE : B - | C - | D - | E - """ - p[0] = p[1] + def sbcadd(self, items): + return Asm(items[0].line, "%s %s,%s" % (items[0], items[1], items[3])) + def arith_a_expr(self, items): + return Asm(items[0].line, "%s A,N" % items[0], items[3]) -def p_reg8i(p): - """reg8i : IXH - | IXL - | IYH - | IYL - """ - p[0] = p[1] + def arith_a_reg_i(self, items): + return Asm(items[0].line, "%s A,%s" % (items[0], items[3][0]), items[3][1]) + def bitwiseop_reg(self, items): + return Asm(items[0][1], "%s %s" % (items[0][0], items[1])) -def p_reg16(p): - """reg16 : BC - | DE - | HL - | reg16i - """ - p[0] = p[1] + def bitwiseop_reg_i(self, items): + return Asm(items[0][1], "%s %s" % (items[0][0], items[1][0]), items[1][1]) + def bitwise_expr(self, items): + return Asm(items[0][1], "%s N" % items[0][0], items[1]) -def p_reg16i(p): - """reg16i : IX - | IY - """ - p[0] = p[1] + def bitwise(self, items): + return (items[0], items[0].line) + def push_pop(self, items): + return Asm(items[0].line, "%s %s" % (items[0], items[1])) -def p_jp(p): - """asm : JP jp_flags COMMA expr - | JP jp_flags COMMA pexpr - | CALL jp_flags COMMA expr - | CALL jp_flags COMMA pexpr - """ - p[0] = Asm(p.lineno(1), "%s %s,NN" % (p[1], p[2]), p[4]) + def ld_addr_reg(self, items): + return Asm(items[0].line, "LD (NN),%s" % items[3], items[1]) + def ld_reg_addr(self, items): + return Asm(items[0].line, "LD %s,(NN)" % items[1], items[3]) -def p_ret(p): - """asm : RET jp_flags""" - p[0] = Asm(p.lineno(1), "RET %s" % p[2]) + def rotate(self, items): + return Asm(items[0][1], "%s %s" % (items[0][0], items[1])) + def rotate_ix(self, items): + return Asm(items[0][1], "%s %s" % (items[0][0], items[1][0]), items[1][1]) -def p_jpflags_other(p): - """jp_flags : P - | M - | PO - | PE - | jr_flags - """ - p[0] = p[1] + def bit(self, items): + bit = items[1].eval() + if bit < 0 or bit > 7: + error(items[2].line, "Invalid bit position %i. Must be in [0..7]" % bit) + return None + return Asm(items[2].line, "%s %i,%s" % (items[0], bit, items[3])) + def bit_ix(self, items): + bit = items[1].eval() + if bit < 0 or bit > 7: + error(items[2].line, "Invalid bit position %i. Must be in [0..7]" % bit) + return None + return Asm(items[2].line, "%s %i,%s" % (items[0], bit, items[3][0]), items[3][1]) -def p_jr(p): - """asm : JR jr_flags COMMA expr - | JR jr_flags COMMA pexpr - """ - p[4] = Expr.makenode(Container("-", p.lineno(3)), p[4], Expr.makenode(Container(MEMORY.org + 2, p.lineno(1)))) - p[0] = Asm(p.lineno(1), "JR %s,N" % p[2], p[4]) + def bitop(self, items): + return items[0] + def rotation(self, items): + return (items[0], items[0].line) -def p_jr_flags(p): - """jr_flags : Z - | C - | NZ - | NC - """ - p[0] = p[1] - - -def p_jrjp(p): - """asm : JP expr - | JR expr - | CALL expr - | DJNZ expr - | JP pexpr - | JR pexpr - | CALL pexpr - | DJNZ pexpr - """ - if p[1] in ("JR", "DJNZ"): - op = "N" - p[2] = Expr.makenode(Container("-", p.lineno(1)), p[2], Expr.makenode(Container(MEMORY.org + 2, p.lineno(1)))) - else: - op = "NN" + def reg_inc(self, items): + return items[0] - p[0] = Asm(p.lineno(1), p[1] + " " + op, p[2]) + def reg8(self, items): + return items[0] + def reg_bcde(self, items): + return items[0] -def p_rst(p): - """asm : RST expr""" - val = p[2].eval() + def reg8i(self, items): + return items[0] - if val not in (0, 8, 16, 24, 32, 40, 48, 56): - error(p.lineno(1), "Invalid RST number %i" % val) - p[0] = None - return + def reg16(self, items): + return items[0] - p[0] = Asm(p.lineno(1), "RST %XH" % val) + def reg16i(self, items): + return items[0] + def jp(self, items): + return Asm(items[0].line, "%s %s,NN" % (items[0], items[1]), items[3]) -def p_im(p): - """asm : IM expr""" - val = p[2].eval() - if val not in (0, 1, 2): - error(p.lineno(1), "Invalid IM number %i" % val) - p[0] = None - return + def ret(self, items): + return Asm(items[0].line, "RET %s" % items[1]) - p[0] = Asm(p.lineno(1), "IM %i" % val) + def jpflags_other(self, items): + return items[0] + def jr(self, items): + expr = Expr.makenode( + Container("-", items[2].line), items[3], Expr.makenode(Container(MEMORY.org + 2, items[0].line)) + ) + return Asm(items[0].line, "JR %s,N" % items[1], expr) -def p_in(p): - """asm : IN A COMMA LP C RP - | IN A COMMA LB C RB - | IN reg8 COMMA LP C RP - | IN reg8 COMMA LB C RB - """ - p[0] = Asm(p.lineno(1), "IN %s,(C)" % p[2]) + def jr_flags(self, items): + return items[0] + def jrjp(self, items): + if items[0] in ("JR", "DJNZ"): + op = "N" + expr = Expr.makenode( + Container("-", items[0].line), items[1], Expr.makenode(Container(MEMORY.org + 2, items[0].line)) + ) + else: + op = "NN" + expr = items[1] + return Asm(items[0].line, items[0] + " " + op, expr) -def p_out(p): - """asm : OUT LP C RP COMMA A - | OUT LB C RB COMMA A - | OUT LP C RP COMMA reg8 - | OUT LB C RB COMMA reg8 - """ - p[0] = Asm(p.lineno(1), "OUT (C),%s" % p[6]) + def rst(self, items): + val = items[1].eval() + if val not in (0, 8, 16, 24, 32, 40, 48, 56): + error(items[0].line, "Invalid RST number %i" % val) + return None + return Asm(items[0].line, "RST %XH" % val) + def im(self, items): + val = items[1].eval() + if val not in (0, 1, 2): + error(items[0].line, "Invalid IM number %i" % val) + return None + return Asm(items[0].line, "IM %i" % val) -def p_in_expr(p): - """asm : IN A COMMA mem_indir - | IN A COMMA pexpr - """ - p[0] = Asm(p.lineno(1), "IN A,(N)", p[4]) + def in_op(self, items): + return Asm(items[0].line, "IN %s,(C)" % items[1]) + def out_op(self, items): + return Asm(items[0].line, "OUT (C),%s" % items[5]) -def p_out_expr(p): - """asm : OUT mem_indir COMMA A - | OUT pexpr COMMA A - """ - p[0] = Asm(p.lineno(1), "OUT (N),A", p[2]) - - -def p_single(p): - """asm : NOP - | EXX - | CCF - | SCF - | LDIR - | LDI - | LDDR - | LDD - | CPIR - | CPI - | CPDR - | CPD - | DAA - | NEG - | CPL - | HALT - | EI - | DI - | OUTD - | OUTI - | OTDR - | OTIR - | IND - | INI - | INDR - | INIR - | RET - | RETI - | RETN - | RLA - | RLCA - | RRA - | RRCA - | RLD - | RRD - """ - p[0] = Asm(p.lineno(1), p[1]) # Single instruction - - -def p_expr_div_expr(p): - """expr : expr BAND expr - | expr BOR expr - | expr BXOR expr - | expr PLUS expr - | expr MINUS expr - | expr MUL expr - | expr DIV expr - | expr MOD expr - | expr POW expr - | expr LSHIFT expr - | expr RSHIFT expr - | pexpr BAND expr - | pexpr BOR expr - | pexpr BXOR expr - | pexpr PLUS expr - | pexpr MINUS expr - | pexpr MUL expr - | pexpr DIV expr - | pexpr MOD expr - | pexpr POW expr - | pexpr LSHIFT expr - | pexpr RSHIFT expr - | expr BAND pexpr - | expr BOR pexpr - | expr BXOR pexpr - | expr PLUS pexpr - | expr MINUS pexpr - | expr MUL pexpr - | expr DIV pexpr - | expr MOD pexpr - | expr POW pexpr - | expr LSHIFT pexpr - | expr RSHIFT pexpr - | pexpr BAND pexpr - | pexpr BOR pexpr - | pexpr BXOR pexpr - | pexpr PLUS pexpr - | pexpr MINUS pexpr - | pexpr MUL pexpr - | pexpr DIV pexpr - | pexpr MOD pexpr - | pexpr POW pexpr - | pexpr LSHIFT pexpr - | pexpr RSHIFT pexpr - """ - p[0] = Expr.makenode(Container(p[2], p.lineno(2)), p[1], p[3]) + def in_expr(self, items): + return Asm(items[0].line, "IN A,(N)", items[3]) + def out_expr(self, items): + return Asm(items[0].line, "OUT (N),A", items[1]) -def p_expr_lprp(p): - """pexpr : LP expr RP""" - p[0] = p[2] + def single(self, items): + return Asm(items[0].line, items[0]) + def mul_d_e(self, items): + return Asm(items[0].line, "MUL D,E") -def p_mem_indir(p): - """mem_indir : LB expr RB""" - p[0] = p[2] + def simple_instruction(self, items): + return Asm(items[0].line, items[0]) + def add_reg16_a(self, items): + return Asm(items[0].line, f"ADD {items[1]},A") -def p_expr_uminus(p): - """expr : MINUS expr %prec UMINUS - | PLUS expr %prec UMINUS - | MINUS pexpr %prec UMINUS - | PLUS pexpr %prec UMINUS - """ - p[0] = Expr.makenode(Container(p[1], p.lineno(1)), p[2]) + def jp_c(self, items): + return Asm(items[0].line, "JP (C)") + def bxxxx_de_b(self, items): + return Asm(items[0].line, f"{items[0]} DE,B") -def p_expr_int(p): - """expr : INTEGER""" - p[0] = Expr.makenode(Container(int(p[1]), p.lineno(1))) + def add_reg_nn(self, items): + return Asm(items[0].line, f"ADD {items[1]},NN", items[3]) + def test_nn(self, items): + return Asm(items[0].line, "TEST N", items[1]) -def p_expr_label(p): - """expr : ID""" - p[0] = Expr.makenode(Container(MEMORY.get_label(p[1], p.lineno(1)), p.lineno(1))) + def nextreg_expr(self, items): + return Asm(items[0].line, "NEXTREG N,N", (items[1], items[3])) + def nextreg_a(self, items): + return Asm(items[0].line, "NEXTREG N,A", items[1]) -def p_expr_paren(p): - """expr : LPP expr RPP""" - p[0] = p[2] + def push_imm(self, items): + mknod = Expr.makenode + cont = lambda x: Container(x, items[0].line) + ff = mknod(cont(0xFF)) + n8 = mknod(cont(8)) + expr = mknod( + cont("|"), + mknod(cont("<<"), mknod(cont("&"), items[1], ff), n8), + mknod(cont("&"), mknod(cont(">>"), items[1], n8), ff), + ) + return Asm(items[0].line, "PUSH NN", expr) + def expr_div_expr(self, items): + return Expr.makenode(Container(items[1], items[1].line), items[0], items[2]) -def p_expr_addr(p): - """expr : ADDR""" - # The current instruction address - p[0] = Expr.makenode(Container(MEMORY.org, p.lineno(1))) + def expr_add_minus_expr(self, items): + return Expr.makenode(Container(items[1], items[1].line), items[0], items[2]) + def expr_lprp(self, items): + return items[1] -# Some preprocessor directives -def p_preprocessor_line(p): - """line : preproc_line""" - p[0] = None + def mem_indir(self, items): + return items[1] + def expr_uminus(self, items): + return Expr.makenode(Container(items[0], items[0].line), items[1]) -def p_preproc_line_init(p): - """preproc_line : _INIT STRING""" - INITS.append(Container(p[2].strip('"'), p.lineno(2))) + def expr_uplus(self, items): + return Expr.makenode(Container(items[0], items[0].line), items[1]) + def expr_int(self, items): + return Expr.makenode(Container(int(items[0]), items[0].line)) -# --- YYERROR + def expr_label(self, items): + return Expr.makenode(Container(MEMORY.get_label(items[0], items[0].line), items[0].line)) + def expr_paren(self, items): + return items[1] -def p_error(p): - if p is not None: - if p.type != "NEWLINE": - error(p.lineno, "Syntax error. Unexpected token '%s' [%s]" % (p.value, p.type)) - else: - error(p.lineno, "Syntax error. Unexpected end of line [NEWLINE]") - else: - OPTIONS.stderr.write("General syntax error at assembler (unexpected End of File?)") - gl.has_errors += 1 + def expr_addr(self, items): + return Expr.makenode(Container(MEMORY.org, items[0].line)) def assemble(input_): @@ -1006,7 +531,42 @@ def assemble(input_): else: parser_ = parser - parser_.parse(input_, lexer=LEXER, debug=OPTIONS.debug_level > 1) + logical_lines = [] + current_buffer = [] + raw_lines = input_.splitlines() + for line in raw_lines: + current_buffer.append(line) + if line.rstrip(" \t").endswith("\\"): + continue + else: + logical_lines.append("\n".join(current_buffer)) + current_buffer = [] + if current_buffer: + logical_lines.append("\n".join(current_buffer)) + + current_lineno = 1 + for line in logical_lines: + LEXER.input(line + "\n") + LEXER.lineno = current_lineno + try: + parser_.parse(LEXER) + current_lineno = LEXER.lineno + except UnexpectedInput as e: + from .asmparse_standalone import UnexpectedToken + + if isinstance(e, UnexpectedToken): + tok = e.token + if tok.type == "$END": + OPTIONS.stderr.write("General syntax error at assembler (unexpected End of File?)") + gl.has_errors += 1 + elif tok.type == "NEWLINE": + error(current_lineno, "Syntax error. Unexpected end of line [NEWLINE]") + else: + error(current_lineno, "Syntax error. Unexpected token '%s' [%s]" % (tok.value, tok.type)) + else: + error(current_lineno, f"Syntax error at line {current_lineno}, column {e.column}") + current_lineno += line.count("\n") + 1 + if len(MEMORY.scopes): error(MEMORY.scopes[-1], "Missing ENDP to close this scope") @@ -1096,11 +656,6 @@ def main(argv): generate_binary(OPTIONS.output_filename, OPTIONS.output_file_type) -# Z80 only ASM parser -parser = src.api.utils.get_or_create("asmparse", lambda: yacc.yacc(start="start", debug=True)) - -# needed for ply -from .zxnext import * # noqa +parser = BaseLarkStandAlone(lexer=AsmLarkLexerAdapter, transformer=AsmTransformer()) -# ZXNEXT extended Opcodes parser -zxnext_parser = src.api.utils.get_or_create("zxnext_asmparse", lambda: yacc.yacc(start="start", debug=True)) +zxnext_parser = ZXNextLarkStandAlone(lexer=AsmLarkLexerAdapter, transformer=AsmTransformer()) diff --git a/src/zxbasm/asmparse_standalone.py b/src/zxbasm/asmparse_standalone.py new file mode 100644 index 000000000..236367a20 --- /dev/null +++ b/src/zxbasm/asmparse_standalone.py @@ -0,0 +1,3574 @@ +# The file was automatically generated by Lark v1.3.1 +__version__ = "1.3.1" + +# +# +# Lark Stand-alone Generator Tool +# ---------------------------------- +# Generates a stand-alone LALR(1) parser +# +# Git: https://github.com/erezsh/lark +# Author: Erez Shinan (erezshin@gmail.com) +# +# +# >>> LICENSE +# +# This tool and its generated code use a separate license from Lark, +# and are subject to the terms of the Mozilla Public License, v. 2.0. +# If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +# If you wish to purchase a commercial license for this tool and its +# generated code, you may contact me via email or otherwise. +# +# If MPL2 is incompatible with your free or open-source project, +# contact me and we'll work it out. +# +# + +from copy import deepcopy +from abc import ABC, abstractmethod +from types import ModuleType +from typing import ( + TypeVar, Generic, Type, Tuple, List, Dict, Iterator, Collection, Callable, Optional, FrozenSet, Any, + Union, Iterable, IO, TYPE_CHECKING, overload, Sequence, + Pattern as REPattern, ClassVar, Set, Mapping +) + + +class LarkError(Exception): + pass + + +class ConfigurationError(LarkError, ValueError): + pass + + +def assert_config(value, options: Collection, msg='Got %r, expected one of %s'): + if value not in options: + raise ConfigurationError(msg % (value, options)) + + +class GrammarError(LarkError): + pass + + +class ParseError(LarkError): + pass + + +class LexError(LarkError): + pass + +T = TypeVar('T') + +class UnexpectedInput(LarkError): + #-- + line: int + column: int + pos_in_stream = None + state: Any + _terminals_by_name = None + interactive_parser: 'InteractiveParser' + + def get_context(self, text: str, span: int=40) -> str: + #-- + pos = self.pos_in_stream or 0 + start = max(pos - span, 0) + end = pos + span + if not isinstance(text, bytes): + before = text[start:pos].rsplit('\n', 1)[-1] + after = text[pos:end].split('\n', 1)[0] + return before + after + '\n' + ' ' * len(before.expandtabs()) + '^\n' + else: + before = text[start:pos].rsplit(b'\n', 1)[-1] + after = text[pos:end].split(b'\n', 1)[0] + return (before + after + b'\n' + b' ' * len(before.expandtabs()) + b'^\n').decode("ascii", "backslashreplace") + + def match_examples(self, parse_fn: 'Callable[[str], Tree]', + examples: Union[Mapping[T, Iterable[str]], Iterable[Tuple[T, Iterable[str]]]], + token_type_match_fallback: bool=False, + use_accepts: bool=True + ) -> Optional[T]: + #-- + assert self.state is not None, "Not supported for this exception" + + if isinstance(examples, Mapping): + examples = examples.items() + + candidate = (None, False) + for i, (label, example) in enumerate(examples): + assert not isinstance(example, str), "Expecting a list" + + for j, malformed in enumerate(example): + try: + parse_fn(malformed) + except UnexpectedInput as ut: + if ut.state == self.state: + if ( + use_accepts + and isinstance(self, UnexpectedToken) + and isinstance(ut, UnexpectedToken) + and ut.accepts != self.accepts + ): + logger.debug("Different accepts with same state[%d]: %s != %s at example [%s][%s]" % + (self.state, self.accepts, ut.accepts, i, j)) + continue + if ( + isinstance(self, (UnexpectedToken, UnexpectedEOF)) + and isinstance(ut, (UnexpectedToken, UnexpectedEOF)) + ): + if ut.token == self.token: ## + + logger.debug("Exact Match at example [%s][%s]" % (i, j)) + return label + + if token_type_match_fallback: + ## + + if (ut.token.type == self.token.type) and not candidate[-1]: + logger.debug("Token Type Fallback at example [%s][%s]" % (i, j)) + candidate = label, True + + if candidate[0] is None: + logger.debug("Same State match at example [%s][%s]" % (i, j)) + candidate = label, False + + return candidate[0] + + def _format_expected(self, expected): + if self._terminals_by_name: + d = self._terminals_by_name + expected = [d[t_name].user_repr() if t_name in d else t_name for t_name in expected] + return "Expected one of: \n\t* %s\n" % '\n\t* '.join(expected) + + +class UnexpectedEOF(ParseError, UnexpectedInput): + #-- + expected: 'List[Token]' + + def __init__(self, expected, state=None, terminals_by_name=None): + super(UnexpectedEOF, self).__init__() + + self.expected = expected + self.state = state + from .lexer import Token + self.token = Token("", "") ## + + self.pos_in_stream = -1 + self.line = -1 + self.column = -1 + self._terminals_by_name = terminals_by_name + + + def __str__(self): + message = "Unexpected end-of-input. " + message += self._format_expected(self.expected) + return message + + +class UnexpectedCharacters(LexError, UnexpectedInput): + #-- + + allowed: Set[str] + considered_tokens: Set[Any] + + def __init__(self, seq, lex_pos, line, column, allowed=None, considered_tokens=None, state=None, token_history=None, + terminals_by_name=None, considered_rules=None): + super(UnexpectedCharacters, self).__init__() + + ## + + self.line = line + self.column = column + self.pos_in_stream = lex_pos + self.state = state + self._terminals_by_name = terminals_by_name + + self.allowed = allowed + self.considered_tokens = considered_tokens + self.considered_rules = considered_rules + self.token_history = token_history + + if isinstance(seq, bytes): + self.char = seq[lex_pos:lex_pos + 1].decode("ascii", "backslashreplace") + else: + self.char = seq[lex_pos] + self._context = self.get_context(seq) + + + def __str__(self): + message = "No terminal matches '%s' in the current parser context, at line %d col %d" % (self.char, self.line, self.column) + message += '\n\n' + self._context + if self.allowed: + message += self._format_expected(self.allowed) + if self.token_history: + message += '\nPrevious tokens: %s\n' % ', '.join(repr(t) for t in self.token_history) + return message + + +class UnexpectedToken(ParseError, UnexpectedInput): + #-- + + expected: Set[str] + considered_rules: Set[str] + + def __init__(self, token, expected, considered_rules=None, state=None, interactive_parser=None, terminals_by_name=None, token_history=None): + super(UnexpectedToken, self).__init__() + + ## + + self.line = getattr(token, 'line', '?') + self.column = getattr(token, 'column', '?') + self.pos_in_stream = getattr(token, 'start_pos', None) + self.state = state + + self.token = token + self.expected = expected ## + + self._accepts = NO_VALUE + self.considered_rules = considered_rules + self.interactive_parser = interactive_parser + self._terminals_by_name = terminals_by_name + self.token_history = token_history + + + @property + def accepts(self) -> Set[str]: + if self._accepts is NO_VALUE: + self._accepts = self.interactive_parser and self.interactive_parser.accepts() + return self._accepts + + def __str__(self): + message = ("Unexpected token %r at line %s, column %s.\n%s" + % (self.token, self.line, self.column, self._format_expected(self.accepts or self.expected))) + if self.token_history: + message += "Previous tokens: %r\n" % self.token_history + + return message + + + +class VisitError(LarkError): + #-- + + obj: 'Union[Tree, Token]' + orig_exc: Exception + + def __init__(self, rule, obj, orig_exc): + message = 'Error trying to process rule "%s":\n\n%s' % (rule, orig_exc) + super(VisitError, self).__init__(message) + + self.rule = rule + self.obj = obj + self.orig_exc = orig_exc + + +class MissingVariableError(LarkError): + pass + + +import sys, re +import logging +from dataclasses import dataclass +from typing import Generic, AnyStr + +logger: logging.Logger = logging.getLogger("lark") +logger.addHandler(logging.StreamHandler()) +## + +## + +logger.setLevel(logging.CRITICAL) + + +NO_VALUE = object() + +T = TypeVar("T") + + +def classify(seq: Iterable, key: Optional[Callable] = None, value: Optional[Callable] = None) -> Dict: + d: Dict[Any, Any] = {} + for item in seq: + k = key(item) if (key is not None) else item + v = value(item) if (value is not None) else item + try: + d[k].append(v) + except KeyError: + d[k] = [v] + return d + + +def _deserialize(data: Any, namespace: Dict[str, Any], memo: Dict) -> Any: + if isinstance(data, dict): + if '__type__' in data: ## + + class_ = namespace[data['__type__']] + return class_.deserialize(data, memo) + elif '@' in data: + return memo[data['@']] + return {key:_deserialize(value, namespace, memo) for key, value in data.items()} + elif isinstance(data, list): + return [_deserialize(value, namespace, memo) for value in data] + return data + + +_T = TypeVar("_T", bound="Serialize") + +class Serialize: + #-- + + def memo_serialize(self, types_to_memoize: List) -> Any: + memo = SerializeMemoizer(types_to_memoize) + return self.serialize(memo), memo.serialize() + + def serialize(self, memo = None) -> Dict[str, Any]: + if memo and memo.in_types(self): + return {'@': memo.memoized.get(self)} + + fields = getattr(self, '__serialize_fields__') + res = {f: _serialize(getattr(self, f), memo) for f in fields} + res['__type__'] = type(self).__name__ + if hasattr(self, '_serialize'): + self._serialize(res, memo) + return res + + @classmethod + def deserialize(cls: Type[_T], data: Dict[str, Any], memo: Dict[int, Any]) -> _T: + namespace = getattr(cls, '__serialize_namespace__', []) + namespace = {c.__name__:c for c in namespace} + + fields = getattr(cls, '__serialize_fields__') + + if '@' in data: + return memo[data['@']] + + inst = cls.__new__(cls) + for f in fields: + try: + setattr(inst, f, _deserialize(data[f], namespace, memo)) + except KeyError as e: + raise KeyError("Cannot find key for class", cls, e) + + if hasattr(inst, '_deserialize'): + inst._deserialize() + + return inst + + +class SerializeMemoizer(Serialize): + #-- + + __serialize_fields__ = 'memoized', + + def __init__(self, types_to_memoize: List) -> None: + self.types_to_memoize = tuple(types_to_memoize) + self.memoized = Enumerator() + + def in_types(self, value: Serialize) -> bool: + return isinstance(value, self.types_to_memoize) + + def serialize(self) -> Dict[int, Any]: ## + + return _serialize(self.memoized.reversed(), None) + + @classmethod + def deserialize(cls, data: Dict[int, Any], namespace: Dict[str, Any], memo: Dict[Any, Any]) -> Dict[int, Any]: ## + + return _deserialize(data, namespace, memo) + + +try: + import regex + _has_regex = True +except ImportError: + _has_regex = False + +if sys.version_info >= (3, 11): + import re._parser as sre_parse + import re._constants as sre_constants +else: + import sre_parse + import sre_constants + +categ_pattern = re.compile(r'\\p{[A-Za-z_]+}') + +def get_regexp_width(expr: str) -> Union[Tuple[int, int], List[int]]: + if _has_regex: + ## + + ## + + ## + + regexp_final = re.sub(categ_pattern, 'A', expr) + else: + if re.search(categ_pattern, expr): + raise ImportError('`regex` module must be installed in order to use Unicode categories.', expr) + regexp_final = expr + try: + ## + + return [int(x) for x in sre_parse.parse(regexp_final).getwidth()] + except sre_constants.error: + if not _has_regex: + raise ValueError(expr) + else: + ## + + ## + + c = regex.compile(regexp_final) + ## + + ## + + MAXWIDTH = getattr(sre_parse, "MAXWIDTH", sre_constants.MAXREPEAT) + if c.match('') is None: + ## + + return 1, int(MAXWIDTH) + else: + return 0, int(MAXWIDTH) + + +@dataclass(frozen=True) +class TextSlice(Generic[AnyStr]): + #-- + text: AnyStr + start: int + end: int + + def __post_init__(self): + if not isinstance(self.text, (str, bytes)): + raise TypeError("text must be str or bytes") + + if self.start < 0: + object.__setattr__(self, 'start', self.start + len(self.text)) + assert self.start >=0 + + if self.end is None: + object.__setattr__(self, 'end', len(self.text)) + elif self.end < 0: + object.__setattr__(self, 'end', self.end + len(self.text)) + assert self.end <= len(self.text) + + @classmethod + def cast_from(cls, text: 'TextOrSlice') -> 'TextSlice[AnyStr]': + if isinstance(text, TextSlice): + return text + + return cls(text, 0, len(text)) + + def is_complete_text(self): + return self.start == 0 and self.end == len(self.text) + + def __len__(self): + return self.end - self.start + + def count(self, substr: AnyStr): + return self.text.count(substr, self.start, self.end) + + def rindex(self, substr: AnyStr): + return self.text.rindex(substr, self.start, self.end) + + +TextOrSlice = Union[AnyStr, 'TextSlice[AnyStr]'] +LarkInput = Union[AnyStr, TextSlice[AnyStr], Any] + + + +class Meta: + + empty: bool + line: int + column: int + start_pos: int + end_line: int + end_column: int + end_pos: int + orig_expansion: 'List[TerminalDef]' + match_tree: bool + + def __init__(self): + self.empty = True + + +_Leaf_T = TypeVar("_Leaf_T") +Branch = Union[_Leaf_T, 'Tree[_Leaf_T]'] + + +class Tree(Generic[_Leaf_T]): + #-- + + data: str + children: 'List[Branch[_Leaf_T]]' + + def __init__(self, data: str, children: 'List[Branch[_Leaf_T]]', meta: Optional[Meta]=None) -> None: + self.data = data + self.children = children + self._meta = meta + + @property + def meta(self) -> Meta: + if self._meta is None: + self._meta = Meta() + return self._meta + + def __repr__(self): + return 'Tree(%r, %r)' % (self.data, self.children) + + __match_args__ = ("data", "children") + + def _pretty_label(self): + return self.data + + def _pretty(self, level, indent_str): + yield f'{indent_str*level}{self._pretty_label()}' + if len(self.children) == 1 and not isinstance(self.children[0], Tree): + yield f'\t{self.children[0]}\n' + else: + yield '\n' + for n in self.children: + if isinstance(n, Tree): + yield from n._pretty(level+1, indent_str) + else: + yield f'{indent_str*(level+1)}{n}\n' + + def pretty(self, indent_str: str=' ') -> str: + #-- + return ''.join(self._pretty(0, indent_str)) + + def __rich__(self, parent:Optional['rich.tree.Tree']=None) -> 'rich.tree.Tree': + #-- + return self._rich(parent) + + def _rich(self, parent): + if parent: + tree = parent.add(f'[bold]{self.data}[/bold]') + else: + import rich.tree + tree = rich.tree.Tree(self.data) + + for c in self.children: + if isinstance(c, Tree): + c._rich(tree) + else: + tree.add(f'[green]{c}[/green]') + + return tree + + def __eq__(self, other): + try: + return self.data == other.data and self.children == other.children + except AttributeError: + return False + + def __ne__(self, other): + return not (self == other) + + def __hash__(self) -> int: + return hash((self.data, tuple(self.children))) + + def iter_subtrees(self) -> 'Iterator[Tree[_Leaf_T]]': + #-- + queue = [self] + subtrees = dict() + for subtree in queue: + subtrees[id(subtree)] = subtree + queue += [c for c in reversed(subtree.children) + if isinstance(c, Tree) and id(c) not in subtrees] + + del queue + return reversed(list(subtrees.values())) + + def iter_subtrees_topdown(self): + #-- + stack = [self] + stack_append = stack.append + stack_pop = stack.pop + while stack: + node = stack_pop() + if not isinstance(node, Tree): + continue + yield node + for child in reversed(node.children): + stack_append(child) + + def find_pred(self, pred: 'Callable[[Tree[_Leaf_T]], bool]') -> 'Iterator[Tree[_Leaf_T]]': + #-- + return filter(pred, self.iter_subtrees()) + + def find_data(self, data: str) -> 'Iterator[Tree[_Leaf_T]]': + #-- + return self.find_pred(lambda t: t.data == data) + + +from functools import wraps, update_wrapper +from inspect import getmembers, getmro + +_Return_T = TypeVar('_Return_T') +_Return_V = TypeVar('_Return_V') +_Leaf_T = TypeVar('_Leaf_T') +_Leaf_U = TypeVar('_Leaf_U') +_R = TypeVar('_R') +_FUNC = Callable[..., _Return_T] +_DECORATED = Union[_FUNC, type] + +class _DiscardType: + #-- + + def __repr__(self): + return "lark.visitors.Discard" + +Discard = _DiscardType() + +## + + +class _Decoratable: + #-- + + @classmethod + def _apply_v_args(cls, visit_wrapper): + mro = getmro(cls) + assert mro[0] is cls + libmembers = {name for _cls in mro[1:] for name, _ in getmembers(_cls)} + for name, value in getmembers(cls): + + ## + + if name.startswith('_') or (name in libmembers and name not in cls.__dict__): + continue + if not callable(value): + continue + + ## + + if isinstance(cls.__dict__[name], _VArgsWrapper): + continue + + setattr(cls, name, _VArgsWrapper(cls.__dict__[name], visit_wrapper)) + return cls + + def __class_getitem__(cls, _): + return cls + + +class Transformer(_Decoratable, ABC, Generic[_Leaf_T, _Return_T]): + #-- + __visit_tokens__ = True ## + + + def __init__(self, visit_tokens: bool=True) -> None: + self.__visit_tokens__ = visit_tokens + + def _call_userfunc(self, tree, new_children=None): + ## + + children = new_children if new_children is not None else tree.children + try: + f = getattr(self, tree.data) + except AttributeError: + return self.__default__(tree.data, children, tree.meta) + else: + try: + wrapper = getattr(f, 'visit_wrapper', None) + if wrapper is not None: + return f.visit_wrapper(f, tree.data, children, tree.meta) + else: + return f(children) + except GrammarError: + raise + except Exception as e: + raise VisitError(tree.data, tree, e) + + def _call_userfunc_token(self, token): + try: + f = getattr(self, token.type) + except AttributeError: + return self.__default_token__(token) + else: + try: + return f(token) + except GrammarError: + raise + except Exception as e: + raise VisitError(token.type, token, e) + + def _transform_children(self, children): + for c in children: + if isinstance(c, Tree): + res = self._transform_tree(c) + elif self.__visit_tokens__ and isinstance(c, Token): + res = self._call_userfunc_token(c) + else: + res = c + + if res is not Discard: + yield res + + def _transform_tree(self, tree): + children = list(self._transform_children(tree.children)) + return self._call_userfunc(tree, children) + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + #-- + res = list(self._transform_children([tree])) + if not res: + return None ## + + assert len(res) == 1 + return res[0] + + def __mul__( + self: 'Transformer[_Leaf_T, Tree[_Leaf_U]]', + other: 'Union[Transformer[_Leaf_U, _Return_V], TransformerChain[_Leaf_U, _Return_V,]]' + ) -> 'TransformerChain[_Leaf_T, _Return_V]': + #-- + return TransformerChain(self, other) + + def __default__(self, data, children, meta): + #-- + return Tree(data, children, meta) + + def __default_token__(self, token): + #-- + return token + + +def merge_transformers(base_transformer=None, **transformers_to_merge): + #-- + if base_transformer is None: + base_transformer = Transformer() + for prefix, transformer in transformers_to_merge.items(): + for method_name in dir(transformer): + method = getattr(transformer, method_name) + if not callable(method): + continue + if method_name.startswith("_") or method_name == "transform": + continue + prefixed_method = prefix + "__" + method_name + if hasattr(base_transformer, prefixed_method): + raise AttributeError("Cannot merge: method '%s' appears more than once" % prefixed_method) + + setattr(base_transformer, prefixed_method, method) + + return base_transformer + + +class InlineTransformer(Transformer): ## + + def _call_userfunc(self, tree, new_children=None): + ## + + children = new_children if new_children is not None else tree.children + try: + f = getattr(self, tree.data) + except AttributeError: + return self.__default__(tree.data, children, tree.meta) + else: + return f(*children) + + +class TransformerChain(Generic[_Leaf_T, _Return_T]): + + transformers: 'Tuple[Union[Transformer, TransformerChain], ...]' + + def __init__(self, *transformers: 'Union[Transformer, TransformerChain]') -> None: + self.transformers = transformers + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + for t in self.transformers: + tree = t.transform(tree) + return cast(_Return_T, tree) + + def __mul__( + self: 'TransformerChain[_Leaf_T, Tree[_Leaf_U]]', + other: 'Union[Transformer[_Leaf_U, _Return_V], TransformerChain[_Leaf_U, _Return_V]]' + ) -> 'TransformerChain[_Leaf_T, _Return_V]': + return TransformerChain(*self.transformers + (other,)) + + +class Transformer_InPlace(Transformer[_Leaf_T, _Return_T]): + #-- + def _transform_tree(self, tree): ## + + return self._call_userfunc(tree) + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + for subtree in tree.iter_subtrees(): + subtree.children = list(self._transform_children(subtree.children)) + + return self._transform_tree(tree) + + +class Transformer_NonRecursive(Transformer[_Leaf_T, _Return_T]): + #-- + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + ## + + rev_postfix = [] + q: List[Branch[_Leaf_T]] = [tree] + while q: + t = q.pop() + rev_postfix.append(t) + if isinstance(t, Tree): + q += t.children + + ## + + stack: List = [] + for x in reversed(rev_postfix): + if isinstance(x, Tree): + size = len(x.children) + if size: + args = stack[-size:] + del stack[-size:] + else: + args = [] + + res = self._call_userfunc(x, args) + if res is not Discard: + stack.append(res) + + elif self.__visit_tokens__ and isinstance(x, Token): + res = self._call_userfunc_token(x) + if res is not Discard: + stack.append(res) + else: + stack.append(x) + + result, = stack ## + + ## + + ## + + ## + + return cast(_Return_T, result) + + +class Transformer_InPlaceRecursive(Transformer[_Leaf_T, _Return_T]): + #-- + def _transform_tree(self, tree): + tree.children = list(self._transform_children(tree.children)) + return self._call_userfunc(tree) + + +## + + +class VisitorBase: + def _call_userfunc(self, tree): + return getattr(self, tree.data, self.__default__)(tree) + + def __default__(self, tree): + #-- + return tree + + def __class_getitem__(cls, _): + return cls + + +class Visitor(VisitorBase, ABC, Generic[_Leaf_T]): + #-- + + def visit(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + for subtree in tree.iter_subtrees(): + self._call_userfunc(subtree) + return tree + + def visit_topdown(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + for subtree in tree.iter_subtrees_topdown(): + self._call_userfunc(subtree) + return tree + + +class Visitor_Recursive(VisitorBase, Generic[_Leaf_T]): + #-- + + def visit(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + for child in tree.children: + if isinstance(child, Tree): + self.visit(child) + + self._call_userfunc(tree) + return tree + + def visit_topdown(self,tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + self._call_userfunc(tree) + + for child in tree.children: + if isinstance(child, Tree): + self.visit_topdown(child) + + return tree + + +class Interpreter(_Decoratable, ABC, Generic[_Leaf_T, _Return_T]): + #-- + + def visit(self, tree: Tree[_Leaf_T]) -> _Return_T: + ## + + ## + + ## + + return self._visit_tree(tree) + + def _visit_tree(self, tree: Tree[_Leaf_T]): + f = getattr(self, tree.data) + wrapper = getattr(f, 'visit_wrapper', None) + if wrapper is not None: + return f.visit_wrapper(f, tree.data, tree.children, tree.meta) + else: + return f(tree) + + def visit_children(self, tree: Tree[_Leaf_T]) -> List: + return [self._visit_tree(child) if isinstance(child, Tree) else child + for child in tree.children] + + def __getattr__(self, name): + return self.__default__ + + def __default__(self, tree): + return self.visit_children(tree) + + +_InterMethod = Callable[[Type[Interpreter], _Return_T], _R] + +def visit_children_decor(func: _InterMethod) -> _InterMethod: + #-- + @wraps(func) + def inner(cls, tree): + values = cls.visit_children(tree) + return func(cls, values) + return inner + +## + + +def _apply_v_args(obj, visit_wrapper): + try: + _apply = obj._apply_v_args + except AttributeError: + return _VArgsWrapper(obj, visit_wrapper) + else: + return _apply(visit_wrapper) + + +class _VArgsWrapper: + #-- + base_func: Callable + + def __init__(self, func: Callable, visit_wrapper: Callable[[Callable, str, list, Any], Any]): + if isinstance(func, _VArgsWrapper): + func = func.base_func + self.base_func = func + self.visit_wrapper = visit_wrapper + update_wrapper(self, func) + + def __call__(self, *args, **kwargs): + return self.base_func(*args, **kwargs) + + def __get__(self, instance, owner=None): + try: + ## + + ## + + g = type(self.base_func).__get__ + except AttributeError: + return self + else: + return _VArgsWrapper(g(self.base_func, instance, owner), self.visit_wrapper) + + def __set_name__(self, owner, name): + try: + f = type(self.base_func).__set_name__ + except AttributeError: + return + else: + f(self.base_func, owner, name) + + +def _vargs_inline(f, _data, children, _meta): + return f(*children) +def _vargs_meta_inline(f, _data, children, meta): + return f(meta, *children) +def _vargs_meta(f, _data, children, meta): + return f(meta, children) +def _vargs_tree(f, data, children, meta): + return f(Tree(data, children, meta)) + + +def v_args(inline: bool = False, meta: bool = False, tree: bool = False, wrapper: Optional[Callable] = None) -> Callable[[_DECORATED], _DECORATED]: + #-- + if tree and (meta or inline): + raise ValueError("Visitor functions cannot combine 'tree' with 'meta' or 'inline'.") + + func = None + if meta: + if inline: + func = _vargs_meta_inline + else: + func = _vargs_meta + elif inline: + func = _vargs_inline + elif tree: + func = _vargs_tree + + if wrapper is not None: + if func is not None: + raise ValueError("Cannot use 'wrapper' along with 'tree', 'meta' or 'inline'.") + func = wrapper + + def _visitor_args_dec(obj): + return _apply_v_args(obj, func) + return _visitor_args_dec + + + +TOKEN_DEFAULT_PRIORITY = 0 + + +class Symbol(Serialize): + __slots__ = ('name',) + + name: str + is_term: ClassVar[bool] = NotImplemented + + def __init__(self, name: str) -> None: + self.name = name + + def __eq__(self, other): + if not isinstance(other, Symbol): + return NotImplemented + return self.is_term == other.is_term and self.name == other.name + + def __ne__(self, other): + return not (self == other) + + def __hash__(self): + return hash(self.name) + + def __repr__(self): + return '%s(%r)' % (type(self).__name__, self.name) + + fullrepr = property(__repr__) + + def renamed(self, f): + return type(self)(f(self.name)) + + +class Terminal(Symbol): + __serialize_fields__ = 'name', 'filter_out' + + is_term: ClassVar[bool] = True + + def __init__(self, name: str, filter_out: bool = False) -> None: + self.name = name + self.filter_out = filter_out + + @property + def fullrepr(self): + return '%s(%r, %r)' % (type(self).__name__, self.name, self.filter_out) + + def renamed(self, f): + return type(self)(f(self.name), self.filter_out) + + +class NonTerminal(Symbol): + __serialize_fields__ = 'name', + + is_term: ClassVar[bool] = False + + def serialize(self, memo=None) -> Dict[str, Any]: + ## + + ## + + return {'name': str(self.name), '__type__': 'NonTerminal'} + + +class RuleOptions(Serialize): + __serialize_fields__ = 'keep_all_tokens', 'expand1', 'priority', 'template_source', 'empty_indices' + + keep_all_tokens: bool + expand1: bool + priority: Optional[int] + template_source: Optional[str] + empty_indices: Tuple[bool, ...] + + def __init__(self, keep_all_tokens: bool=False, expand1: bool=False, priority: Optional[int]=None, template_source: Optional[str]=None, empty_indices: Tuple[bool, ...]=()) -> None: + self.keep_all_tokens = keep_all_tokens + self.expand1 = expand1 + self.priority = priority + self.template_source = template_source + self.empty_indices = empty_indices + + def __repr__(self): + return 'RuleOptions(%r, %r, %r, %r)' % ( + self.keep_all_tokens, + self.expand1, + self.priority, + self.template_source + ) + + +class Rule(Serialize): + #-- + __slots__ = ('origin', 'expansion', 'alias', 'options', 'order', '_hash') + + __serialize_fields__ = 'origin', 'expansion', 'order', 'alias', 'options' + __serialize_namespace__ = Terminal, NonTerminal, RuleOptions + + origin: NonTerminal + expansion: Sequence[Symbol] + order: int + alias: Optional[str] + options: RuleOptions + _hash: int + + def __init__(self, origin: NonTerminal, expansion: Sequence[Symbol], + order: int=0, alias: Optional[str]=None, options: Optional[RuleOptions]=None): + self.origin = origin + self.expansion = expansion + self.alias = alias + self.order = order + self.options = options or RuleOptions() + self._hash = hash((self.origin, tuple(self.expansion))) + + def _deserialize(self): + self._hash = hash((self.origin, tuple(self.expansion))) + + def __str__(self): + return '<%s : %s>' % (self.origin.name, ' '.join(x.name for x in self.expansion)) + + def __repr__(self): + return 'Rule(%r, %r, %r, %r)' % (self.origin, self.expansion, self.alias, self.options) + + def __hash__(self): + return self._hash + + def __eq__(self, other): + if not isinstance(other, Rule): + return False + return self.origin == other.origin and self.expansion == other.expansion + + + +from contextlib import suppress +from copy import copy + +try: ## + + has_interegular = bool(interegular) +except NameError: + has_interegular = False + +class Pattern(Serialize, ABC): + #-- + + value: str + flags: Collection[str] + raw: Optional[str] + type: ClassVar[str] + + def __init__(self, value: str, flags: Collection[str] = (), raw: Optional[str] = None) -> None: + self.value = value + self.flags = frozenset(flags) + self.raw = raw + + def __repr__(self): + return repr(self.to_regexp()) + + ## + + def __hash__(self): + return hash((type(self), self.value, self.flags)) + + def __eq__(self, other): + return type(self) == type(other) and self.value == other.value and self.flags == other.flags + + @abstractmethod + def to_regexp(self) -> str: + raise NotImplementedError() + + @property + @abstractmethod + def min_width(self) -> int: + raise NotImplementedError() + + @property + @abstractmethod + def max_width(self) -> int: + raise NotImplementedError() + + def _get_flags(self, value): + for f in self.flags: + value = ('(?%s:%s)' % (f, value)) + return value + + +class PatternStr(Pattern): + __serialize_fields__ = 'value', 'flags', 'raw' + + type: ClassVar[str] = "str" + + def to_regexp(self) -> str: + return self._get_flags(re.escape(self.value)) + + @property + def min_width(self) -> int: + return len(self.value) + + @property + def max_width(self) -> int: + return len(self.value) + + +class PatternRE(Pattern): + __serialize_fields__ = 'value', 'flags', 'raw', '_width' + + type: ClassVar[str] = "re" + + def to_regexp(self) -> str: + return self._get_flags(self.value) + + _width = None + def _get_width(self): + if self._width is None: + self._width = get_regexp_width(self.to_regexp()) + return self._width + + @property + def min_width(self) -> int: + return self._get_width()[0] + + @property + def max_width(self) -> int: + return self._get_width()[1] + + +class TerminalDef(Serialize): + #-- + __serialize_fields__ = 'name', 'pattern', 'priority' + __serialize_namespace__ = PatternStr, PatternRE + + name: str + pattern: Pattern + priority: int + + def __init__(self, name: str, pattern: Pattern, priority: int = TOKEN_DEFAULT_PRIORITY) -> None: + assert isinstance(pattern, Pattern), pattern + self.name = name + self.pattern = pattern + self.priority = priority + + def __repr__(self): + return '%s(%r, %r)' % (type(self).__name__, self.name, self.pattern) + + def user_repr(self) -> str: + if self.name.startswith('__'): ## + + return self.pattern.raw or self.name + else: + return self.name + +_T = TypeVar('_T', bound="Token") + +class Token(str): + #-- + __slots__ = ('type', 'start_pos', 'value', 'line', 'column', 'end_line', 'end_column', 'end_pos') + + __match_args__ = ('type', 'value') + + type: str + start_pos: Optional[int] + value: Any + line: Optional[int] + column: Optional[int] + end_line: Optional[int] + end_column: Optional[int] + end_pos: Optional[int] + + + @overload + def __new__( + cls, + type: str, + value: Any, + start_pos: Optional[int] = None, + line: Optional[int] = None, + column: Optional[int] = None, + end_line: Optional[int] = None, + end_column: Optional[int] = None, + end_pos: Optional[int] = None + ) -> 'Token': + ... + + @overload + def __new__( + cls, + type_: str, + value: Any, + start_pos: Optional[int] = None, + line: Optional[int] = None, + column: Optional[int] = None, + end_line: Optional[int] = None, + end_column: Optional[int] = None, + end_pos: Optional[int] = None + ) -> 'Token': ... + + def __new__(cls, *args, **kwargs): + if "type_" in kwargs: + warnings.warn("`type_` is deprecated use `type` instead", DeprecationWarning) + + if "type" in kwargs: + raise TypeError("Error: using both 'type' and the deprecated 'type_' as arguments.") + kwargs["type"] = kwargs.pop("type_") + + return cls._future_new(*args, **kwargs) + + + @classmethod + def _future_new(cls, type, value, start_pos=None, line=None, column=None, end_line=None, end_column=None, end_pos=None): + inst = super(Token, cls).__new__(cls, value) + + inst.type = type + inst.start_pos = start_pos + inst.value = value + inst.line = line + inst.column = column + inst.end_line = end_line + inst.end_column = end_column + inst.end_pos = end_pos + return inst + + @overload + def update(self, type: Optional[str] = None, value: Optional[Any] = None) -> 'Token': + ... + + @overload + def update(self, type_: Optional[str] = None, value: Optional[Any] = None) -> 'Token': + ... + + def update(self, *args, **kwargs): + if "type_" in kwargs: + warnings.warn("`type_` is deprecated use `type` instead", DeprecationWarning) + + if "type" in kwargs: + raise TypeError("Error: using both 'type' and the deprecated 'type_' as arguments.") + kwargs["type"] = kwargs.pop("type_") + + return self._future_update(*args, **kwargs) + + def _future_update(self, type: Optional[str] = None, value: Optional[Any] = None) -> 'Token': + return Token.new_borrow_pos( + type if type is not None else self.type, + value if value is not None else self.value, + self + ) + + @classmethod + def new_borrow_pos(cls: Type[_T], type_: str, value: Any, borrow_t: 'Token') -> _T: + return cls(type_, value, borrow_t.start_pos, borrow_t.line, borrow_t.column, borrow_t.end_line, borrow_t.end_column, borrow_t.end_pos) + + def __reduce__(self): + return (self.__class__, (self.type, self.value, self.start_pos, self.line, self.column)) + + def __repr__(self): + return 'Token(%r, %r)' % (self.type, self.value) + + def __deepcopy__(self, memo): + return Token(self.type, self.value, self.start_pos, self.line, self.column) + + def __eq__(self, other): + if isinstance(other, Token) and self.type != other.type: + return False + + return str.__eq__(self, other) + + __hash__ = str.__hash__ + + +class LineCounter: + #-- + + __slots__ = 'char_pos', 'line', 'column', 'line_start_pos', 'newline_char' + + def __init__(self, newline_char): + self.newline_char = newline_char + self.char_pos = 0 + self.line = 1 + self.column = 1 + self.line_start_pos = 0 + + def __eq__(self, other): + if not isinstance(other, LineCounter): + return NotImplemented + + return self.char_pos == other.char_pos and self.newline_char == other.newline_char + + def feed(self, token: TextOrSlice, test_newline=True): + #-- + if test_newline: + newlines = token.count(self.newline_char) + if newlines: + self.line += newlines + self.line_start_pos = self.char_pos + token.rindex(self.newline_char) + 1 + + self.char_pos += len(token) + self.column = self.char_pos - self.line_start_pos + 1 + + +class UnlessCallback: + def __init__(self, scanner: 'Scanner'): + self.scanner = scanner + + def __call__(self, t: Token): + res = self.scanner.fullmatch(t.value) + if res is not None: + t.type = res + return t + + +class CallChain: + def __init__(self, callback1, callback2, cond): + self.callback1 = callback1 + self.callback2 = callback2 + self.cond = cond + + def __call__(self, t): + t2 = self.callback1(t) + return self.callback2(t) if self.cond(t2) else t2 + + +def _get_match(re_, regexp, s, flags): + m = re_.match(regexp, s, flags) + if m: + return m.group(0) + +def _create_unless(terminals, g_regex_flags, re_, use_bytes): + tokens_by_type = classify(terminals, lambda t: type(t.pattern)) + assert len(tokens_by_type) <= 2, tokens_by_type.keys() + embedded_strs = set() + callback = {} + for retok in tokens_by_type.get(PatternRE, []): + unless = [] + for strtok in tokens_by_type.get(PatternStr, []): + if strtok.priority != retok.priority: + continue + s = strtok.pattern.value + if s == _get_match(re_, retok.pattern.to_regexp(), s, g_regex_flags): + unless.append(strtok) + if strtok.pattern.flags <= retok.pattern.flags: + embedded_strs.add(strtok) + if unless: + callback[retok.name] = UnlessCallback(Scanner(unless, g_regex_flags, re_, use_bytes=use_bytes)) + + new_terminals = [t for t in terminals if t not in embedded_strs] + return new_terminals, callback + + +class Scanner: + def __init__(self, terminals, g_regex_flags, re_, use_bytes): + self.terminals = terminals + self.g_regex_flags = g_regex_flags + self.re_ = re_ + self.use_bytes = use_bytes + + self.allowed_types = {t.name for t in self.terminals} + + self._mres = self._build_mres(terminals, len(terminals)) + + def _build_mres(self, terminals, max_size): + ## + + ## + + ## + + mres = [] + while terminals: + pattern = u'|'.join(u'(?P<%s>%s)' % (t.name, t.pattern.to_regexp()) for t in terminals[:max_size]) + if self.use_bytes: + pattern = pattern.encode('latin-1') + try: + mre = self.re_.compile(pattern, self.g_regex_flags) + except AssertionError: ## + + return self._build_mres(terminals, max_size // 2) + + mres.append(mre) + terminals = terminals[max_size:] + return mres + + def match(self, text: TextSlice, pos): + for mre in self._mres: + m = mre.match(text.text, pos, text.end) + if m: + return m.group(0), m.lastgroup + + + def fullmatch(self, text: str) -> Optional[str]: + for mre in self._mres: + m = mre.fullmatch(text) + if m: + return m.lastgroup + return None + +def _regexp_has_newline(r: str): + #-- + return '\n' in r or '\\n' in r or '\\s' in r or '[^' in r or ('(?s' in r and '.' in r) + + +class LexerState: + #-- + + __slots__ = 'text', 'line_ctr', 'last_token' + + text: TextSlice + line_ctr: LineCounter + last_token: Optional[Token] + + def __init__(self, text: TextSlice, line_ctr: Optional[LineCounter] = None, last_token: Optional[Token]=None): + if isinstance(text, TextSlice): + if line_ctr is None: + line_ctr = LineCounter(b'\n' if isinstance(text.text, bytes) else '\n') + + if text.start > 0: + ## + + line_ctr.feed(TextSlice(text.text, 0, text.start)) + + if not (text.start <= line_ctr.char_pos <= text.end): + raise ValueError("LineCounter.char_pos is out of bounds") + + self.text = text + if line_ctr is None: + line_ctr = LineCounter(b'\n' if isinstance(text, bytes) or (hasattr(text, 'text') and isinstance(text.text, bytes)) else '\n') + self.line_ctr = line_ctr + self.last_token = last_token + + + def __eq__(self, other): + if not isinstance(other, LexerState): + return NotImplemented + + return self.text == other.text and self.line_ctr == other.line_ctr and self.last_token == other.last_token + + def __copy__(self): + return type(self)(self.text, copy(self.line_ctr), self.last_token) + + +class LexerThread: + #-- + + def __init__(self, lexer: 'Lexer', lexer_state: Optional[LexerState]): + self.lexer = lexer + self.state = lexer_state + + @classmethod + def from_text(cls, lexer: 'Lexer', text_or_slice: TextOrSlice) -> 'LexerThread': + text = TextSlice.cast_from(text_or_slice) + return cls(lexer, LexerState(text)) + + @classmethod + def from_custom_input(cls, lexer: 'Lexer', text: Any) -> 'LexerThread': + return cls(lexer, LexerState(text)) + + def lex(self, parser_state): + if self.state is None: + raise TypeError("Cannot lex: No text assigned to lexer state") + return self.lexer.lex(self.state, parser_state) + + def __copy__(self): + return type(self)(self.lexer, copy(self.state)) + + _Token = Token + + +_Callback = Callable[[Token], Token] + +class Lexer(ABC): + #-- + @abstractmethod + def lex(self, lexer_state: LexerState, parser_state: Any) -> Iterator[Token]: + return NotImplemented + + def make_lexer_state(self, text: str): + #-- + return LexerState(TextSlice.cast_from(text)) + + +def _check_regex_collisions(terminal_to_regexp: Dict[TerminalDef, str], comparator, strict_mode, max_collisions_to_show=8): + if not comparator: + comparator = interegular.Comparator.from_regexes(terminal_to_regexp) + + ## + + ## + + max_time = 2 if strict_mode else 0.2 + + ## + + if comparator.count_marked_pairs() >= max_collisions_to_show: + return + for group in classify(terminal_to_regexp, lambda t: t.priority).values(): + for a, b in comparator.check(group, skip_marked=True): + assert a.priority == b.priority + ## + + comparator.mark(a, b) + + ## + + message = f"Collision between Terminals {a.name} and {b.name}. " + try: + example = comparator.get_example_overlap(a, b, max_time).format_multiline() + except ValueError: + ## + + example = "No example could be found fast enough. However, the collision does still exists" + if strict_mode: + raise LexError(f"{message}\n{example}") + logger.warning("%s The lexer will choose between them arbitrarily.\n%s", message, example) + if comparator.count_marked_pairs() >= max_collisions_to_show: + logger.warning("Found 8 regex collisions, will not check for more.") + return + + +class AbstractBasicLexer(Lexer): + terminals_by_name: Dict[str, TerminalDef] + + @abstractmethod + def __init__(self, conf: 'LexerConf', comparator=None) -> None: + ... + + @abstractmethod + def next_token(self, lex_state: LexerState, parser_state: Any = None) -> Token: + ... + + def lex(self, state: LexerState, parser_state: Any) -> Iterator[Token]: + with suppress(EOFError): + while True: + yield self.next_token(state, parser_state) + + +class BasicLexer(AbstractBasicLexer): + terminals: Collection[TerminalDef] + ignore_types: FrozenSet[str] + newline_types: FrozenSet[str] + user_callbacks: Dict[str, _Callback] + callback: Dict[str, _Callback] + re: ModuleType + + def __init__(self, conf: 'LexerConf', comparator=None) -> None: + terminals = list(conf.terminals) + assert all(isinstance(t, TerminalDef) for t in terminals), terminals + + self.re = conf.re_module + + if not conf.skip_validation: + ## + + terminal_to_regexp = {} + for t in terminals: + regexp = t.pattern.to_regexp() + try: + self.re.compile(regexp, conf.g_regex_flags) + except self.re.error: + raise LexError("Cannot compile token %s: %s" % (t.name, t.pattern)) + + if t.pattern.min_width == 0: + raise LexError("Lexer does not allow zero-width terminals. (%s: %s)" % (t.name, t.pattern)) + if t.pattern.type == "re": + terminal_to_regexp[t] = regexp + + if not (set(conf.ignore) <= {t.name for t in terminals}): + raise LexError("Ignore terminals are not defined: %s" % (set(conf.ignore) - {t.name for t in terminals})) + + if has_interegular: + _check_regex_collisions(terminal_to_regexp, comparator, conf.strict) + elif conf.strict: + raise LexError("interegular must be installed for strict mode. Use `pip install 'lark[interegular]'`.") + + ## + + self.newline_types = frozenset(t.name for t in terminals if _regexp_has_newline(t.pattern.to_regexp())) + self.ignore_types = frozenset(conf.ignore) + + terminals.sort(key=lambda x: (-x.priority, -x.pattern.max_width, -len(x.pattern.value), x.name)) + self.terminals = terminals + self.user_callbacks = conf.callbacks + self.g_regex_flags = conf.g_regex_flags + self.use_bytes = conf.use_bytes + self.terminals_by_name = conf.terminals_by_name + + self._scanner: Optional[Scanner] = None + + def _build_scanner(self) -> Scanner: + terminals, self.callback = _create_unless(self.terminals, self.g_regex_flags, self.re, self.use_bytes) + assert all(self.callback.values()) + + for type_, f in self.user_callbacks.items(): + if type_ in self.callback: + ## + + self.callback[type_] = CallChain(self.callback[type_], f, lambda t: t.type == type_) + else: + self.callback[type_] = f + + return Scanner(terminals, self.g_regex_flags, self.re, self.use_bytes) + + @property + def scanner(self) -> Scanner: + if self._scanner is None: + self._scanner = self._build_scanner() + return self._scanner + + def match(self, text, pos): + return self.scanner.match(text, pos) + + def next_token(self, lex_state: LexerState, parser_state: Any = None) -> Token: + line_ctr = lex_state.line_ctr + while line_ctr.char_pos < lex_state.text.end: + res = self.match(lex_state.text, line_ctr.char_pos) + if not res: + allowed = self.scanner.allowed_types - self.ignore_types + if not allowed: + allowed = {""} + raise UnexpectedCharacters(lex_state.text.text, line_ctr.char_pos, line_ctr.line, line_ctr.column, + allowed=allowed, token_history=lex_state.last_token and [lex_state.last_token], + state=parser_state, terminals_by_name=self.terminals_by_name) + + value, type_ = res + + ignored = type_ in self.ignore_types + t = None + if not ignored or type_ in self.callback: + t = Token(type_, value, line_ctr.char_pos, line_ctr.line, line_ctr.column) + line_ctr.feed(value, type_ in self.newline_types) + if t is not None: + t.end_line = line_ctr.line + t.end_column = line_ctr.column + t.end_pos = line_ctr.char_pos + if t.type in self.callback: + t = self.callback[t.type](t) + if not ignored: + if not isinstance(t, Token): + raise LexError("Callbacks must return a token (returned %r)" % t) + lex_state.last_token = t + return t + + ## + + raise EOFError(self) + + +class ContextualLexer(Lexer): + lexers: Dict[int, AbstractBasicLexer] + root_lexer: AbstractBasicLexer + + BasicLexer: Type[AbstractBasicLexer] = BasicLexer + + def __init__(self, conf: 'LexerConf', states: Dict[int, Collection[str]], always_accept: Collection[str]=()) -> None: + terminals = list(conf.terminals) + terminals_by_name = conf.terminals_by_name + + trad_conf = copy(conf) + trad_conf.terminals = terminals + + if has_interegular and not conf.skip_validation: + comparator = interegular.Comparator.from_regexes({t: t.pattern.to_regexp() for t in terminals}) + else: + comparator = None + lexer_by_tokens: Dict[FrozenSet[str], AbstractBasicLexer] = {} + self.lexers = {} + for state, accepts in states.items(): + key = frozenset(accepts) + try: + lexer = lexer_by_tokens[key] + except KeyError: + accepts = set(accepts) | set(conf.ignore) | set(always_accept) + lexer_conf = copy(trad_conf) + lexer_conf.terminals = [terminals_by_name[n] for n in accepts if n in terminals_by_name] + lexer = self.BasicLexer(lexer_conf, comparator) + lexer_by_tokens[key] = lexer + + self.lexers[state] = lexer + + assert trad_conf.terminals is terminals + trad_conf.skip_validation = True ## + + self.root_lexer = self.BasicLexer(trad_conf, comparator) + + def lex(self, lexer_state: LexerState, parser_state: 'ParserState') -> Iterator[Token]: + try: + while True: + lexer = self.lexers[parser_state.position] + yield lexer.next_token(lexer_state, parser_state) + except EOFError: + pass + except UnexpectedCharacters as e: + ## + + ## + + try: + last_token = lexer_state.last_token ## + + token = self.root_lexer.next_token(lexer_state, parser_state) + raise UnexpectedToken(token, e.allowed, state=parser_state, token_history=[last_token], terminals_by_name=self.root_lexer.terminals_by_name) + except UnexpectedCharacters: + raise e ## + + + + +_ParserArgType: 'TypeAlias' = 'Literal["earley", "lalr", "cyk", "auto"]' +_LexerArgType: 'TypeAlias' = 'Union[Literal["auto", "basic", "contextual", "dynamic", "dynamic_complete"], Type[Lexer]]' +_LexerCallback = Callable[[Token], Token] +ParserCallbacks = Dict[str, Callable] + +class LexerConf(Serialize): + __serialize_fields__ = 'terminals', 'ignore', 'g_regex_flags', 'use_bytes', 'lexer_type' + __serialize_namespace__ = TerminalDef, + + terminals: Collection[TerminalDef] + re_module: ModuleType + ignore: Collection[str] + postlex: 'Optional[PostLex]' + callbacks: Dict[str, _LexerCallback] + g_regex_flags: int + skip_validation: bool + use_bytes: bool + lexer_type: Optional[_LexerArgType] + strict: bool + + def __init__(self, terminals: Collection[TerminalDef], re_module: ModuleType, ignore: Collection[str]=(), postlex: 'Optional[PostLex]'=None, + callbacks: Optional[Dict[str, _LexerCallback]]=None, g_regex_flags: int=0, skip_validation: bool=False, use_bytes: bool=False, strict: bool=False): + self.terminals = terminals + self.terminals_by_name = {t.name: t for t in self.terminals} + assert len(self.terminals) == len(self.terminals_by_name) + self.ignore = ignore + self.postlex = postlex + self.callbacks = callbacks or {} + self.g_regex_flags = g_regex_flags + self.re_module = re_module + self.skip_validation = skip_validation + self.use_bytes = use_bytes + self.strict = strict + self.lexer_type = None + + def _deserialize(self): + self.terminals_by_name = {t.name: t for t in self.terminals} + + def __deepcopy__(self, memo=None): + return type(self)( + deepcopy(self.terminals, memo), + self.re_module, + deepcopy(self.ignore, memo), + deepcopy(self.postlex, memo), + deepcopy(self.callbacks, memo), + deepcopy(self.g_regex_flags, memo), + deepcopy(self.skip_validation, memo), + deepcopy(self.use_bytes, memo), + ) + +class ParserConf(Serialize): + __serialize_fields__ = 'rules', 'start', 'parser_type' + + rules: List['Rule'] + callbacks: ParserCallbacks + start: List[str] + parser_type: _ParserArgType + + def __init__(self, rules: List['Rule'], callbacks: ParserCallbacks, start: List[str]): + assert isinstance(start, list) + self.rules = rules + self.callbacks = callbacks + self.start = start + + +from functools import partial, wraps +from itertools import product + + +class ExpandSingleChild: + def __init__(self, node_builder): + self.node_builder = node_builder + + def __call__(self, children): + if len(children) == 1: + return children[0] + else: + return self.node_builder(children) + + + +class PropagatePositions: + def __init__(self, node_builder, node_filter=None): + self.node_builder = node_builder + self.node_filter = node_filter + + def __call__(self, children): + res = self.node_builder(children) + + if isinstance(res, Tree): + ## + + ## + + ## + + ## + + + res_meta = res.meta + + first_meta = self._pp_get_meta(children) + if first_meta is not None: + if not hasattr(res_meta, 'line'): + ## + + res_meta.line = getattr(first_meta, 'container_line', first_meta.line) + res_meta.column = getattr(first_meta, 'container_column', first_meta.column) + res_meta.start_pos = getattr(first_meta, 'container_start_pos', first_meta.start_pos) + res_meta.empty = False + + res_meta.container_line = getattr(first_meta, 'container_line', first_meta.line) + res_meta.container_column = getattr(first_meta, 'container_column', first_meta.column) + res_meta.container_start_pos = getattr(first_meta, 'container_start_pos', first_meta.start_pos) + + last_meta = self._pp_get_meta(reversed(children)) + if last_meta is not None: + if not hasattr(res_meta, 'end_line'): + res_meta.end_line = getattr(last_meta, 'container_end_line', last_meta.end_line) + res_meta.end_column = getattr(last_meta, 'container_end_column', last_meta.end_column) + res_meta.end_pos = getattr(last_meta, 'container_end_pos', last_meta.end_pos) + res_meta.empty = False + + res_meta.container_end_line = getattr(last_meta, 'container_end_line', last_meta.end_line) + res_meta.container_end_column = getattr(last_meta, 'container_end_column', last_meta.end_column) + res_meta.container_end_pos = getattr(last_meta, 'container_end_pos', last_meta.end_pos) + + return res + + def _pp_get_meta(self, children): + for c in children: + if self.node_filter is not None and not self.node_filter(c): + continue + if isinstance(c, Tree): + if not c.meta.empty: + return c.meta + elif isinstance(c, Token): + return c + elif hasattr(c, '__lark_meta__'): + return c.__lark_meta__() + +def make_propagate_positions(option): + if callable(option): + return partial(PropagatePositions, node_filter=option) + elif option is True: + return PropagatePositions + elif option is False: + return None + + raise ConfigurationError('Invalid option for propagate_positions: %r' % option) + + +class ChildFilter: + def __init__(self, to_include, append_none, node_builder): + self.node_builder = node_builder + self.to_include = to_include + self.append_none = append_none + + def __call__(self, children): + filtered = [] + + for i, to_expand, add_none in self.to_include: + if add_none: + filtered += [None] * add_none + if to_expand: + filtered += children[i].children + else: + filtered.append(children[i]) + + if self.append_none: + filtered += [None] * self.append_none + + return self.node_builder(filtered) + + +class ChildFilterLALR(ChildFilter): + #-- + + def __call__(self, children): + filtered = [] + for i, to_expand, add_none in self.to_include: + if add_none: + filtered += [None] * add_none + if to_expand: + if filtered: + filtered += children[i].children + else: ## + + filtered = children[i].children + else: + filtered.append(children[i]) + + if self.append_none: + filtered += [None] * self.append_none + + return self.node_builder(filtered) + + +class ChildFilterLALR_NoPlaceholders(ChildFilter): + #-- + def __init__(self, to_include, node_builder): + self.node_builder = node_builder + self.to_include = to_include + + def __call__(self, children): + filtered = [] + for i, to_expand in self.to_include: + if to_expand: + if filtered: + filtered += children[i].children + else: ## + + filtered = children[i].children + else: + filtered.append(children[i]) + return self.node_builder(filtered) + + +def _should_expand(sym): + return not sym.is_term and sym.name.startswith('_') + + +def maybe_create_child_filter(expansion, keep_all_tokens, ambiguous, _empty_indices: List[bool]): + ## + + if _empty_indices: + assert _empty_indices.count(False) == len(expansion) + s = ''.join(str(int(b)) for b in _empty_indices) + empty_indices = [len(ones) for ones in s.split('0')] + assert len(empty_indices) == len(expansion)+1, (empty_indices, len(expansion)) + else: + empty_indices = [0] * (len(expansion)+1) + + to_include = [] + nones_to_add = 0 + for i, sym in enumerate(expansion): + nones_to_add += empty_indices[i] + if keep_all_tokens or not (sym.is_term and sym.filter_out): + to_include.append((i, _should_expand(sym), nones_to_add)) + nones_to_add = 0 + + nones_to_add += empty_indices[len(expansion)] + + if _empty_indices or len(to_include) < len(expansion) or any(to_expand for i, to_expand,_ in to_include): + if _empty_indices or ambiguous: + return partial(ChildFilter if ambiguous else ChildFilterLALR, to_include, nones_to_add) + else: + ## + + return partial(ChildFilterLALR_NoPlaceholders, [(i, x) for i,x,_ in to_include]) + + +class AmbiguousExpander: + #-- + def __init__(self, to_expand, tree_class, node_builder): + self.node_builder = node_builder + self.tree_class = tree_class + self.to_expand = to_expand + + def __call__(self, children): + def _is_ambig_tree(t): + return hasattr(t, 'data') and t.data == '_ambig' + + ## + + ## + + ## + + ## + + ambiguous = [] + for i, child in enumerate(children): + if _is_ambig_tree(child): + if i in self.to_expand: + ambiguous.append(i) + + child.expand_kids_by_data('_ambig') + + if not ambiguous: + return self.node_builder(children) + + expand = [child.children if i in ambiguous else (child,) for i, child in enumerate(children)] + return self.tree_class('_ambig', [self.node_builder(list(f)) for f in product(*expand)]) + + +def maybe_create_ambiguous_expander(tree_class, expansion, keep_all_tokens): + to_expand = [i for i, sym in enumerate(expansion) + if keep_all_tokens or ((not (sym.is_term and sym.filter_out)) and _should_expand(sym))] + if to_expand: + return partial(AmbiguousExpander, to_expand, tree_class) + + +class AmbiguousIntermediateExpander: + #-- + + def __init__(self, tree_class, node_builder): + self.node_builder = node_builder + self.tree_class = tree_class + + def __call__(self, children): + def _is_iambig_tree(child): + return hasattr(child, 'data') and child.data == '_iambig' + + def _collapse_iambig(children): + #-- + + ## + + ## + + if children and _is_iambig_tree(children[0]): + iambig_node = children[0] + result = [] + for grandchild in iambig_node.children: + collapsed = _collapse_iambig(grandchild.children) + if collapsed: + for child in collapsed: + child.children += children[1:] + result += collapsed + else: + new_tree = self.tree_class('_inter', grandchild.children + children[1:]) + result.append(new_tree) + return result + + collapsed = _collapse_iambig(children) + if collapsed: + processed_nodes = [self.node_builder(c.children) for c in collapsed] + return self.tree_class('_ambig', processed_nodes) + + return self.node_builder(children) + + + +def inplace_transformer(func): + @wraps(func) + def f(children): + ## + + tree = Tree(func.__name__, children) + return func(tree) + return f + + +def apply_visit_wrapper(func, name, wrapper): + if wrapper is _vargs_meta or wrapper is _vargs_meta_inline: + raise NotImplementedError("Meta args not supported for internal transformer; use YourTransformer().transform(parser.parse()) instead") + + @wraps(func) + def f(children): + return wrapper(func, name, children, None) + return f + + +class ParseTreeBuilder: + def __init__(self, rules, tree_class, propagate_positions=False, ambiguous=False, maybe_placeholders=False): + self.tree_class = tree_class + self.propagate_positions = propagate_positions + self.ambiguous = ambiguous + self.maybe_placeholders = maybe_placeholders + + self.rule_builders = list(self._init_builders(rules)) + + def _init_builders(self, rules): + propagate_positions = make_propagate_positions(self.propagate_positions) + + for rule in rules: + options = rule.options + keep_all_tokens = options.keep_all_tokens + expand_single_child = options.expand1 + + wrapper_chain = list(filter(None, [ + (expand_single_child and not rule.alias) and ExpandSingleChild, + maybe_create_child_filter(rule.expansion, keep_all_tokens, self.ambiguous, options.empty_indices if self.maybe_placeholders else None), + propagate_positions, + self.ambiguous and maybe_create_ambiguous_expander(self.tree_class, rule.expansion, keep_all_tokens), + self.ambiguous and partial(AmbiguousIntermediateExpander, self.tree_class) + ])) + + yield rule, wrapper_chain + + def create_callback(self, transformer=None): + callbacks = {} + + default_handler = getattr(transformer, '__default__', None) + if default_handler: + def default_callback(data, children): + return default_handler(data, children, None) + else: + default_callback = self.tree_class + + for rule, wrapper_chain in self.rule_builders: + + user_callback_name = rule.alias or rule.options.template_source or rule.origin.name + try: + f = getattr(transformer, user_callback_name) + wrapper = getattr(f, 'visit_wrapper', None) + if wrapper is not None: + f = apply_visit_wrapper(f, user_callback_name, wrapper) + elif isinstance(transformer, Transformer_InPlace): + f = inplace_transformer(f) + except AttributeError: + f = partial(default_callback, user_callback_name) + + for w in wrapper_chain: + f = w(f) + + if rule in callbacks: + raise GrammarError("Rule '%s' already exists" % (rule,)) + + callbacks[rule] = f + + return callbacks + + + +class Action: + def __init__(self, name): + self.name = name + def __str__(self): + return self.name + def __repr__(self): + return str(self) + +Shift = Action('Shift') +Reduce = Action('Reduce') + +StateT = TypeVar("StateT") + +class ParseTableBase(Generic[StateT]): + states: Dict[StateT, Dict[str, Tuple]] + start_states: Dict[str, StateT] + end_states: Dict[str, StateT] + + def __init__(self, states, start_states, end_states): + self.states = states + self.start_states = start_states + self.end_states = end_states + + def serialize(self, memo): + tokens = Enumerator() + + states = { + state: {tokens.get(token): ((1, arg.serialize(memo)) if action is Reduce else (0, arg)) + for token, (action, arg) in actions.items()} + for state, actions in self.states.items() + } + + return { + 'tokens': tokens.reversed(), + 'states': states, + 'start_states': self.start_states, + 'end_states': self.end_states, + } + + @classmethod + def deserialize(cls, data, memo): + tokens = data['tokens'] + states = { + state: {tokens[token]: ((Reduce, Rule.deserialize(arg, memo)) if action==1 else (Shift, arg)) + for token, (action, arg) in actions.items()} + for state, actions in data['states'].items() + } + return cls(states, data['start_states'], data['end_states']) + +class ParseTable(ParseTableBase['State']): + #-- + pass + + +class IntParseTable(ParseTableBase[int]): + #-- + + @classmethod + def from_ParseTable(cls, parse_table: ParseTable): + enum = list(parse_table.states) + state_to_idx: Dict['State', int] = {s:i for i,s in enumerate(enum)} + int_states = {} + + for s, la in parse_table.states.items(): + la = {k:(v[0], state_to_idx[v[1]]) if v[0] is Shift else v + for k,v in la.items()} + int_states[ state_to_idx[s] ] = la + + + start_states = {start:state_to_idx[s] for start, s in parse_table.start_states.items()} + end_states = {start:state_to_idx[s] for start, s in parse_table.end_states.items()} + return cls(int_states, start_states, end_states) + + + +class ParseConf(Generic[StateT]): + __slots__ = 'parse_table', 'callbacks', 'start', 'start_state', 'end_state', 'states' + + parse_table: ParseTableBase[StateT] + callbacks: ParserCallbacks + start: str + + start_state: StateT + end_state: StateT + states: Dict[StateT, Dict[str, tuple]] + + def __init__(self, parse_table: ParseTableBase[StateT], callbacks: ParserCallbacks, start: str): + self.parse_table = parse_table + + self.start_state = self.parse_table.start_states[start] + self.end_state = self.parse_table.end_states[start] + self.states = self.parse_table.states + + self.callbacks = callbacks + self.start = start + +class ParserState(Generic[StateT]): + __slots__ = 'parse_conf', 'lexer', 'state_stack', 'value_stack' + + parse_conf: ParseConf[StateT] + lexer: LexerThread + state_stack: List[StateT] + value_stack: list + + def __init__(self, parse_conf: ParseConf[StateT], lexer: LexerThread, state_stack=None, value_stack=None): + self.parse_conf = parse_conf + self.lexer = lexer + self.state_stack = state_stack or [self.parse_conf.start_state] + self.value_stack = value_stack or [] + + @property + def position(self) -> StateT: + return self.state_stack[-1] + + ## + + def __eq__(self, other) -> bool: + if not isinstance(other, ParserState): + return NotImplemented + return len(self.state_stack) == len(other.state_stack) and self.position == other.position + + def __copy__(self): + return self.copy() + + def copy(self, deepcopy_values=True) -> 'ParserState[StateT]': + return type(self)( + self.parse_conf, + self.lexer, ## + + copy(self.state_stack), + deepcopy(self.value_stack) if deepcopy_values else copy(self.value_stack), + ) + + def feed_token(self, token: Token, is_end=False) -> Any: + state_stack = self.state_stack + value_stack = self.value_stack + states = self.parse_conf.states + end_state = self.parse_conf.end_state + callbacks = self.parse_conf.callbacks + + while True: + state = state_stack[-1] + try: + action, arg = states[state][token.type] + except KeyError: + expected = {s for s in states[state].keys() if s.isupper()} + raise UnexpectedToken(token, expected, state=self, interactive_parser=None) + + assert arg != end_state + + if action is Shift: + ## + + assert not is_end + state_stack.append(arg) + value_stack.append(token if token.type not in callbacks else callbacks[token.type](token)) + return + else: + ## + + rule = arg + size = len(rule.expansion) + if size: + s = value_stack[-size:] + del state_stack[-size:] + del value_stack[-size:] + else: + s = [] + + value = callbacks[rule](s) if callbacks else s + + _action, new_state = states[state_stack[-1]][rule.origin.name] + assert _action is Shift + state_stack.append(new_state) + value_stack.append(value) + + if is_end and state_stack[-1] == end_state: + return value_stack[-1] + + +class LALR_Parser(Serialize): + def __init__(self, parser_conf: ParserConf, debug: bool=False, strict: bool=False): + analysis = LALR_Analyzer(parser_conf, debug=debug, strict=strict) + analysis.compute_lalr() + callbacks = parser_conf.callbacks + + self._parse_table = analysis.parse_table + self.parser_conf = parser_conf + self.parser = _Parser(analysis.parse_table, callbacks, debug) + + @classmethod + def deserialize(cls, data, memo, callbacks, debug=False): + inst = cls.__new__(cls) + inst._parse_table = IntParseTable.deserialize(data, memo) + inst.parser = _Parser(inst._parse_table, callbacks, debug) + return inst + + def serialize(self, memo: Any = None) -> Dict[str, Any]: + return self._parse_table.serialize(memo) + + def parse_interactive(self, lexer: LexerThread, start: str): + return self.parser.parse(lexer, start, start_interactive=True) + + def parse(self, lexer, start, on_error=None): + try: + return self.parser.parse(lexer, start) + except UnexpectedInput as e: + if on_error is None: + raise + + while True: + if isinstance(e, UnexpectedCharacters): + s = e.interactive_parser.lexer_thread.state + p = s.line_ctr.char_pos + + if not on_error(e): + raise e + + if isinstance(e, UnexpectedCharacters): + ## + + if p == s.line_ctr.char_pos: + s.line_ctr.feed(s.text.text[p:p+1]) + + try: + return e.interactive_parser.resume_parse() + except UnexpectedToken as e2: + if (isinstance(e, UnexpectedToken) + and e.token.type == e2.token.type == '$END' + and e.interactive_parser == e2.interactive_parser): + ## + + raise e2 + e = e2 + except UnexpectedCharacters as e2: + e = e2 + + +class _Parser: + parse_table: ParseTableBase + callbacks: ParserCallbacks + debug: bool + + def __init__(self, parse_table: ParseTableBase, callbacks: ParserCallbacks, debug: bool=False): + self.parse_table = parse_table + self.callbacks = callbacks + self.debug = debug + + def parse(self, lexer: LexerThread, start: str, value_stack=None, state_stack=None, start_interactive=False): + parse_conf = ParseConf(self.parse_table, self.callbacks, start) + parser_state = ParserState(parse_conf, lexer, state_stack, value_stack) + if start_interactive: + return InteractiveParser(self, parser_state, parser_state.lexer) + return self.parse_from_state(parser_state) + + + def parse_from_state(self, state: ParserState, last_token: Optional[Token]=None): + #-- + try: + token = last_token + for token in state.lexer.lex(state): + assert token is not None + state.feed_token(token) + + end_token = Token.new_borrow_pos('$END', '', token) if token else Token('$END', '', 0, 1, 1) + return state.feed_token(end_token, True) + except UnexpectedInput as e: + try: + e.interactive_parser = InteractiveParser(self, state, state.lexer) + except NameError: + pass + raise e + except Exception as e: + if self.debug: + print("") + print("STATE STACK DUMP") + print("----------------") + for i, s in enumerate(state.state_stack): + print('%d)' % i , s) + print("") + + raise + + +class InteractiveParser: + #-- + def __init__(self, parser, parser_state: ParserState, lexer_thread: LexerThread): + self.parser = parser + self.parser_state = parser_state + self.lexer_thread = lexer_thread + self.result = None + + @property + def lexer_state(self) -> LexerThread: + warnings.warn("lexer_state will be removed in subsequent releases. Use lexer_thread instead.", DeprecationWarning) + return self.lexer_thread + + def feed_token(self, token: Token): + #-- + return self.parser_state.feed_token(token, token.type == '$END') + + def iter_parse(self) -> Iterator[Token]: + #-- + for token in self.lexer_thread.lex(self.parser_state): + yield token + self.result = self.feed_token(token) + + def exhaust_lexer(self) -> List[Token]: + #-- + return list(self.iter_parse()) + + + def feed_eof(self, last_token=None): + #-- + eof = Token.new_borrow_pos('$END', '', last_token) if last_token is not None else self.lexer_thread._Token('$END', '', 0, 1, 1) + return self.feed_token(eof) + + + def __copy__(self): + #-- + return self.copy() + + def copy(self, deepcopy_values=True): + return type(self)( + self.parser, + self.parser_state.copy(deepcopy_values=deepcopy_values), + copy(self.lexer_thread), + ) + + def __eq__(self, other): + if not isinstance(other, InteractiveParser): + return False + + return self.parser_state == other.parser_state and self.lexer_thread == other.lexer_thread + + def as_immutable(self): + #-- + p = copy(self) + return ImmutableInteractiveParser(p.parser, p.parser_state, p.lexer_thread) + + def pretty(self): + #-- + out = ["Parser choices:"] + for k, v in self.choices().items(): + out.append('\t- %s -> %r' % (k, v)) + out.append('stack size: %s' % len(self.parser_state.state_stack)) + return '\n'.join(out) + + def choices(self): + #-- + return self.parser_state.parse_conf.parse_table.states[self.parser_state.position] + + def accepts(self): + #-- + accepts = set() + conf_no_callbacks = copy(self.parser_state.parse_conf) + ## + + ## + + conf_no_callbacks.callbacks = {} + for t in self.choices(): + if t.isupper(): ## + + new_cursor = self.copy(deepcopy_values=False) + new_cursor.parser_state.parse_conf = conf_no_callbacks + try: + new_cursor.feed_token(self.lexer_thread._Token(t, '')) + except UnexpectedToken: + pass + else: + accepts.add(t) + return accepts + + def resume_parse(self): + #-- + return self.parser.parse_from_state(self.parser_state, last_token=self.lexer_thread.state.last_token) + + + +class ImmutableInteractiveParser(InteractiveParser): + #-- + + result = None + + def __hash__(self): + return hash((self.parser_state, self.lexer_thread)) + + def feed_token(self, token): + c = copy(self) + c.result = InteractiveParser.feed_token(c, token) + return c + + def exhaust_lexer(self): + #-- + cursor = self.as_mutable() + cursor.exhaust_lexer() + return cursor.as_immutable() + + def as_mutable(self): + #-- + p = copy(self) + return InteractiveParser(p.parser, p.parser_state, p.lexer_thread) + + + +def _wrap_lexer(lexer_class): + future_interface = getattr(lexer_class, '__future_interface__', 0) + if future_interface == 2: + return lexer_class + elif future_interface == 1: + class CustomLexerWrapper1(Lexer): + def __init__(self, lexer_conf): + self.lexer = lexer_class(lexer_conf) + def lex(self, lexer_state, parser_state): + if isinstance(lexer_state.text, TextSlice) and not lexer_state.text.is_complete_text(): + raise TypeError("Interface=1 Custom Lexer don't support TextSlice") + lexer_state.text = lexer_state.text + return self.lexer.lex(lexer_state, parser_state) + return CustomLexerWrapper1 + elif future_interface == 0: + class CustomLexerWrapper0(Lexer): + def __init__(self, lexer_conf): + self.lexer = lexer_class(lexer_conf) + + def lex(self, lexer_state, parser_state): + if isinstance(lexer_state.text, TextSlice): + if not lexer_state.text.is_complete_text(): + raise TypeError("Interface=0 Custom Lexer don't support TextSlice") + return self.lexer.lex(lexer_state.text.text) + return self.lexer.lex(lexer_state.text) + return CustomLexerWrapper0 + else: + raise ValueError(f"Unknown __future_interface__ value {future_interface}, integer 0-2 expected") + + +def _deserialize_parsing_frontend(data, memo, lexer_conf, callbacks, options): + parser_conf = ParserConf.deserialize(data['parser_conf'], memo) + cls = (options and options._plugins.get('LALR_Parser')) or LALR_Parser + parser = cls.deserialize(data['parser'], memo, callbacks, options.debug) + parser_conf.callbacks = callbacks + return ParsingFrontend(lexer_conf, parser_conf, options, parser=parser) + + +_parser_creators: 'Dict[str, Callable[[LexerConf, Any, Any], Any]]' = {} + + +class ParsingFrontend(Serialize): + __serialize_fields__ = 'lexer_conf', 'parser_conf', 'parser' + + lexer_conf: LexerConf + parser_conf: ParserConf + options: Any + + def __init__(self, lexer_conf: LexerConf, parser_conf: ParserConf, options, parser=None): + self.parser_conf = parser_conf + self.lexer_conf = lexer_conf + self.options = options + + ## + + if parser: ## + + self.parser = parser + else: + create_parser = _parser_creators.get(parser_conf.parser_type) + assert create_parser is not None, "{} is not supported in standalone mode".format( + parser_conf.parser_type + ) + self.parser = create_parser(lexer_conf, parser_conf, options) + + ## + + lexer_type = options.lexer if (options and options.lexer) else lexer_conf.lexer_type + self.skip_lexer = False + if lexer_type in ('dynamic', 'dynamic_complete'): + assert lexer_conf.postlex is None + self.skip_lexer = True + return + + if isinstance(lexer_type, type): + assert issubclass(lexer_type, Lexer) or hasattr(lexer_type, 'lex') + self.lexer = _wrap_lexer(lexer_type)(lexer_conf) + elif isinstance(lexer_type, str): + create_lexer = { + 'basic': create_basic_lexer, + 'contextual': create_contextual_lexer, + }[lexer_type] + self.lexer = create_lexer(lexer_conf, self.parser, lexer_conf.postlex, options) + else: + raise TypeError("Bad value for lexer_type: {lexer_type}") + + if lexer_conf.postlex: + self.lexer = PostLexConnector(self.lexer, lexer_conf.postlex) + + def _verify_start(self, start=None): + if start is None: + start_decls = self.parser_conf.start + if len(start_decls) > 1: + raise ConfigurationError("Lark initialized with more than 1 possible start rule. Must specify which start rule to parse", start_decls) + start ,= start_decls + elif start not in self.parser_conf.start: + raise ConfigurationError("Unknown start rule %s. Must be one of %r" % (start, self.parser_conf.start)) + return start + + def _make_lexer_thread(self, text: Optional[LarkInput]) -> Union[LarkInput, LexerThread, None]: + cls = (self.options and self.options._plugins.get('LexerThread')) or LexerThread + if self.skip_lexer: + return text + if text is None: + return cls(self.lexer, None) + if isinstance(text, (str, bytes, TextSlice)): + return cls.from_text(self.lexer, text) + return cls.from_custom_input(self.lexer, text) + + def parse(self, text: Optional[LarkInput], start=None, on_error=None): + if self.lexer_conf.lexer_type in ("dynamic", "dynamic_complete"): + if isinstance(text, TextSlice) and not text.is_complete_text(): + raise TypeError(f"Lexer {self.lexer_conf.lexer_type} does not support text slices.") + + chosen_start = self._verify_start(start) + kw = {} if on_error is None else {'on_error': on_error} + stream = self._make_lexer_thread(text) + return self.parser.parse(stream, chosen_start, **kw) + + def parse_interactive(self, text: Optional[TextOrSlice]=None, start=None): + ## + + ## + + chosen_start = self._verify_start(start) + if self.parser_conf.parser_type != 'lalr': + raise ConfigurationError("parse_interactive() currently only works with parser='lalr' ") + stream = self._make_lexer_thread(text) + return self.parser.parse_interactive(stream, chosen_start) + + +def _validate_frontend_args(parser, lexer) -> None: + assert_config(parser, ('lalr', 'earley', 'cyk')) + if not isinstance(lexer, type): ## + + expected = { + 'lalr': ('basic', 'contextual'), + 'earley': ('basic', 'dynamic', 'dynamic_complete'), + 'cyk': ('basic', ), + }[parser] + assert_config(lexer, expected, 'Parser %r does not support lexer %%r, expected one of %%s' % parser) + + +def _get_lexer_callbacks(transformer, terminals): + result = {} + for terminal in terminals: + callback = getattr(transformer, terminal.name, None) + if callback is not None: + result[terminal.name] = callback + return result + +class PostLexConnector: + def __init__(self, lexer, postlexer): + self.lexer = lexer + self.postlexer = postlexer + + def lex(self, lexer_state, parser_state): + i = self.lexer.lex(lexer_state, parser_state) + return self.postlexer.process(i) + + + +def create_basic_lexer(lexer_conf, parser, postlex, options) -> BasicLexer: + cls = (options and options._plugins.get('BasicLexer')) or BasicLexer + return cls(lexer_conf) + +def create_contextual_lexer(lexer_conf: LexerConf, parser, postlex, options) -> ContextualLexer: + cls = (options and options._plugins.get('ContextualLexer')) or ContextualLexer + parse_table: ParseTableBase[int] = parser._parse_table + states: Dict[int, Collection[str]] = {idx:list(t.keys()) for idx, t in parse_table.states.items()} + always_accept: Collection[str] = postlex.always_accept if postlex else () + return cls(lexer_conf, states, always_accept=always_accept) + +def create_lalr_parser(lexer_conf: LexerConf, parser_conf: ParserConf, options=None) -> LALR_Parser: + debug = options.debug if options else False + strict = options.strict if options else False + cls = (options and options._plugins.get('LALR_Parser')) or LALR_Parser + return cls(parser_conf, debug=debug, strict=strict) + +_parser_creators['lalr'] = create_lalr_parser + + + + +class PostLex(ABC): + @abstractmethod + def process(self, stream: Iterator[Token]) -> Iterator[Token]: + return stream + + always_accept: Iterable[str] = () + +class LarkOptions(Serialize): + #-- + + start: List[str] + debug: bool + strict: bool + transformer: 'Optional[Transformer]' + propagate_positions: Union[bool, str] + maybe_placeholders: bool + cache: Union[bool, str] + cache_grammar: bool + regex: bool + g_regex_flags: int + keep_all_tokens: bool + tree_class: Optional[Callable[[str, List], Any]] + parser: _ParserArgType + lexer: _LexerArgType + ambiguity: 'Literal["auto", "resolve", "explicit", "forest"]' + postlex: Optional[PostLex] + priority: 'Optional[Literal["auto", "normal", "invert"]]' + lexer_callbacks: Dict[str, Callable[[Token], Token]] + use_bytes: bool + ordered_sets: bool + edit_terminals: Optional[Callable[[TerminalDef], TerminalDef]] + import_paths: 'List[Union[str, Callable[[Union[None, str, PackageResource], str], Tuple[str, str]]]]' + source_path: Optional[str] + + OPTIONS_DOC = r""" + **=== General Options ===** + + start + The start symbol. Either a string, or a list of strings for multiple possible starts (Default: "start") + debug + Display debug information and extra warnings. Use only when debugging (Default: ``False``) + When used with Earley, it generates a forest graph as "sppf.png", if 'dot' is installed. + strict + Throw an exception on any potential ambiguity, including shift/reduce conflicts, and regex collisions. + transformer + Applies the transformer to every parse tree (equivalent to applying it after the parse, but faster) + propagate_positions + Propagates positional attributes into the 'meta' attribute of all tree branches. + Sets attributes: (line, column, end_line, end_column, start_pos, end_pos, + container_line, container_column, container_end_line, container_end_column) + Accepts ``False``, ``True``, or a callable, which will filter which nodes to ignore when propagating. + maybe_placeholders + When ``True``, the ``[]`` operator returns ``None`` when not matched. + When ``False``, ``[]`` behaves like the ``?`` operator, and returns no value at all. + (default= ``True``) + cache + Cache the results of the Lark grammar analysis, for x2 to x3 faster loading. LALR only for now. + + - When ``False``, does nothing (default) + - When ``True``, caches to a temporary file in the local directory + - When given a string, caches to the path pointed by the string + cache_grammar + For use with ``cache`` option. When ``True``, the unanalyzed grammar is also included in the cache. + Useful for classes that require the ``Lark.grammar`` to be present (e.g. Reconstructor). + (default= ``False``) + regex + When True, uses the ``regex`` module instead of the stdlib ``re``. + g_regex_flags + Flags that are applied to all terminals (both regex and strings) + keep_all_tokens + Prevent the tree builder from automagically removing "punctuation" tokens (Default: ``False``) + tree_class + Lark will produce trees comprised of instances of this class instead of the default ``lark.Tree``. + + **=== Algorithm Options ===** + + parser + Decides which parser engine to use. Accepts "earley" or "lalr". (Default: "earley"). + (there is also a "cyk" option for legacy) + lexer + Decides whether or not to use a lexer stage + + - "auto" (default): Choose for me based on the parser + - "basic": Use a basic lexer + - "contextual": Stronger lexer (only works with parser="lalr") + - "dynamic": Flexible and powerful (only with parser="earley") + - "dynamic_complete": Same as dynamic, but tries *every* variation of tokenizing possible. + ambiguity + Decides how to handle ambiguity in the parse. Only relevant if parser="earley" + + - "resolve": The parser will automatically choose the simplest derivation + (it chooses consistently: greedy for tokens, non-greedy for rules) + - "explicit": The parser will return all derivations wrapped in "_ambig" tree nodes (i.e. a forest). + - "forest": The parser will return the root of the shared packed parse forest. + + **=== Misc. / Domain Specific Options ===** + + postlex + Lexer post-processing (Default: ``None``) Only works with the basic and contextual lexers. + priority + How priorities should be evaluated - "auto", ``None``, "normal", "invert" (Default: "auto") + lexer_callbacks + Dictionary of callbacks for the lexer. May alter tokens during lexing. Use with caution. + use_bytes + Accept an input of type ``bytes`` instead of ``str``. + ordered_sets + Should Earley use ordered-sets to achieve stable output (~10% slower than regular sets. Default: True) + edit_terminals + A callback for editing the terminals before parse. + import_paths + A List of either paths or loader functions to specify from where grammars are imported + source_path + Override the source of from where the grammar was loaded. Useful for relative imports and unconventional grammar loading + **=== End of Options ===** + """ + if __doc__: + __doc__ += OPTIONS_DOC + + + ## + + ## + + ## + + ## + + ## + + ## + + _defaults: Dict[str, Any] = { + 'debug': False, + 'strict': False, + 'keep_all_tokens': False, + 'tree_class': None, + 'cache': False, + 'cache_grammar': False, + 'postlex': None, + 'parser': 'earley', + 'lexer': 'auto', + 'transformer': None, + 'start': 'start', + 'priority': 'auto', + 'ambiguity': 'auto', + 'regex': False, + 'propagate_positions': False, + 'lexer_callbacks': {}, + 'maybe_placeholders': True, + 'edit_terminals': None, + 'g_regex_flags': 0, + 'use_bytes': False, + 'ordered_sets': True, + 'import_paths': [], + 'source_path': None, + '_plugins': {}, + } + + def __init__(self, options_dict: Dict[str, Any]) -> None: + o = dict(options_dict) + + options = {} + for name, default in self._defaults.items(): + if name in o: + value = o.pop(name) + if isinstance(default, bool) and name not in ('cache', 'use_bytes', 'propagate_positions'): + value = bool(value) + else: + value = default + + options[name] = value + + if isinstance(options['start'], str): + options['start'] = [options['start']] + + self.__dict__['options'] = options + + + assert_config(self.parser, ('earley', 'lalr', 'cyk', None)) + + if self.parser == 'earley' and self.transformer: + raise ConfigurationError('Cannot specify an embedded transformer when using the Earley algorithm. ' + 'Please use your transformer on the resulting parse tree, or use a different algorithm (i.e. LALR)') + + if self.cache_grammar and not self.cache: + raise ConfigurationError('cache_grammar cannot be set when cache is disabled') + + if o: + raise ConfigurationError("Unknown options: %s" % o.keys()) + + def __getattr__(self, name: str) -> Any: + try: + return self.__dict__['options'][name] + except KeyError as e: + raise AttributeError(e) + + def __setattr__(self, name: str, value: str) -> None: + assert_config(name, self.options.keys(), "%r isn't a valid option. Expected one of: %s") + self.options[name] = value + + def serialize(self, memo = None) -> Dict[str, Any]: + return self.options + + @classmethod + def deserialize(cls, data: Dict[str, Any], memo: Dict[int, Union[TerminalDef, Rule]]) -> "LarkOptions": + return cls(data) + + +## + +## + +_LOAD_ALLOWED_OPTIONS = {'lexer', 'postlex', 'transformer', 'lexer_callbacks', 'use_bytes', 'debug', 'g_regex_flags', 'regex', 'propagate_positions', 'tree_class', '_plugins'} + +_VALID_PRIORITY_OPTIONS = ('auto', 'normal', 'invert', None) +_VALID_AMBIGUITY_OPTIONS = ('auto', 'resolve', 'explicit', 'forest') + + +_T = TypeVar('_T', bound="Lark") + +class Lark(Serialize): + #-- + + source_path: str + source_grammar: str + grammar: 'Grammar' + options: LarkOptions + lexer: Lexer + parser: 'ParsingFrontend' + terminals: Collection[TerminalDef] + + __serialize_fields__ = ['parser', 'rules', 'options'] + + def __init__(self, grammar: 'Union[Grammar, str, IO[str]]', **options) -> None: + self.options = LarkOptions(options) + re_module: types.ModuleType + + ## + + if self.options.cache_grammar: + self.__serialize_fields__ = self.__serialize_fields__ + ['grammar'] + + ## + + use_regex = self.options.regex + if use_regex: + if _has_regex: + re_module = regex + else: + raise ImportError('`regex` module must be installed if calling `Lark(regex=True)`.') + else: + re_module = re + + ## + + if self.options.source_path is None: + try: + self.source_path = grammar.name ## + + except AttributeError: + self.source_path = '' + else: + self.source_path = self.options.source_path + + ## + + try: + read = grammar.read ## + + except AttributeError: + pass + else: + grammar = read() + + cache_fn = None + cache_sha256 = None + if isinstance(grammar, str): + self.source_grammar = grammar + if self.options.use_bytes: + if not grammar.isascii(): + raise ConfigurationError("Grammar must be ascii only, when use_bytes=True") + + if self.options.cache: + if self.options.parser != 'lalr': + raise ConfigurationError("cache only works with parser='lalr' for now") + + unhashable = ('transformer', 'postlex', 'lexer_callbacks', 'edit_terminals', '_plugins') + options_str = ''.join(k+str(v) for k, v in options.items() if k not in unhashable) + from . import __version__ + s = grammar + options_str + __version__ + str(sys.version_info[:2]) + cache_sha256 = sha256_digest(s) + + if isinstance(self.options.cache, str): + cache_fn = self.options.cache + else: + if self.options.cache is not True: + raise ConfigurationError("cache argument must be bool or str") + + try: + username = getpass.getuser() + except Exception: + ## + + ## + + ## + + username = "unknown" + + + cache_fn = tempfile.gettempdir() + "/.lark_%s_%s_%s_%s_%s.tmp" % ( + "cache_grammar" if self.options.cache_grammar else "cache", username, cache_sha256, *sys.version_info[:2]) + + old_options = self.options + try: + with FS.open(cache_fn, 'rb') as f: + logger.debug('Loading grammar from cache: %s', cache_fn) + ## + + for name in (set(options) - _LOAD_ALLOWED_OPTIONS): + del options[name] + file_sha256 = f.readline().rstrip(b'\n') + cached_used_files = pickle.load(f) + if file_sha256 == cache_sha256.encode('utf8') and verify_used_files(cached_used_files): + cached_parser_data = pickle.load(f) + self._load(cached_parser_data, **options) + return + except FileNotFoundError: + ## + + pass + except Exception: ## + + logger.exception("Failed to load Lark from cache: %r. We will try to carry on.", cache_fn) + + ## + + ## + + self.options = old_options + + + ## + + self.grammar, used_files = load_grammar(grammar, self.source_path, self.options.import_paths, self.options.keep_all_tokens) + else: + assert isinstance(grammar, Grammar) + self.grammar = grammar + + + if self.options.lexer == 'auto': + if self.options.parser == 'lalr': + self.options.lexer = 'contextual' + elif self.options.parser == 'earley': + if self.options.postlex is not None: + logger.info("postlex can't be used with the dynamic lexer, so we use 'basic' instead. " + "Consider using lalr with contextual instead of earley") + self.options.lexer = 'basic' + else: + self.options.lexer = 'dynamic' + elif self.options.parser == 'cyk': + self.options.lexer = 'basic' + else: + assert False, self.options.parser + lexer = self.options.lexer + if isinstance(lexer, type): + assert issubclass(lexer, Lexer) ## + + else: + assert_config(lexer, ('basic', 'contextual', 'dynamic', 'dynamic_complete')) + if self.options.postlex is not None and 'dynamic' in lexer: + raise ConfigurationError("Can't use postlex with a dynamic lexer. Use basic or contextual instead") + + if self.options.ambiguity == 'auto': + if self.options.parser == 'earley': + self.options.ambiguity = 'resolve' + else: + assert_config(self.options.parser, ('earley', 'cyk'), "%r doesn't support disambiguation. Use one of these parsers instead: %s") + + if self.options.priority == 'auto': + self.options.priority = 'normal' + + if self.options.priority not in _VALID_PRIORITY_OPTIONS: + raise ConfigurationError("invalid priority option: %r. Must be one of %r" % (self.options.priority, _VALID_PRIORITY_OPTIONS)) + if self.options.ambiguity not in _VALID_AMBIGUITY_OPTIONS: + raise ConfigurationError("invalid ambiguity option: %r. Must be one of %r" % (self.options.ambiguity, _VALID_AMBIGUITY_OPTIONS)) + + if self.options.parser is None: + terminals_to_keep = '*' ## + + elif self.options.postlex is not None: + terminals_to_keep = set(self.options.postlex.always_accept) + else: + terminals_to_keep = set() + + ## + + self.terminals, self.rules, self.ignore_tokens = self.grammar.compile(self.options.start, terminals_to_keep) + + if self.options.edit_terminals: + for t in self.terminals: + self.options.edit_terminals(t) + + self._terminals_dict = {t.name: t for t in self.terminals} + + ## + + if self.options.priority == 'invert': + for rule in self.rules: + if rule.options.priority is not None: + rule.options.priority = -rule.options.priority + for term in self.terminals: + term.priority = -term.priority + ## + + ## + + ## + + elif self.options.priority is None: + for rule in self.rules: + if rule.options.priority is not None: + rule.options.priority = None + for term in self.terminals: + term.priority = 0 + + ## + + self.lexer_conf = LexerConf( + self.terminals, re_module, self.ignore_tokens, self.options.postlex, + self.options.lexer_callbacks, self.options.g_regex_flags, use_bytes=self.options.use_bytes, strict=self.options.strict + ) + + if self.options.parser: + self.parser = self._build_parser() + elif lexer: + self.lexer = self._build_lexer() + + if cache_fn: + logger.debug('Saving grammar to cache: %s', cache_fn) + try: + with FS.open(cache_fn, 'wb') as f: + assert cache_sha256 is not None + f.write(cache_sha256.encode('utf8') + b'\n') + pickle.dump(used_files, f) + self.save(f, _LOAD_ALLOWED_OPTIONS) + except IOError as e: + logger.exception("Failed to save Lark to cache: %r.", cache_fn, e) + + if __doc__: + __doc__ += "\n\n" + LarkOptions.OPTIONS_DOC + + def _build_lexer(self, dont_ignore: bool=False) -> BasicLexer: + lexer_conf = self.lexer_conf + if dont_ignore: + from copy import copy + lexer_conf = copy(lexer_conf) + lexer_conf.ignore = () + return BasicLexer(lexer_conf) + + def _prepare_callbacks(self) -> None: + self._callbacks = {} + ## + + if self.options.ambiguity != 'forest': + self._parse_tree_builder = ParseTreeBuilder( + self.rules, + self.options.tree_class or Tree, + self.options.propagate_positions, + self.options.parser != 'lalr' and self.options.ambiguity == 'explicit', + self.options.maybe_placeholders + ) + self._callbacks = self._parse_tree_builder.create_callback(self.options.transformer) + self._callbacks.update(_get_lexer_callbacks(self.options.transformer, self.terminals)) + + def _build_parser(self) -> "ParsingFrontend": + self._prepare_callbacks() + _validate_frontend_args(self.options.parser, self.options.lexer) + parser_conf = ParserConf(self.rules, self._callbacks, self.options.start) + return _construct_parsing_frontend( + self.options.parser, + self.options.lexer, + self.lexer_conf, + parser_conf, + options=self.options + ) + + def save(self, f, exclude_options: Collection[str] = ()) -> None: + #-- + if self.options.parser != 'lalr': + raise NotImplementedError("Lark.save() is only implemented for the LALR(1) parser.") + data, m = self.memo_serialize([TerminalDef, Rule]) + if exclude_options: + data["options"] = {n: v for n, v in data["options"].items() if n not in exclude_options} + pickle.dump({'data': data, 'memo': m}, f, protocol=pickle.HIGHEST_PROTOCOL) + + @classmethod + def load(cls: Type[_T], f) -> _T: + #-- + inst = cls.__new__(cls) + return inst._load(f) + + def _deserialize_lexer_conf(self, data: Dict[str, Any], memo: Dict[int, Union[TerminalDef, Rule]], options: LarkOptions) -> LexerConf: + lexer_conf = LexerConf.deserialize(data['lexer_conf'], memo) + lexer_conf.callbacks = options.lexer_callbacks or {} + lexer_conf.re_module = regex if options.regex else re + lexer_conf.use_bytes = options.use_bytes + lexer_conf.g_regex_flags = options.g_regex_flags + lexer_conf.skip_validation = True + lexer_conf.postlex = options.postlex + return lexer_conf + + def _load(self: _T, f: Any, **kwargs) -> _T: + if isinstance(f, dict): + d = f + else: + d = pickle.load(f) + memo_json = d['memo'] + data = d['data'] + + assert memo_json + memo = SerializeMemoizer.deserialize(memo_json, {'Rule': Rule, 'TerminalDef': TerminalDef}, {}) + if 'grammar' in data: + self.grammar = Grammar.deserialize(data['grammar'], memo) + options = dict(data['options']) + if (set(kwargs) - _LOAD_ALLOWED_OPTIONS) & set(LarkOptions._defaults): + raise ConfigurationError("Some options are not allowed when loading a Parser: {}" + .format(set(kwargs) - _LOAD_ALLOWED_OPTIONS)) + options.update(kwargs) + self.options = LarkOptions.deserialize(options, memo) + self.rules = [Rule.deserialize(r, memo) for r in data['rules']] + self.source_path = '' + _validate_frontend_args(self.options.parser, self.options.lexer) + self.lexer_conf = self._deserialize_lexer_conf(data['parser'], memo, self.options) + self.terminals = self.lexer_conf.terminals + self._prepare_callbacks() + self._terminals_dict = {t.name: t for t in self.terminals} + self.parser = _deserialize_parsing_frontend( + data['parser'], + memo, + self.lexer_conf, + self._callbacks, + self.options, ## + + ) + return self + + @classmethod + def _load_from_dict(cls, data, memo, **kwargs): + inst = cls.__new__(cls) + return inst._load({'data': data, 'memo': memo}, **kwargs) + + @classmethod + def open(cls: Type[_T], grammar_filename: str, rel_to: Optional[str]=None, **options) -> _T: + #-- + if rel_to: + basepath = os.path.dirname(rel_to) + grammar_filename = os.path.join(basepath, grammar_filename) + with open(grammar_filename, encoding='utf8') as f: + return cls(f, **options) + + @classmethod + def open_from_package(cls: Type[_T], package: str, grammar_path: str, search_paths: 'Sequence[str]'=[""], **options) -> _T: + #-- + package_loader = FromPackageLoader(package, search_paths) + full_path, text = package_loader(None, grammar_path) + options.setdefault('source_path', full_path) + options.setdefault('import_paths', []) + options['import_paths'].append(package_loader) + return cls(text, **options) + + def __repr__(self): + return 'Lark(open(%r), parser=%r, lexer=%r, ...)' % (self.source_path, self.options.parser, self.options.lexer) + + + def lex(self, text: TextOrSlice, dont_ignore: bool=False) -> Iterator[Token]: + #-- + lexer: Lexer + if not hasattr(self, 'lexer') or dont_ignore: + lexer = self._build_lexer(dont_ignore) + else: + lexer = self.lexer + lexer_thread = LexerThread.from_text(lexer, text) + stream = lexer_thread.lex(None) + if self.options.postlex: + return self.options.postlex.process(stream) + return stream + + def get_terminal(self, name: str) -> TerminalDef: + #-- + return self._terminals_dict[name] + + def parse_interactive(self, text: Optional[LarkInput]=None, start: Optional[str]=None) -> 'InteractiveParser': + #-- + return self.parser.parse_interactive(text, start=start) + + def parse(self, text: LarkInput, start: Optional[str]=None, on_error: 'Optional[Callable[[UnexpectedInput], bool]]'=None) -> 'ParseTree': + #-- + if on_error is not None and self.options.parser != 'lalr': + raise NotImplementedError("The on_error option is only implemented for the LALR(1) parser.") + return self.parser.parse(text, start=start, on_error=on_error) + + + + +class DedentError(LarkError): + pass + +class Indenter(PostLex, ABC): + #-- + paren_level: int + indent_level: List[int] + + def __init__(self) -> None: + self.paren_level = 0 + self.indent_level = [0] + assert self.tab_len > 0 + + def handle_NL(self, token: Token) -> Iterator[Token]: + if self.paren_level > 0: + return + + yield token + + indent_str = token.rsplit('\n', 1)[1] ## + + indent = indent_str.count(' ') + indent_str.count('\t') * self.tab_len + + if indent > self.indent_level[-1]: + self.indent_level.append(indent) + yield Token.new_borrow_pos(self.INDENT_type, indent_str, token) + else: + while indent < self.indent_level[-1]: + self.indent_level.pop() + yield Token.new_borrow_pos(self.DEDENT_type, indent_str, token) + + if indent != self.indent_level[-1]: + raise DedentError('Unexpected dedent to column %s. Expected dedent to %s' % (indent, self.indent_level[-1])) + + def _process(self, stream): + token = None + for token in stream: + if token.type == self.NL_type: + yield from self.handle_NL(token) + else: + yield token + + if token.type in self.OPEN_PAREN_types: + self.paren_level += 1 + elif token.type in self.CLOSE_PAREN_types: + self.paren_level -= 1 + assert self.paren_level >= 0 + + while len(self.indent_level) > 1: + self.indent_level.pop() + yield Token.new_borrow_pos(self.DEDENT_type, '', token) if token else Token(self.DEDENT_type, '', 0, 0, 0, 0, 0, 0) + + assert self.indent_level == [0], self.indent_level + + def process(self, stream): + self.paren_level = 0 + self.indent_level = [0] + return self._process(stream) + + ## + + @property + def always_accept(self): + return (self.NL_type,) + + @property + @abstractmethod + def NL_type(self) -> str: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def OPEN_PAREN_types(self) -> List[str]: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def CLOSE_PAREN_types(self) -> List[str]: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def INDENT_type(self) -> str: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def DEDENT_type(self) -> str: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def tab_len(self) -> int: + #-- + raise NotImplementedError() + + +class PythonIndenter(Indenter): + #-- + + NL_type = '_NEWLINE' + OPEN_PAREN_types = ['LPAR', 'LSQB', 'LBRACE'] + CLOSE_PAREN_types = ['RPAR', 'RSQB', 'RBRACE'] + INDENT_type = '_INDENT' + DEDENT_type = '_DEDENT' + tab_len = 8 + + +import pickle, zlib, base64 +DATA = ( +{'parser': {'lexer_conf': {'terminals': [], 'ignore': [], 'g_regex_flags': 0, 'use_bytes': False, 'lexer_type': 'contextual', '__type__': 'LexerConf'}, 'parser_conf': {'rules': [{'@': 0}, {'@': 1}, {'@': 2}, {'@': 3}, {'@': 4}, {'@': 5}, {'@': 6}, {'@': 7}, {'@': 8}, {'@': 9}, {'@': 10}, {'@': 11}, {'@': 12}, {'@': 13}, {'@': 14}, {'@': 15}, {'@': 16}, {'@': 17}, {'@': 18}, {'@': 19}, {'@': 20}, {'@': 21}, {'@': 22}, {'@': 23}, {'@': 24}, {'@': 25}, {'@': 26}, {'@': 27}, {'@': 28}, {'@': 29}, {'@': 30}, {'@': 31}, {'@': 32}, {'@': 33}, {'@': 34}, {'@': 35}, {'@': 36}, {'@': 37}, {'@': 38}, {'@': 39}, {'@': 40}, {'@': 41}, {'@': 42}, {'@': 43}, {'@': 44}, {'@': 45}, {'@': 46}, {'@': 47}, {'@': 48}, {'@': 49}, {'@': 50}, {'@': 51}, {'@': 52}, {'@': 53}, {'@': 54}, {'@': 55}, {'@': 56}, {'@': 57}, {'@': 58}, {'@': 59}, {'@': 60}, {'@': 61}, {'@': 62}, {'@': 63}, {'@': 64}, {'@': 65}, {'@': 66}, {'@': 67}, {'@': 68}, {'@': 69}, {'@': 70}, {'@': 71}, {'@': 72}, {'@': 73}, {'@': 74}, {'@': 75}, {'@': 76}, {'@': 77}, {'@': 78}, {'@': 79}, {'@': 80}, {'@': 81}, {'@': 82}, {'@': 83}, {'@': 84}, {'@': 85}, {'@': 86}, {'@': 87}, {'@': 88}, {'@': 89}, {'@': 90}, {'@': 91}, {'@': 92}, {'@': 93}, {'@': 94}, {'@': 95}, {'@': 96}, {'@': 97}, {'@': 98}, {'@': 99}, {'@': 100}, {'@': 101}, {'@': 102}, {'@': 103}, {'@': 104}, {'@': 105}, {'@': 106}, {'@': 107}, {'@': 108}, {'@': 109}, {'@': 110}, {'@': 111}, {'@': 112}, {'@': 113}, {'@': 114}, {'@': 115}, {'@': 116}, {'@': 117}, {'@': 118}, {'@': 119}, {'@': 120}, {'@': 121}, {'@': 122}, {'@': 123}, {'@': 124}, {'@': 125}, {'@': 126}, {'@': 127}, {'@': 128}, {'@': 129}, {'@': 130}, {'@': 131}, {'@': 132}, {'@': 133}, {'@': 134}, {'@': 135}, {'@': 136}, {'@': 137}, {'@': 138}, {'@': 139}, {'@': 140}, {'@': 141}, {'@': 142}, {'@': 143}, {'@': 144}, {'@': 145}, {'@': 146}, {'@': 147}, {'@': 148}, {'@': 149}, {'@': 150}, {'@': 151}, {'@': 152}, {'@': 153}, {'@': 154}, {'@': 155}, {'@': 156}, {'@': 157}, {'@': 158}, {'@': 159}, {'@': 160}, {'@': 161}, {'@': 162}, {'@': 163}, {'@': 164}, {'@': 165}, {'@': 166}, {'@': 167}, {'@': 168}, {'@': 169}, {'@': 170}, {'@': 171}, {'@': 172}, {'@': 173}, {'@': 174}, {'@': 175}, {'@': 176}, {'@': 177}, {'@': 178}, {'@': 179}, {'@': 180}, {'@': 181}, {'@': 182}, {'@': 183}, {'@': 184}, {'@': 185}, {'@': 186}, {'@': 187}, {'@': 188}, {'@': 189}, {'@': 190}, {'@': 191}, {'@': 192}, {'@': 193}, {'@': 194}, {'@': 195}, {'@': 196}, {'@': 197}, {'@': 198}, {'@': 199}, {'@': 200}, {'@': 201}, {'@': 202}, {'@': 203}, {'@': 204}, {'@': 205}, {'@': 206}, {'@': 207}, {'@': 208}, {'@': 209}, {'@': 210}, {'@': 211}, {'@': 212}, {'@': 213}, {'@': 214}, {'@': 215}, {'@': 216}, {'@': 217}, {'@': 218}, {'@': 219}, {'@': 220}, {'@': 221}, {'@': 222}, {'@': 223}, {'@': 224}, {'@': 225}, {'@': 226}, {'@': 227}, {'@': 228}, {'@': 229}, {'@': 230}, {'@': 231}, {'@': 232}, {'@': 233}, {'@': 234}, {'@': 235}, {'@': 236}, {'@': 237}, {'@': 238}, {'@': 239}, {'@': 240}, {'@': 241}, {'@': 242}, {'@': 243}, {'@': 244}, {'@': 245}, {'@': 246}, {'@': 247}, {'@': 248}, {'@': 249}, {'@': 250}, {'@': 251}, {'@': 252}, {'@': 253}, {'@': 254}, {'@': 255}, {'@': 256}, {'@': 257}, {'@': 258}, {'@': 259}, {'@': 260}, {'@': 261}, {'@': 262}, {'@': 263}, {'@': 264}, {'@': 265}, {'@': 266}, {'@': 267}, {'@': 268}, {'@': 269}, {'@': 270}, {'@': 271}, {'@': 272}, {'@': 273}, {'@': 274}, {'@': 275}, {'@': 276}, {'@': 277}, {'@': 278}, {'@': 279}, {'@': 280}, {'@': 281}, {'@': 282}, {'@': 283}, {'@': 284}, {'@': 285}, {'@': 286}, {'@': 287}, {'@': 288}, {'@': 289}, {'@': 290}, {'@': 291}, {'@': 292}, {'@': 293}, {'@': 294}, {'@': 295}, {'@': 296}, {'@': 297}, {'@': 298}, {'@': 299}, {'@': 300}, {'@': 301}, {'@': 302}, {'@': 303}, {'@': 304}, {'@': 305}, {'@': 306}, {'@': 307}, {'@': 308}, {'@': 309}, {'@': 310}, {'@': 311}, {'@': 312}, {'@': 313}, {'@': 314}, {'@': 315}, {'@': 316}, {'@': 317}, {'@': 318}, {'@': 319}, {'@': 320}, {'@': 321}, {'@': 322}, {'@': 323}, {'@': 324}, {'@': 325}, {'@': 326}, {'@': 327}, {'@': 328}, {'@': 329}, {'@': 330}, {'@': 331}, {'@': 332}, {'@': 333}, {'@': 334}, {'@': 335}, {'@': 336}, {'@': 337}, {'@': 338}], 'start': ['start'], 'parser_type': 'lalr', '__type__': 'ParserConf'}, 'parser': {'tokens': {0: 'A', 1: 'PLUS', 2: 'LPP', 3: 'ADDR', 4: 'ID', 5: 'expr_atom', 6: 'LP', 7: 'MINUS', 8: 'INTEGER', 9: 'pexpr', 10: 'expr_unary', 11: 'RP', 12: 'DIV', 13: 'MOD', 14: 'BOR', 15: 'BAND', 16: 'MUL', 17: 'RSHIFT', 18: 'BXOR', 19: 'POW', 20: 'LSHIFT', 21: 'reg16i', 22: 'HL', 23: 'IY', 24: 'IX', 25: 'NEWLINE', 26: 'CO', 27: 'COMMA', 28: 'expr_mul', 29: 'expr_add_operand', 30: 'expr_add', 31: 'expr_bitwise', 32: 'expr_bitwise_operand', 33: 'expr_mul_operand', 34: 'expr_unary_operand', 35: 'expr_pow', 36: 'expr', 37: 'RB', 38: 'LB', 39: 'IYH', 40: 'IXL', 41: 'reg8', 42: 'reg8_i', 43: 'reg8_hl', 44: 'IYL', 45: 'D', 46: 'L', 47: 'C', 48: 'H', 49: 'B', 50: 'E', 51: 'reg_bcde', 52: 'reg8i', 53: 'IXH', 54: '$END', 55: 'Z', 56: 'NC', 57: 'P', 58: 'PE', 59: 'M', 60: 'jp_flags', 61: 'NZ', 62: 'jr_flags', 63: 'PO', 64: 'STRING', 65: 'DE', 66: 'BC', 67: 'RPP', 68: 'expr_pow_operand', 69: 'reg16', 70: 'inc_reg', 71: 'SP', 72: 'NAMESPACE', 73: 'AF', 74: 'EQU', 75: 'number_list', 76: 'expr_list', 77: 'OUT', 78: 'asms', 79: 'LD', 80: 'JP', 81: 'SLA', 82: 'line', 83: 'IN', 84: 'bitop', 85: 'RST', 86: 'ADD', 87: 'RRCA', 88: 'NOP', 89: 'ADC', 90: 'SBC', 91: 'rotation', 92: 'RET', 93: 'PUSH', 94: 'RETN', 95: 'CPIR', 96: 'SUB', 97: 'LOCAL', 98: 'DAA', 99: 'SCF', 100: 'CP', 101: 'CPL', 102: 'SLL', 103: 'CALL', 104: 'bitwiseop', 105: 'OR', 106: 'endline', 107: 'CPDR', 108: 'DJNZ', 109: 'END', 110: 'CCF', 111: 'RRD', 112: 'IND', 113: 'INCBIN', 114: 'INDR', 115: 'CPI', 116: 'ALIGN', 117: 'ENDP', 118: 'EX', 119: 'OUTD', 120: 'POP', 121: 'RRC', 122: 'ORG', 123: 'LDD', 124: 'EXX', 125: 'JR', 126: 'HALT', 127: 'DEC', 128: 'preproc_line', 129: 'RLCA', 130: 'RETI', 131: 'INIR', 132: 'SRL', 133: 'RLD', 134: '_INIT', 135: 'INC', 136: 'LDI', 137: 'XOR', 138: 'NEG', 139: 'SRA', 140: 'OTDR', 141: 'OTIR', 142: 'INI', 143: 'DEFS', 144: 'DEFB', 145: 'PROC', 146: 'RL', 147: 'EI', 148: 'LDIR', 149: 'asm', 150: 'OUTI', 151: 'RR', 152: 'DI', 153: 'RLA', 154: 'BIT', 155: 'RLC', 156: 'DEFW', 157: 'CPD', 158: 'AND', 159: 'RRA', 160: 'RES', 161: 'IM', 162: 'SET', 163: 'LDDR', 164: 'id_list', 165: 'APO', 166: 'mem_indir', 167: 'R', 168: 'I', 169: 'program', 170: 'start'}, 'states': {0: {0: (0, 103)}, 1: {1: (0, 360), 2: (0, 252), 3: (0, 473), 4: (0, 486), 5: (0, 472), 6: (0, 355), 7: (0, 374), 8: (0, 332), 9: (0, 122), 10: (0, 362)}, 2: {11: (0, 92), 12: (1, {'@': 331}), 13: (1, {'@': 331}), 14: (1, {'@': 331}), 15: (1, {'@': 331}), 16: (1, {'@': 331}), 1: (1, {'@': 331}), 17: (1, {'@': 331}), 18: (1, {'@': 331}), 19: (1, {'@': 331}), 20: (1, {'@': 331}), 7: (1, {'@': 331})}, 3: {21: (0, 34), 0: (0, 320), 22: (0, 533), 23: (0, 416), 24: (0, 469)}, 4: {25: (0, 27), 7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309})}, 5: {25: (1, {'@': 86}), 26: (1, {'@': 86})}, 6: {11: (0, 94), 12: (1, {'@': 330}), 13: (1, {'@': 330}), 14: (1, {'@': 330}), 15: (1, {'@': 330}), 16: (1, {'@': 330}), 1: (1, {'@': 330}), 17: (1, {'@': 330}), 18: (1, {'@': 330}), 19: (1, {'@': 330}), 20: (1, {'@': 330}), 7: (1, {'@': 330})}, 7: {25: (1, {'@': 241}), 26: (1, {'@': 241}), 27: (1, {'@': 241})}, 8: {25: (1, {'@': 147}), 26: (1, {'@': 147}), 7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309})}, 9: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252), 36: (0, 445)}, 10: {11: (0, 100), 12: (1, {'@': 330}), 13: (1, {'@': 330}), 14: (1, {'@': 330}), 16: (1, {'@': 330}), 15: (1, {'@': 330}), 1: (1, {'@': 330}), 17: (1, {'@': 330}), 18: (1, {'@': 330}), 19: (1, {'@': 330}), 20: (1, {'@': 330}), 7: (1, {'@': 330})}, 11: {25: (1, {'@': 138}), 26: (1, {'@': 138})}, 12: {11: (0, 96), 12: (1, {'@': 331}), 13: (1, {'@': 331}), 14: (1, {'@': 331}), 16: (1, {'@': 331}), 15: (1, {'@': 331}), 1: (1, {'@': 331}), 17: (1, {'@': 331}), 18: (1, {'@': 331}), 19: (1, {'@': 331}), 20: (1, {'@': 331}), 7: (1, {'@': 331})}, 13: {25: (1, {'@': 150}), 26: (1, {'@': 150})}, 14: {37: (0, 77)}, 15: {25: (1, {'@': 24}), 26: (1, {'@': 24})}, 16: {25: (1, {'@': 25}), 26: (1, {'@': 25})}, 17: {37: (0, 81)}, 18: {25: (1, {'@': 84}), 26: (1, {'@': 84})}, 19: {25: (1, {'@': 54}), 26: (1, {'@': 54})}, 20: {11: (0, 85)}, 21: {25: (1, {'@': 220}), 26: (1, {'@': 220})}, 22: {25: (1, {'@': 55}), 26: (1, {'@': 55})}, 23: {0: (0, 229), 38: (0, 524), 39: (0, 430), 40: (0, 498), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 163), 29: (0, 490), 30: (0, 509), 36: (0, 247), 9: (0, 248), 4: (0, 486), 31: (0, 488), 32: (0, 412), 41: (0, 249), 42: (0, 251), 3: (0, 473), 43: (0, 253), 33: (0, 395), 44: (0, 361), 45: (0, 414), 7: (0, 374), 46: (0, 532), 47: (0, 520), 48: (0, 408), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 49: (0, 491), 2: (0, 252), 50: (0, 481), 51: (0, 369), 52: (0, 254), 53: (0, 462)}, 24: {1: (0, 360), 2: (0, 252), 3: (0, 473), 9: (0, 321), 4: (0, 486), 5: (0, 472), 6: (0, 355), 7: (0, 374), 8: (0, 332), 10: (0, 362)}, 25: {11: (0, 88)}, 26: {25: (1, {'@': 200}), 26: (1, {'@': 200})}, 27: {54: (1, {'@': 7}), 25: (1, {'@': 7})}, 28: {0: (0, 106)}, 29: {37: (0, 109)}, 30: {11: (0, 112)}, 31: {22: (0, 303)}, 32: {25: (1, {'@': 204}), 26: (1, {'@': 204})}, 33: {1: (0, 360), 2: (0, 252), 3: (0, 473), 9: (0, 338), 5: (0, 472), 7: (0, 374), 8: (0, 332), 6: (0, 355), 10: (0, 385), 4: (0, 486)}, 34: {27: (0, 359)}, 35: {21: (0, 142), 23: (0, 416), 22: (0, 128), 24: (0, 469)}, 36: {25: (1, {'@': 30}), 26: (1, {'@': 30})}, 37: {6: (1, {'@': 257}), 8: (1, {'@': 257}), 2: (1, {'@': 257}), 49: (1, {'@': 257}), 50: (1, {'@': 257}), 45: (1, {'@': 257}), 0: (1, {'@': 257}), 1: (1, {'@': 257}), 47: (1, {'@': 257}), 4: (1, {'@': 257}), 3: (1, {'@': 257}), 48: (1, {'@': 257}), 39: (1, {'@': 257}), 53: (1, {'@': 257}), 38: (1, {'@': 257}), 46: (1, {'@': 257}), 44: (1, {'@': 257}), 40: (1, {'@': 257}), 7: (1, {'@': 257})}, 38: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 309), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 36: (0, 310), 35: (0, 200), 2: (0, 252)}, 39: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 36: (0, 144), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 40: {36: (0, 312), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 41: {25: (1, {'@': 143}), 26: (1, {'@': 143})}, 42: {21: (0, 131), 23: (0, 416), 22: (0, 123), 24: (0, 469)}, 43: {25: (0, 276), 7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309})}, 44: {37: (0, 340)}, 45: {11: (0, 116)}, 46: {25: (0, 277)}, 47: {38: (0, 524), 36: (0, 260), 39: (0, 430), 40: (0, 498), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 163), 29: (0, 490), 30: (0, 509), 9: (0, 286), 0: (0, 290), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 44: (0, 361), 33: (0, 395), 45: (0, 414), 7: (0, 374), 46: (0, 532), 41: (0, 293), 47: (0, 520), 42: (0, 294), 48: (0, 408), 10: (0, 382), 34: (0, 456), 8: (0, 332), 52: (0, 298), 35: (0, 200), 49: (0, 491), 2: (0, 252), 50: (0, 481), 51: (0, 369), 53: (0, 462), 43: (0, 302)}, 48: {37: (0, 119)}, 49: {37: (0, 307)}, 50: {25: (1, {'@': 58}), 26: (1, {'@': 58})}, 51: {25: (1, {'@': 185}), 26: (1, {'@': 185})}, 52: {28: (0, 499), 55: (0, 424), 56: (0, 398), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 57: (0, 434), 47: (0, 390), 9: (0, 419), 58: (0, 528), 59: (0, 516), 4: (0, 486), 31: (0, 488), 60: (0, 425), 32: (0, 412), 3: (0, 473), 36: (0, 417), 33: (0, 395), 61: (0, 388), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 62: (0, 335), 35: (0, 200), 2: (0, 252), 63: (0, 190)}, 53: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 9: (0, 331), 29: (0, 490), 30: (0, 509), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252), 64: (0, 334), 36: (0, 336)}, 54: {1: (0, 360), 2: (0, 252), 3: (0, 473), 9: (0, 350), 5: (0, 472), 7: (0, 374), 8: (0, 332), 6: (0, 355), 10: (0, 385), 4: (0, 486)}, 55: {25: (1, {'@': 187}), 26: (1, {'@': 187})}, 56: {25: (1, {'@': 144}), 26: (1, {'@': 144})}, 57: {25: (1, {'@': 95}), 26: (1, {'@': 95})}, 58: {38: (0, 524), 41: (0, 431), 42: (0, 380), 0: (0, 169), 6: (0, 132), 45: (0, 414), 46: (0, 532), 47: (0, 520), 43: (0, 152), 48: (0, 408), 49: (0, 491), 50: (0, 481), 51: (0, 369)}, 59: {23: (0, 377), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 24: (0, 387), 9: (0, 454), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 65: (0, 20), 66: (0, 25), 10: (0, 382), 34: (0, 456), 8: (0, 332), 22: (0, 505), 35: (0, 200), 2: (0, 252), 36: (0, 392)}, 60: {25: (1, {'@': 251}), 26: (1, {'@': 251}), 27: (1, {'@': 251})}, 61: {25: (1, {'@': 190}), 26: (1, {'@': 190})}, 62: {25: (1, {'@': 186}), 26: (1, {'@': 186})}, 63: {25: (1, {'@': 252}), 26: (1, {'@': 252}), 27: (1, {'@': 252})}, 64: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 322), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 36: (0, 324), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 65: {25: (1, {'@': 148}), 26: (1, {'@': 148})}, 66: {27: (0, 316)}, 67: {25: (1, {'@': 249}), 26: (1, {'@': 249}), 27: (1, {'@': 249})}, 68: {27: (0, 389)}, 69: {37: (0, 280)}, 70: {54: (1, {'@': 6}), 25: (1, {'@': 6})}, 71: {25: (1, {'@': 250}), 26: (1, {'@': 250}), 27: (1, {'@': 250})}, 72: {13: (1, {'@': 334}), 14: (1, {'@': 334}), 37: (1, {'@': 334}), 19: (1, {'@': 334}), 12: (1, {'@': 334}), 15: (1, {'@': 334}), 16: (1, {'@': 334}), 1: (1, {'@': 334}), 17: (1, {'@': 334}), 18: (1, {'@': 334}), 20: (1, {'@': 334}), 7: (1, {'@': 334}), 25: (1, {'@': 334}), 26: (1, {'@': 334}), 27: (1, {'@': 334}), 11: (1, {'@': 334}), 67: (1, {'@': 334})}, 73: {25: (1, {'@': 45}), 26: (1, {'@': 45})}, 74: {25: (1, {'@': 47}), 26: (1, {'@': 47})}, 75: {22: (0, 233), 0: (0, 381)}, 76: {25: (1, {'@': 145}), 26: (1, {'@': 145}), 7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309})}, 77: {25: (1, {'@': 43}), 26: (1, {'@': 43})}, 78: {9: (0, 95), 1: (0, 360), 10: (0, 382), 2: (0, 252), 3: (0, 473), 6: (0, 355), 34: (0, 456), 7: (0, 374), 5: (0, 472), 8: (0, 332), 68: (0, 99), 4: (0, 486), 35: (0, 175)}, 79: {25: (1, {'@': 83}), 26: (1, {'@': 83})}, 80: {25: (1, {'@': 35}), 26: (1, {'@': 35})}, 81: {25: (1, {'@': 41}), 26: (1, {'@': 41})}, 82: {25: (1, {'@': 31}), 26: (1, {'@': 31})}, 83: {52: (0, 125), 40: (0, 498), 39: (0, 430), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 36: (0, 133), 44: (0, 361), 33: (0, 395), 45: (0, 414), 7: (0, 374), 47: (0, 520), 10: (0, 382), 34: (0, 456), 8: (0, 332), 0: (0, 135), 35: (0, 200), 49: (0, 491), 2: (0, 252), 50: (0, 481), 53: (0, 462), 51: (0, 137)}, 84: {25: (1, {'@': 26}), 26: (1, {'@': 26})}, 85: {25: (1, {'@': 42}), 26: (1, {'@': 42})}, 86: {25: (1, {'@': 29}), 26: (1, {'@': 29})}, 87: {25: (1, {'@': 33}), 26: (1, {'@': 33})}, 88: {25: (1, {'@': 40}), 26: (1, {'@': 40})}, 89: {25: (1, {'@': 57}), 26: (1, {'@': 57})}, 90: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 328), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 36: (0, 108), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 91: {25: (1, {'@': 226}), 26: (1, {'@': 226})}, 92: {25: (1, {'@': 245}), 26: (1, {'@': 245}), 27: (1, {'@': 245})}, 93: {25: (1, {'@': 136}), 26: (1, {'@': 136})}, 94: {25: (1, {'@': 246}), 26: (1, {'@': 246}), 27: (1, {'@': 246})}, 95: {19: (1, {'@': 309}), 13: (1, {'@': 311}), 14: (1, {'@': 311}), 11: (1, {'@': 311}), 37: (1, {'@': 311}), 27: (1, {'@': 311}), 12: (1, {'@': 311}), 67: (1, {'@': 311}), 25: (1, {'@': 311}), 15: (1, {'@': 311}), 16: (1, {'@': 311}), 1: (1, {'@': 311}), 17: (1, {'@': 311}), 18: (1, {'@': 311}), 20: (1, {'@': 311}), 7: (1, {'@': 311}), 26: (1, {'@': 311})}, 96: {25: (1, {'@': 243}), 26: (1, {'@': 243}), 27: (1, {'@': 243})}, 97: {48: (1, {'@': 262}), 50: (1, {'@': 262}), 38: (1, {'@': 262}), 45: (1, {'@': 262}), 46: (1, {'@': 262}), 0: (1, {'@': 262}), 6: (1, {'@': 262}), 47: (1, {'@': 262}), 49: (1, {'@': 262})}, 98: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 327), 4: (0, 486), 36: (0, 329), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 99: {12: (1, {'@': 322}), 13: (1, {'@': 322}), 14: (1, {'@': 322}), 15: (1, {'@': 322}), 16: (1, {'@': 322}), 1: (1, {'@': 322}), 17: (1, {'@': 322}), 18: (1, {'@': 322}), 37: (1, {'@': 322}), 20: (1, {'@': 322}), 7: (1, {'@': 322}), 25: (1, {'@': 322}), 26: (1, {'@': 322}), 27: (1, {'@': 322}), 11: (1, {'@': 322}), 67: (1, {'@': 322})}, 100: {25: (1, {'@': 244}), 26: (1, {'@': 244}), 27: (1, {'@': 244})}, 101: {25: (1, {'@': 17}), 26: (1, {'@': 17})}, 102: {1: (0, 360), 2: (0, 252), 3: (0, 473), 9: (0, 6), 5: (0, 472), 7: (0, 374), 8: (0, 332), 6: (0, 355), 10: (0, 385), 4: (0, 486)}, 103: {25: (1, {'@': 46}), 26: (1, {'@': 46})}, 104: {6: (1, {'@': 254}), 8: (1, {'@': 254}), 2: (1, {'@': 254}), 49: (1, {'@': 254}), 50: (1, {'@': 254}), 45: (1, {'@': 254}), 0: (1, {'@': 254}), 1: (1, {'@': 254}), 47: (1, {'@': 254}), 4: (1, {'@': 254}), 3: (1, {'@': 254}), 48: (1, {'@': 254}), 39: (1, {'@': 254}), 53: (1, {'@': 254}), 38: (1, {'@': 254}), 46: (1, {'@': 254}), 44: (1, {'@': 254}), 40: (1, {'@': 254}), 7: (1, {'@': 254})}, 105: {25: (1, {'@': 162}), 26: (1, {'@': 162})}, 106: {25: (1, {'@': 44}), 26: (1, {'@': 44})}, 107: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252), 36: (0, 400)}, 108: {27: (0, 269)}, 109: {25: (1, {'@': 183}), 26: (1, {'@': 183})}, 110: {12: (1, {'@': 337}), 13: (1, {'@': 337}), 14: (1, {'@': 337}), 15: (1, {'@': 337}), 16: (1, {'@': 337}), 1: (1, {'@': 337}), 17: (1, {'@': 337}), 18: (1, {'@': 337}), 37: (1, {'@': 337}), 19: (1, {'@': 337}), 20: (1, {'@': 337}), 7: (1, {'@': 337}), 25: (1, {'@': 337}), 26: (1, {'@': 337}), 27: (1, {'@': 337}), 11: (1, {'@': 337}), 67: (1, {'@': 337})}, 111: {25: (1, {'@': 224}), 26: (1, {'@': 224})}, 112: {25: (1, {'@': 182}), 26: (1, {'@': 182})}, 113: {25: (1, {'@': 208}), 26: (1, {'@': 208})}, 114: {25: (1, {'@': 89}), 26: (1, {'@': 89})}, 115: {25: (1, {'@': 114}), 26: (1, {'@': 114})}, 116: {25: (1, {'@': 180}), 26: (1, {'@': 180})}, 117: {25: (1, {'@': 221}), 26: (1, {'@': 221})}, 118: {11: (0, 352)}, 119: {25: (1, {'@': 181}), 26: (1, {'@': 181})}, 120: {1: (0, 360), 2: (0, 252), 3: (0, 473), 9: (0, 10), 5: (0, 472), 7: (0, 374), 8: (0, 332), 6: (0, 355), 10: (0, 385), 4: (0, 486)}, 121: {25: (1, {'@': 207}), 26: (1, {'@': 207})}, 122: {37: (0, 67), 12: (1, {'@': 331}), 13: (1, {'@': 331}), 14: (1, {'@': 331}), 15: (1, {'@': 331}), 16: (1, {'@': 331}), 1: (1, {'@': 331}), 17: (1, {'@': 331}), 18: (1, {'@': 331}), 19: (1, {'@': 331}), 20: (1, {'@': 331}), 7: (1, {'@': 331})}, 123: {25: (1, {'@': 74}), 26: (1, {'@': 74})}, 124: {25: (1, {'@': 214}), 26: (1, {'@': 214})}, 125: {25: (1, {'@': 39}), 26: (1, {'@': 39})}, 126: {48: (1, {'@': 264}), 50: (1, {'@': 264}), 38: (1, {'@': 264}), 45: (1, {'@': 264}), 46: (1, {'@': 264}), 0: (1, {'@': 264}), 6: (1, {'@': 264}), 47: (1, {'@': 264}), 49: (1, {'@': 264})}, 127: {25: (1, {'@': 158}), 26: (1, {'@': 158})}, 128: {25: (1, {'@': 73}), 26: (1, {'@': 73})}, 129: {25: (1, {'@': 216}), 26: (1, {'@': 216})}, 130: {11: (0, 7)}, 131: {25: (1, {'@': 72}), 26: (1, {'@': 72})}, 132: {23: (0, 377), 24: (0, 387), 22: (0, 505)}, 133: {25: (1, {'@': 85}), 26: (1, {'@': 85})}, 134: {25: (1, {'@': 48}), 26: (1, {'@': 48})}, 135: {25: (1, {'@': 36}), 26: (1, {'@': 36})}, 136: {25: (1, {'@': 192}), 26: (1, {'@': 192})}, 137: {25: (1, {'@': 38}), 26: (1, {'@': 38})}, 138: {48: (1, {'@': 261}), 50: (1, {'@': 261}), 38: (1, {'@': 261}), 45: (1, {'@': 261}), 46: (1, {'@': 261}), 0: (1, {'@': 261}), 6: (1, {'@': 261}), 47: (1, {'@': 261}), 49: (1, {'@': 261})}, 139: {1: (0, 437), 7: (0, 451), 37: (1, {'@': 316}), 20: (1, {'@': 316}), 14: (1, {'@': 316}), 15: (1, {'@': 316}), 17: (1, {'@': 316}), 18: (1, {'@': 316}), 25: (1, {'@': 316}), 26: (1, {'@': 316}), 27: (1, {'@': 316}), 11: (1, {'@': 316}), 67: (1, {'@': 316})}, 140: {25: (1, {'@': 201}), 26: (1, {'@': 201})}, 141: {25: (1, {'@': 27}), 26: (1, {'@': 27})}, 142: {25: (1, {'@': 71}), 26: (1, {'@': 71})}, 143: {25: (1, {'@': 23}), 26: (1, {'@': 23})}, 144: {25: (1, {'@': 70}), 26: (1, {'@': 70})}, 145: {38: (0, 524), 39: (0, 430), 40: (0, 498), 52: (0, 448), 43: (0, 463), 23: (0, 416), 6: (0, 132), 69: (0, 465), 44: (0, 361), 45: (0, 414), 42: (0, 483), 46: (0, 532), 47: (0, 520), 70: (0, 534), 48: (0, 408), 66: (0, 447), 24: (0, 469), 21: (0, 501), 0: (0, 530), 22: (0, 399), 41: (0, 502), 49: (0, 491), 50: (0, 481), 51: (0, 369), 53: (0, 462), 65: (0, 459), 71: (0, 474)}, 146: {25: (1, {'@': 56}), 26: (1, {'@': 56})}, 147: {25: (1, {'@': 79}), 26: (1, {'@': 79})}, 148: {1: (1, {'@': 258}), 6: (1, {'@': 258}), 4: (1, {'@': 258}), 8: (1, {'@': 258}), 2: (1, {'@': 258}), 7: (1, {'@': 258}), 3: (1, {'@': 258})}, 149: {22: (0, 399), 23: (0, 416), 69: (0, 41), 65: (0, 459), 66: (0, 447), 71: (0, 56), 0: (0, 450), 24: (0, 469), 21: (0, 501)}, 150: {25: (1, {'@': 37}), 26: (1, {'@': 37})}, 151: {25: (1, {'@': 21}), 26: (1, {'@': 21})}, 152: {25: (1, {'@': 152}), 26: (1, {'@': 152})}, 153: {25: (1, {'@': 141}), 26: (1, {'@': 141})}, 154: {16: (0, 78), 12: (0, 446), 13: (0, 386), 14: (1, {'@': 319}), 15: (1, {'@': 319}), 1: (1, {'@': 319}), 17: (1, {'@': 319}), 18: (1, {'@': 319}), 37: (1, {'@': 319}), 20: (1, {'@': 319}), 7: (1, {'@': 319}), 25: (1, {'@': 319}), 26: (1, {'@': 319}), 27: (1, {'@': 319}), 11: (1, {'@': 319}), 67: (1, {'@': 319})}, 155: {13: (1, {'@': 307}), 14: (1, {'@': 307}), 11: (1, {'@': 307}), 37: (1, {'@': 307}), 27: (1, {'@': 307}), 12: (1, {'@': 307}), 67: (1, {'@': 307}), 25: (1, {'@': 307}), 15: (1, {'@': 307}), 16: (1, {'@': 307}), 1: (1, {'@': 307}), 17: (1, {'@': 307}), 18: (1, {'@': 307}), 20: (1, {'@': 307}), 7: (1, {'@': 307}), 26: (1, {'@': 307}), 19: (1, {'@': 309})}, 156: {47: (0, 30)}, 157: {25: (1, {'@': 196}), 26: (1, {'@': 196})}, 158: {25: (1, {'@': 149}), 26: (1, {'@': 149})}, 159: {25: (1, {'@': 146}), 26: (1, {'@': 146}), 7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309})}, 160: {25: (1, {'@': 209}), 26: (1, {'@': 209})}, 161: {25: (1, {'@': 81}), 26: (1, {'@': 81})}, 162: {25: (1, {'@': 88}), 26: (1, {'@': 88})}, 163: {23: (0, 377), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 24: (0, 387), 9: (0, 454), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 22: (0, 505), 35: (0, 200), 2: (0, 252), 36: (0, 392)}, 164: {25: (1, {'@': 22}), 26: (1, {'@': 22})}, 165: {25: (1, {'@': 223}), 26: (1, {'@': 223})}, 166: {25: (1, {'@': 28}), 26: (1, {'@': 28})}, 167: {25: (1, {'@': 49}), 26: (1, {'@': 49})}, 168: {25: (1, {'@': 82}), 26: (1, {'@': 82})}, 169: {25: (1, {'@': 153}), 26: (1, {'@': 153})}, 170: {13: (1, {'@': 306}), 14: (1, {'@': 306}), 11: (1, {'@': 306}), 37: (1, {'@': 306}), 27: (1, {'@': 306}), 12: (1, {'@': 306}), 67: (1, {'@': 306}), 25: (1, {'@': 306}), 15: (1, {'@': 306}), 16: (1, {'@': 306}), 1: (1, {'@': 306}), 17: (1, {'@': 306}), 18: (1, {'@': 306}), 20: (1, {'@': 306}), 7: (1, {'@': 306}), 26: (1, {'@': 306})}, 171: {25: (1, {'@': 206}), 26: (1, {'@': 206})}, 172: {16: (0, 78), 12: (0, 446), 13: (0, 386), 14: (1, {'@': 320}), 15: (1, {'@': 320}), 1: (1, {'@': 320}), 17: (1, {'@': 320}), 18: (1, {'@': 320}), 37: (1, {'@': 320}), 20: (1, {'@': 320}), 7: (1, {'@': 320}), 25: (1, {'@': 320}), 26: (1, {'@': 320}), 27: (1, {'@': 320}), 11: (1, {'@': 320}), 67: (1, {'@': 320})}, 173: {}, 174: {47: (0, 29)}, 175: {13: (1, {'@': 310}), 14: (1, {'@': 310}), 11: (1, {'@': 310}), 37: (1, {'@': 310}), 27: (1, {'@': 310}), 12: (1, {'@': 310}), 67: (1, {'@': 310}), 25: (1, {'@': 310}), 15: (1, {'@': 310}), 16: (1, {'@': 310}), 1: (1, {'@': 310}), 17: (1, {'@': 310}), 18: (1, {'@': 310}), 20: (1, {'@': 310}), 7: (1, {'@': 310}), 26: (1, {'@': 310})}, 176: {25: (1, {'@': 211}), 26: (1, {'@': 211})}, 177: {14: (1, {'@': 305}), 11: (1, {'@': 305}), 37: (1, {'@': 305}), 27: (1, {'@': 305}), 67: (1, {'@': 305}), 25: (1, {'@': 305}), 15: (1, {'@': 305}), 1: (1, {'@': 305}), 17: (1, {'@': 305}), 18: (1, {'@': 305}), 20: (1, {'@': 305}), 7: (1, {'@': 305}), 26: (1, {'@': 305}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309})}, 178: {14: (1, {'@': 304}), 11: (1, {'@': 304}), 37: (1, {'@': 304}), 27: (1, {'@': 304}), 67: (1, {'@': 304}), 25: (1, {'@': 304}), 15: (1, {'@': 304}), 1: (1, {'@': 304}), 17: (1, {'@': 304}), 18: (1, {'@': 304}), 20: (1, {'@': 304}), 7: (1, {'@': 304}), 26: (1, {'@': 304})}, 179: {4: (0, 289), 25: (1, {'@': 63}), 26: (1, {'@': 63})}, 180: {25: (1, {'@': 156}), 26: (1, {'@': 156})}, 181: {25: (1, {'@': 164}), 26: (1, {'@': 164}), 7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309})}, 182: {25: (1, {'@': 163}), 26: (1, {'@': 163})}, 183: {22: (0, 399), 72: (0, 179), 23: (0, 416), 65: (0, 459), 66: (0, 447), 69: (0, 93), 73: (0, 210), 24: (0, 469), 21: (0, 501)}, 184: {25: (1, {'@': 199}), 26: (1, {'@': 199})}, 185: {1: (0, 437), 7: (0, 451), 37: (1, {'@': 315}), 20: (1, {'@': 315}), 14: (1, {'@': 315}), 15: (1, {'@': 315}), 17: (1, {'@': 315}), 18: (1, {'@': 315}), 25: (1, {'@': 315}), 26: (1, {'@': 315}), 27: (1, {'@': 315}), 11: (1, {'@': 315}), 67: (1, {'@': 315})}, 186: {1: (0, 437), 7: (0, 451), 37: (1, {'@': 317}), 20: (1, {'@': 317}), 14: (1, {'@': 317}), 15: (1, {'@': 317}), 17: (1, {'@': 317}), 18: (1, {'@': 317}), 25: (1, {'@': 317}), 26: (1, {'@': 317}), 27: (1, {'@': 317}), 11: (1, {'@': 317}), 67: (1, {'@': 317})}, 187: {22: (0, 399), 73: (0, 476), 23: (0, 416), 69: (0, 11), 65: (0, 459), 66: (0, 447), 72: (0, 458), 24: (0, 469), 21: (0, 501)}, 188: {1: (0, 437), 7: (0, 451), 37: (1, {'@': 313}), 20: (1, {'@': 313}), 14: (1, {'@': 313}), 15: (1, {'@': 313}), 17: (1, {'@': 313}), 18: (1, {'@': 313}), 25: (1, {'@': 313}), 26: (1, {'@': 313}), 27: (1, {'@': 313}), 11: (1, {'@': 313}), 67: (1, {'@': 313})}, 189: {1: (0, 437), 7: (0, 451), 37: (1, {'@': 314}), 20: (1, {'@': 314}), 14: (1, {'@': 314}), 15: (1, {'@': 314}), 17: (1, {'@': 314}), 18: (1, {'@': 314}), 25: (1, {'@': 314}), 26: (1, {'@': 314}), 27: (1, {'@': 314}), 11: (1, {'@': 314}), 67: (1, {'@': 314})}, 190: {27: (1, {'@': 294}), 25: (1, {'@': 294}), 26: (1, {'@': 294})}, 191: {48: (1, {'@': 263}), 50: (1, {'@': 263}), 38: (1, {'@': 263}), 45: (1, {'@': 263}), 46: (1, {'@': 263}), 0: (1, {'@': 263}), 6: (1, {'@': 263}), 47: (1, {'@': 263}), 49: (1, {'@': 263})}, 192: {12: (1, {'@': 323}), 13: (1, {'@': 323}), 14: (1, {'@': 323}), 15: (1, {'@': 323}), 16: (1, {'@': 323}), 1: (1, {'@': 323}), 17: (1, {'@': 323}), 18: (1, {'@': 323}), 37: (1, {'@': 323}), 20: (1, {'@': 323}), 7: (1, {'@': 323}), 25: (1, {'@': 323}), 26: (1, {'@': 323}), 27: (1, {'@': 323}), 11: (1, {'@': 323}), 67: (1, {'@': 323})}, 193: {12: (1, {'@': 324}), 13: (1, {'@': 324}), 14: (1, {'@': 324}), 15: (1, {'@': 324}), 16: (1, {'@': 324}), 1: (1, {'@': 324}), 17: (1, {'@': 324}), 18: (1, {'@': 324}), 37: (1, {'@': 324}), 20: (1, {'@': 324}), 7: (1, {'@': 324}), 25: (1, {'@': 324}), 26: (1, {'@': 324}), 27: (1, {'@': 324}), 11: (1, {'@': 324}), 67: (1, {'@': 324})}, 194: {12: (1, {'@': 326}), 13: (1, {'@': 326}), 14: (1, {'@': 326}), 15: (1, {'@': 326}), 16: (1, {'@': 326}), 1: (1, {'@': 326}), 17: (1, {'@': 326}), 18: (1, {'@': 326}), 37: (1, {'@': 326}), 20: (1, {'@': 326}), 7: (1, {'@': 326}), 25: (1, {'@': 326}), 26: (1, {'@': 326}), 27: (1, {'@': 326}), 11: (1, {'@': 326}), 67: (1, {'@': 326})}, 195: {25: (1, {'@': 160}), 26: (1, {'@': 160})}, 196: {25: (1, {'@': 197}), 26: (1, {'@': 197})}, 197: {25: (1, {'@': 189}), 26: (1, {'@': 189})}, 198: {27: (0, 363)}, 199: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 454), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252), 47: (0, 45), 36: (0, 392)}, 200: {12: (1, {'@': 325}), 13: (1, {'@': 325}), 14: (1, {'@': 325}), 15: (1, {'@': 325}), 16: (1, {'@': 325}), 1: (1, {'@': 325}), 17: (1, {'@': 325}), 18: (1, {'@': 325}), 37: (1, {'@': 325}), 20: (1, {'@': 325}), 7: (1, {'@': 325}), 25: (1, {'@': 325}), 26: (1, {'@': 325}), 27: (1, {'@': 325}), 11: (1, {'@': 325}), 67: (1, {'@': 325})}, 201: {74: (0, 300), 25: (1, {'@': 19}), 26: (1, {'@': 19})}, 202: {38: (0, 524), 39: (0, 430), 40: (0, 498), 52: (0, 448), 43: (0, 463), 23: (0, 416), 6: (0, 132), 69: (0, 465), 44: (0, 361), 45: (0, 414), 46: (0, 532), 47: (0, 520), 48: (0, 408), 66: (0, 447), 70: (0, 513), 24: (0, 469), 21: (0, 501), 0: (0, 530), 22: (0, 399), 41: (0, 502), 49: (0, 491), 50: (0, 481), 51: (0, 369), 53: (0, 462), 65: (0, 459), 71: (0, 474), 42: (0, 457)}, 203: {25: (1, {'@': 139}), 26: (1, {'@': 139})}, 204: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 460), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 36: (0, 449), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 205: {25: (1, {'@': 188}), 26: (1, {'@': 188})}, 206: {48: (1, {'@': 265}), 50: (1, {'@': 265}), 38: (1, {'@': 265}), 45: (1, {'@': 265}), 46: (1, {'@': 265}), 0: (1, {'@': 265}), 6: (1, {'@': 265}), 47: (1, {'@': 265}), 49: (1, {'@': 265})}, 207: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 47: (0, 48), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252), 36: (0, 445)}, 208: {28: (0, 499), 1: (0, 360), 9: (0, 468), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 36: (0, 378), 35: (0, 200), 2: (0, 252)}, 209: {25: (1, {'@': 155}), 26: (1, {'@': 155})}, 210: {25: (1, {'@': 135}), 26: (1, {'@': 135})}, 211: {25: (1, {'@': 161}), 26: (1, {'@': 161})}, 212: {25: (1, {'@': 198}), 26: (1, {'@': 198})}, 213: {25: (1, {'@': 167}), 26: (1, {'@': 167})}, 214: {25: (1, {'@': 157}), 26: (1, {'@': 157})}, 215: {48: (1, {'@': 266}), 50: (1, {'@': 266}), 38: (1, {'@': 266}), 45: (1, {'@': 266}), 46: (1, {'@': 266}), 0: (1, {'@': 266}), 6: (1, {'@': 266}), 47: (1, {'@': 266}), 49: (1, {'@': 266})}, 216: {25: (1, {'@': 159}), 26: (1, {'@': 159})}, 217: {1: (1, {'@': 260}), 6: (1, {'@': 260}), 4: (1, {'@': 260}), 8: (1, {'@': 260}), 2: (1, {'@': 260}), 7: (1, {'@': 260}), 3: (1, {'@': 260})}, 218: {25: (1, {'@': 115}), 26: (1, {'@': 115})}, 219: {25: (1, {'@': 20}), 26: (1, {'@': 20})}, 220: {25: (1, {'@': 116}), 26: (1, {'@': 116})}, 221: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 527), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 36: (0, 521), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 75: (0, 529), 35: (0, 200), 2: (0, 252)}, 222: {25: (0, 70)}, 223: {25: (1, {'@': 118}), 26: (1, {'@': 118})}, 224: {25: (1, {'@': 32}), 26: (1, {'@': 32})}, 225: {25: (1, {'@': 117}), 26: (1, {'@': 117})}, 226: {46: (0, 532), 41: (0, 281), 47: (0, 520), 49: (0, 491), 50: (0, 481), 51: (0, 369), 0: (0, 198), 48: (0, 408), 45: (0, 414)}, 227: {25: (1, {'@': 110}), 26: (1, {'@': 110})}, 228: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 64: (0, 487), 29: (0, 490), 30: (0, 509), 9: (0, 471), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 76: (0, 466), 7: (0, 374), 36: (0, 475), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 229: {25: (1, {'@': 100}), 26: (1, {'@': 100})}, 230: {25: (1, {'@': 111}), 26: (1, {'@': 111})}, 231: {54: (1, {'@': 5}), 25: (1, {'@': 5})}, 232: {25: (1, {'@': 106}), 26: (1, {'@': 106})}, 233: {27: (0, 503)}, 234: {25: (1, {'@': 108}), 26: (1, {'@': 108})}, 235: {62: (0, 470), 28: (0, 499), 55: (0, 424), 56: (0, 398), 9: (0, 535), 1: (0, 360), 6: (0, 355), 5: (0, 472), 29: (0, 490), 30: (0, 509), 47: (0, 390), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 36: (0, 494), 33: (0, 395), 61: (0, 388), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 236: {25: (1, {'@': 113}), 26: (1, {'@': 113})}, 237: {25: (1, {'@': 109}), 26: (1, {'@': 109})}, 238: {25: (1, {'@': 170}), 26: (1, {'@': 170})}, 239: {25: (1, {'@': 112}), 26: (1, {'@': 112})}, 240: {25: (1, {'@': 195}), 26: (1, {'@': 195})}, 241: {25: (1, {'@': 124}), 26: (1, {'@': 124}), 7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309})}, 242: {25: (1, {'@': 213}), 26: (1, {'@': 213})}, 243: {27: (0, 0)}, 244: {25: (1, {'@': 107}), 26: (1, {'@': 107})}, 245: {64: (0, 492)}, 246: {71: (0, 69)}, 247: {25: (1, {'@': 121}), 26: (1, {'@': 121})}, 248: {7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309}), 25: (1, {'@': 122}), 26: (1, {'@': 122})}, 249: {25: (1, {'@': 98}), 26: (1, {'@': 98})}, 250: {25: (0, 231), 54: (1, {'@': 2})}, 251: {25: (1, {'@': 126}), 26: (1, {'@': 126})}, 252: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 36: (0, 366), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 253: {25: (1, {'@': 101}), 26: (1, {'@': 101})}, 254: {25: (1, {'@': 99}), 26: (1, {'@': 99})}, 255: {48: (1, {'@': 267}), 50: (1, {'@': 267}), 38: (1, {'@': 267}), 45: (1, {'@': 267}), 46: (1, {'@': 267}), 0: (1, {'@': 267}), 6: (1, {'@': 267}), 47: (1, {'@': 267}), 49: (1, {'@': 267})}, 256: {25: (1, {'@': 96}), 26: (1, {'@': 96})}, 257: {77: (0, 479), 78: (0, 423), 79: (0, 429), 80: (0, 296), 81: (0, 206), 82: (0, 497), 83: (0, 226), 84: (0, 90), 85: (0, 107), 86: (0, 3), 87: (0, 111), 88: (0, 136), 89: (0, 75), 90: (0, 259), 91: (0, 58), 92: (0, 326), 93: (0, 183), 94: (0, 21), 95: (0, 26), 96: (0, 265), 97: (0, 291), 98: (0, 32), 4: (0, 201), 99: (0, 240), 100: (0, 37), 101: (0, 171), 102: (0, 215), 103: (0, 52), 8: (0, 219), 104: (0, 295), 105: (0, 337), 106: (0, 515), 107: (0, 344), 108: (0, 208), 109: (0, 262), 110: (0, 275), 111: (0, 91), 112: (0, 124), 113: (0, 285), 114: (0, 129), 115: (0, 140), 116: (0, 299), 117: (0, 167), 118: (0, 315), 119: (0, 318), 120: (0, 187), 121: (0, 191), 122: (0, 204), 123: (0, 184), 124: (0, 314), 125: (0, 235), 126: (0, 121), 127: (0, 145), 128: (0, 347), 129: (0, 284), 130: (0, 325), 131: (0, 287), 72: (0, 305), 132: (0, 308), 133: (0, 292), 134: (0, 245), 135: (0, 202), 136: (0, 196), 137: (0, 266), 138: (0, 278), 139: (0, 255), 140: (0, 268), 141: (0, 242), 142: (0, 261), 143: (0, 221), 144: (0, 228), 145: (0, 134), 146: (0, 97), 147: (0, 113), 148: (0, 157), 149: (0, 101), 150: (0, 176), 151: (0, 138), 152: (0, 160), 153: (0, 117), 154: (0, 148), 155: (0, 126), 156: (0, 297), 157: (0, 341), 158: (0, 104), 159: (0, 165), 160: (0, 301), 161: (0, 282), 162: (0, 217), 163: (0, 212), 25: (1, {'@': 16}), 26: (1, {'@': 16}), 54: (1, {'@': 0})}, 258: {25: (1, {'@': 97}), 26: (1, {'@': 97})}, 259: {22: (0, 383), 0: (0, 364)}, 260: {25: (1, {'@': 119}), 26: (1, {'@': 119})}, 261: {25: (1, {'@': 215}), 26: (1, {'@': 215})}, 262: {36: (0, 222), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 4), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 25: (0, 480), 35: (0, 200), 2: (0, 252)}, 263: {25: (1, {'@': 94}), 26: (1, {'@': 94})}, 264: {25: (1, {'@': 104}), 26: (1, {'@': 104})}, 265: {6: (1, {'@': 256}), 8: (1, {'@': 256}), 2: (1, {'@': 256}), 49: (1, {'@': 256}), 50: (1, {'@': 256}), 45: (1, {'@': 256}), 0: (1, {'@': 256}), 1: (1, {'@': 256}), 47: (1, {'@': 256}), 4: (1, {'@': 256}), 3: (1, {'@': 256}), 48: (1, {'@': 256}), 39: (1, {'@': 256}), 53: (1, {'@': 256}), 38: (1, {'@': 256}), 46: (1, {'@': 256}), 44: (1, {'@': 256}), 40: (1, {'@': 256}), 7: (1, {'@': 256})}, 266: {6: (1, {'@': 255}), 8: (1, {'@': 255}), 2: (1, {'@': 255}), 49: (1, {'@': 255}), 50: (1, {'@': 255}), 45: (1, {'@': 255}), 0: (1, {'@': 255}), 1: (1, {'@': 255}), 47: (1, {'@': 255}), 4: (1, {'@': 255}), 3: (1, {'@': 255}), 48: (1, {'@': 255}), 39: (1, {'@': 255}), 53: (1, {'@': 255}), 38: (1, {'@': 255}), 46: (1, {'@': 255}), 44: (1, {'@': 255}), 40: (1, {'@': 255}), 7: (1, {'@': 255})}, 267: {25: (1, {'@': 102}), 26: (1, {'@': 102})}, 268: {25: (1, {'@': 212}), 26: (1, {'@': 212})}, 269: {38: (0, 524), 0: (0, 209), 42: (0, 211), 6: (0, 132), 45: (0, 414), 46: (0, 532), 47: (0, 520), 48: (0, 408), 49: (0, 491), 41: (0, 214), 51: (0, 369), 50: (0, 481), 43: (0, 216)}, 270: {25: (1, {'@': 127}), 26: (1, {'@': 127})}, 271: {25: (1, {'@': 103}), 26: (1, {'@': 103})}, 272: {25: (1, {'@': 105}), 26: (1, {'@': 105})}, 273: {25: (1, {'@': 123}), 26: (1, {'@': 123})}, 274: {38: (0, 524), 0: (0, 180), 6: (0, 132), 45: (0, 414), 46: (0, 532), 47: (0, 520), 48: (0, 408), 41: (0, 127), 49: (0, 491), 50: (0, 481), 43: (0, 195), 51: (0, 369), 42: (0, 105)}, 275: {25: (1, {'@': 194}), 26: (1, {'@': 194})}, 276: {143: (1, {'@': 11}), 159: (1, {'@': 11}), 152: (1, {'@': 11}), 157: (1, {'@': 11}), 139: (1, {'@': 11}), 83: (1, {'@': 11}), 94: (1, {'@': 11}), 97: (1, {'@': 11}), 124: (1, {'@': 11}), 118: (1, {'@': 11}), 130: (1, {'@': 11}), 133: (1, {'@': 11}), 25: (1, {'@': 11}), 137: (1, {'@': 11}), 96: (1, {'@': 11}), 95: (1, {'@': 11}), 89: (1, {'@': 11}), 113: (1, {'@': 11}), 4: (1, {'@': 11}), 154: (1, {'@': 11}), 127: (1, {'@': 11}), 102: (1, {'@': 11}), 86: (1, {'@': 11}), 141: (1, {'@': 11}), 148: (1, {'@': 11}), 126: (1, {'@': 11}), 144: (1, {'@': 11}), 79: (1, {'@': 11}), 101: (1, {'@': 11}), 142: (1, {'@': 11}), 162: (1, {'@': 11}), 117: (1, {'@': 11}), 98: (1, {'@': 11}), 105: (1, {'@': 11}), 112: (1, {'@': 11}), 146: (1, {'@': 11}), 123: (1, {'@': 11}), 147: (1, {'@': 11}), 138: (1, {'@': 11}), 151: (1, {'@': 11}), 8: (1, {'@': 11}), 81: (1, {'@': 11}), 129: (1, {'@': 11}), 120: (1, {'@': 11}), 116: (1, {'@': 11}), 131: (1, {'@': 11}), 119: (1, {'@': 11}), 99: (1, {'@': 11}), 135: (1, {'@': 11}), 93: (1, {'@': 11}), 122: (1, {'@': 11}), 90: (1, {'@': 11}), 111: (1, {'@': 11}), 108: (1, {'@': 11}), 54: (1, {'@': 11}), 156: (1, {'@': 11}), 160: (1, {'@': 11}), 92: (1, {'@': 11}), 158: (1, {'@': 11}), 150: (1, {'@': 11}), 153: (1, {'@': 11}), 145: (1, {'@': 11}), 109: (1, {'@': 11}), 114: (1, {'@': 11}), 110: (1, {'@': 11}), 72: (1, {'@': 11}), 132: (1, {'@': 11}), 161: (1, {'@': 11}), 140: (1, {'@': 11}), 125: (1, {'@': 11}), 136: (1, {'@': 11}), 134: (1, {'@': 11}), 115: (1, {'@': 11}), 87: (1, {'@': 11}), 85: (1, {'@': 11}), 121: (1, {'@': 11}), 155: (1, {'@': 11}), 77: (1, {'@': 11}), 88: (1, {'@': 11}), 103: (1, {'@': 11}), 100: (1, {'@': 11}), 163: (1, {'@': 11}), 80: (1, {'@': 11}), 107: (1, {'@': 11}), 26: (1, {'@': 11})}, 277: {143: (1, {'@': 10}), 159: (1, {'@': 10}), 152: (1, {'@': 10}), 157: (1, {'@': 10}), 139: (1, {'@': 10}), 83: (1, {'@': 10}), 94: (1, {'@': 10}), 97: (1, {'@': 10}), 124: (1, {'@': 10}), 118: (1, {'@': 10}), 130: (1, {'@': 10}), 133: (1, {'@': 10}), 25: (1, {'@': 10}), 137: (1, {'@': 10}), 96: (1, {'@': 10}), 95: (1, {'@': 10}), 89: (1, {'@': 10}), 113: (1, {'@': 10}), 4: (1, {'@': 10}), 154: (1, {'@': 10}), 127: (1, {'@': 10}), 102: (1, {'@': 10}), 86: (1, {'@': 10}), 141: (1, {'@': 10}), 148: (1, {'@': 10}), 126: (1, {'@': 10}), 144: (1, {'@': 10}), 79: (1, {'@': 10}), 101: (1, {'@': 10}), 142: (1, {'@': 10}), 162: (1, {'@': 10}), 117: (1, {'@': 10}), 98: (1, {'@': 10}), 105: (1, {'@': 10}), 112: (1, {'@': 10}), 146: (1, {'@': 10}), 123: (1, {'@': 10}), 147: (1, {'@': 10}), 138: (1, {'@': 10}), 151: (1, {'@': 10}), 8: (1, {'@': 10}), 81: (1, {'@': 10}), 129: (1, {'@': 10}), 120: (1, {'@': 10}), 116: (1, {'@': 10}), 131: (1, {'@': 10}), 119: (1, {'@': 10}), 99: (1, {'@': 10}), 135: (1, {'@': 10}), 93: (1, {'@': 10}), 122: (1, {'@': 10}), 90: (1, {'@': 10}), 111: (1, {'@': 10}), 108: (1, {'@': 10}), 54: (1, {'@': 10}), 156: (1, {'@': 10}), 160: (1, {'@': 10}), 92: (1, {'@': 10}), 158: (1, {'@': 10}), 150: (1, {'@': 10}), 153: (1, {'@': 10}), 145: (1, {'@': 10}), 109: (1, {'@': 10}), 114: (1, {'@': 10}), 110: (1, {'@': 10}), 72: (1, {'@': 10}), 132: (1, {'@': 10}), 161: (1, {'@': 10}), 140: (1, {'@': 10}), 125: (1, {'@': 10}), 136: (1, {'@': 10}), 134: (1, {'@': 10}), 115: (1, {'@': 10}), 87: (1, {'@': 10}), 85: (1, {'@': 10}), 121: (1, {'@': 10}), 155: (1, {'@': 10}), 77: (1, {'@': 10}), 88: (1, {'@': 10}), 103: (1, {'@': 10}), 100: (1, {'@': 10}), 163: (1, {'@': 10}), 80: (1, {'@': 10}), 107: (1, {'@': 10}), 26: (1, {'@': 10})}, 278: {25: (1, {'@': 205}), 26: (1, {'@': 205})}, 279: {25: (1, {'@': 140}), 26: (1, {'@': 140})}, 280: {27: (0, 42)}, 281: {27: (0, 354)}, 282: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 36: (0, 504), 35: (0, 200), 2: (0, 252)}, 283: {25: (1, {'@': 228}), 27: (1, {'@': 228}), 26: (1, {'@': 228})}, 284: {25: (1, {'@': 222}), 26: (1, {'@': 222})}, 285: {64: (0, 427)}, 286: {7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309}), 25: (1, {'@': 120}), 26: (1, {'@': 120})}, 287: {25: (1, {'@': 217}), 26: (1, {'@': 217})}, 288: {143: (1, {'@': 8}), 152: (1, {'@': 8}), 157: (1, {'@': 8}), 83: (1, {'@': 8}), 97: (1, {'@': 8}), 124: (1, {'@': 8}), 130: (1, {'@': 8}), 133: (1, {'@': 8}), 25: (1, {'@': 8}), 137: (1, {'@': 8}), 96: (1, {'@': 8}), 89: (1, {'@': 8}), 4: (1, {'@': 8}), 154: (1, {'@': 8}), 127: (1, {'@': 8}), 102: (1, {'@': 8}), 126: (1, {'@': 8}), 144: (1, {'@': 8}), 101: (1, {'@': 8}), 162: (1, {'@': 8}), 117: (1, {'@': 8}), 98: (1, {'@': 8}), 105: (1, {'@': 8}), 112: (1, {'@': 8}), 123: (1, {'@': 8}), 147: (1, {'@': 8}), 8: (1, {'@': 8}), 81: (1, {'@': 8}), 120: (1, {'@': 8}), 116: (1, {'@': 8}), 135: (1, {'@': 8}), 93: (1, {'@': 8}), 122: (1, {'@': 8}), 90: (1, {'@': 8}), 111: (1, {'@': 8}), 54: (1, {'@': 8}), 150: (1, {'@': 8}), 109: (1, {'@': 8}), 114: (1, {'@': 8}), 110: (1, {'@': 8}), 72: (1, {'@': 8}), 161: (1, {'@': 8}), 140: (1, {'@': 8}), 134: (1, {'@': 8}), 87: (1, {'@': 8}), 155: (1, {'@': 8}), 107: (1, {'@': 8}), 26: (1, {'@': 8}), 159: (1, {'@': 8}), 139: (1, {'@': 8}), 94: (1, {'@': 8}), 118: (1, {'@': 8}), 95: (1, {'@': 8}), 113: (1, {'@': 8}), 86: (1, {'@': 8}), 141: (1, {'@': 8}), 148: (1, {'@': 8}), 79: (1, {'@': 8}), 142: (1, {'@': 8}), 146: (1, {'@': 8}), 138: (1, {'@': 8}), 151: (1, {'@': 8}), 129: (1, {'@': 8}), 131: (1, {'@': 8}), 119: (1, {'@': 8}), 99: (1, {'@': 8}), 108: (1, {'@': 8}), 156: (1, {'@': 8}), 160: (1, {'@': 8}), 92: (1, {'@': 8}), 158: (1, {'@': 8}), 153: (1, {'@': 8}), 145: (1, {'@': 8}), 132: (1, {'@': 8}), 125: (1, {'@': 8}), 136: (1, {'@': 8}), 115: (1, {'@': 8}), 85: (1, {'@': 8}), 121: (1, {'@': 8}), 77: (1, {'@': 8}), 88: (1, {'@': 8}), 103: (1, {'@': 8}), 100: (1, {'@': 8}), 163: (1, {'@': 8}), 80: (1, {'@': 8})}, 289: {25: (1, {'@': 64}), 26: (1, {'@': 64})}, 290: {25: (1, {'@': 92}), 26: (1, {'@': 92})}, 291: {4: (0, 333), 164: (0, 323)}, 292: {25: (1, {'@': 225}), 26: (1, {'@': 225})}, 293: {25: (1, {'@': 90}), 26: (1, {'@': 90})}, 294: {25: (1, {'@': 125}), 26: (1, {'@': 125})}, 295: {38: (0, 524), 39: (0, 430), 40: (0, 498), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 163), 29: (0, 490), 30: (0, 509), 0: (0, 467), 9: (0, 371), 4: (0, 486), 42: (0, 444), 31: (0, 488), 32: (0, 412), 3: (0, 473), 44: (0, 361), 33: (0, 395), 45: (0, 414), 43: (0, 396), 7: (0, 374), 36: (0, 420), 46: (0, 532), 47: (0, 520), 52: (0, 384), 48: (0, 408), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 49: (0, 491), 2: (0, 252), 50: (0, 481), 53: (0, 462), 51: (0, 369), 41: (0, 373)}, 296: {38: (0, 365), 28: (0, 499), 55: (0, 424), 56: (0, 398), 1: (0, 360), 5: (0, 472), 6: (0, 443), 29: (0, 490), 30: (0, 509), 60: (0, 526), 57: (0, 434), 47: (0, 390), 9: (0, 517), 58: (0, 528), 59: (0, 516), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 43: (0, 453), 33: (0, 395), 61: (0, 388), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 62: (0, 335), 35: (0, 200), 36: (0, 238), 2: (0, 252), 63: (0, 190)}, 297: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 527), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 75: (0, 464), 36: (0, 521), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 298: {25: (1, {'@': 91}), 26: (1, {'@': 91})}, 299: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 511), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252), 36: (0, 512)}, 300: {9: (0, 43), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252), 36: (0, 46)}, 301: {1: (1, {'@': 259}), 6: (1, {'@': 259}), 4: (1, {'@': 259}), 8: (1, {'@': 259}), 2: (1, {'@': 259}), 7: (1, {'@': 259}), 3: (1, {'@': 259})}, 302: {25: (1, {'@': 93}), 26: (1, {'@': 93})}, 303: {25: (1, {'@': 59}), 26: (1, {'@': 59})}, 304: {46: (0, 532), 47: (0, 520), 49: (0, 491), 50: (0, 481), 51: (0, 369), 48: (0, 408), 0: (0, 51), 45: (0, 414), 41: (0, 55)}, 305: {4: (0, 508)}, 306: {27: (0, 35)}, 307: {25: (1, {'@': 247}), 26: (1, {'@': 247}), 27: (1, {'@': 247})}, 308: {48: (1, {'@': 268}), 50: (1, {'@': 268}), 38: (1, {'@': 268}), 45: (1, {'@': 268}), 46: (1, {'@': 268}), 0: (1, {'@': 268}), 6: (1, {'@': 268}), 47: (1, {'@': 268}), 49: (1, {'@': 268})}, 309: {7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309}), 25: (1, {'@': 166}), 26: (1, {'@': 166})}, 310: {25: (1, {'@': 165}), 26: (1, {'@': 165})}, 311: {165: (0, 50)}, 312: {27: (0, 39), 25: (1, {'@': 69}), 26: (1, {'@': 69})}, 313: {71: (0, 263), 22: (0, 258), 65: (0, 256), 66: (0, 57)}, 314: {25: (1, {'@': 193}), 26: (1, {'@': 193})}, 315: {6: (0, 401), 73: (0, 68), 38: (0, 246), 65: (0, 500)}, 316: {46: (0, 532), 47: (0, 520), 49: (0, 491), 50: (0, 481), 51: (0, 369), 48: (0, 408), 41: (0, 62), 0: (0, 353), 45: (0, 414)}, 317: {1: (0, 360), 2: (0, 252), 3: (0, 473), 4: (0, 486), 5: (0, 472), 6: (0, 355), 7: (0, 374), 8: (0, 332), 9: (0, 12), 10: (0, 362)}, 318: {25: (1, {'@': 210}), 26: (1, {'@': 210})}, 319: {0: (0, 74)}, 320: {27: (0, 23)}, 321: {37: (0, 60), 12: (1, {'@': 331}), 13: (1, {'@': 331}), 14: (1, {'@': 331}), 15: (1, {'@': 331}), 16: (1, {'@': 331}), 1: (1, {'@': 331}), 17: (1, {'@': 331}), 18: (1, {'@': 331}), 19: (1, {'@': 331}), 20: (1, {'@': 331}), 7: (1, {'@': 331})}, 322: {7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309}), 25: (1, {'@': 169}), 26: (1, {'@': 169})}, 323: {27: (0, 489), 25: (1, {'@': 50}), 26: (1, {'@': 50})}, 324: {25: (1, {'@': 168}), 26: (1, {'@': 168})}, 325: {25: (1, {'@': 219}), 26: (1, {'@': 219})}, 326: {55: (0, 424), 56: (0, 398), 63: (0, 190), 57: (0, 434), 62: (0, 335), 47: (0, 390), 60: (0, 213), 61: (0, 388), 58: (0, 528), 59: (0, 516), 25: (1, {'@': 218}), 26: (1, {'@': 218})}, 327: {7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309}), 25: (1, {'@': 238}), 27: (1, {'@': 238}), 26: (1, {'@': 238})}, 328: {27: (0, 274), 7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309})}, 329: {25: (1, {'@': 237}), 27: (1, {'@': 237}), 26: (1, {'@': 237})}, 330: {25: (1, {'@': 191}), 26: (1, {'@': 191})}, 331: {7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309}), 25: (1, {'@': 233}), 27: (1, {'@': 233}), 26: (1, {'@': 233})}, 332: {12: (1, {'@': 335}), 13: (1, {'@': 335}), 14: (1, {'@': 335}), 15: (1, {'@': 335}), 16: (1, {'@': 335}), 1: (1, {'@': 335}), 17: (1, {'@': 335}), 18: (1, {'@': 335}), 37: (1, {'@': 335}), 19: (1, {'@': 335}), 20: (1, {'@': 335}), 7: (1, {'@': 335}), 25: (1, {'@': 335}), 26: (1, {'@': 335}), 27: (1, {'@': 335}), 11: (1, {'@': 335}), 67: (1, {'@': 335})}, 333: {25: (1, {'@': 227}), 27: (1, {'@': 227}), 26: (1, {'@': 227})}, 334: {25: (1, {'@': 234}), 27: (1, {'@': 234}), 26: (1, {'@': 234})}, 335: {27: (1, {'@': 296}), 25: (1, {'@': 296}), 26: (1, {'@': 296})}, 336: {25: (1, {'@': 232}), 27: (1, {'@': 232}), 26: (1, {'@': 232})}, 337: {6: (1, {'@': 253}), 8: (1, {'@': 253}), 2: (1, {'@': 253}), 49: (1, {'@': 253}), 50: (1, {'@': 253}), 45: (1, {'@': 253}), 0: (1, {'@': 253}), 1: (1, {'@': 253}), 47: (1, {'@': 253}), 4: (1, {'@': 253}), 3: (1, {'@': 253}), 48: (1, {'@': 253}), 39: (1, {'@': 253}), 53: (1, {'@': 253}), 38: (1, {'@': 253}), 46: (1, {'@': 253}), 44: (1, {'@': 253}), 40: (1, {'@': 253}), 7: (1, {'@': 253})}, 338: {37: (0, 63), 12: (1, {'@': 330}), 13: (1, {'@': 330}), 14: (1, {'@': 330}), 15: (1, {'@': 330}), 16: (1, {'@': 330}), 1: (1, {'@': 330}), 17: (1, {'@': 330}), 18: (1, {'@': 330}), 19: (1, {'@': 330}), 20: (1, {'@': 330}), 7: (1, {'@': 330})}, 339: {13: (1, {'@': 333}), 14: (1, {'@': 333}), 37: (1, {'@': 333}), 19: (1, {'@': 333}), 12: (1, {'@': 333}), 15: (1, {'@': 333}), 16: (1, {'@': 333}), 1: (1, {'@': 333}), 17: (1, {'@': 333}), 18: (1, {'@': 333}), 20: (1, {'@': 333}), 7: (1, {'@': 333}), 25: (1, {'@': 333}), 26: (1, {'@': 333}), 27: (1, {'@': 333}), 11: (1, {'@': 333}), 67: (1, {'@': 333})}, 340: {25: (1, {'@': 248}), 26: (1, {'@': 248}), 27: (1, {'@': 248})}, 341: {25: (1, {'@': 203}), 26: (1, {'@': 203})}, 342: {25: (1, {'@': 301}), 26: (1, {'@': 301}), 27: (1, {'@': 301})}, 343: {0: (0, 73)}, 344: {25: (1, {'@': 202}), 26: (1, {'@': 202})}, 345: {25: (1, {'@': 34}), 26: (1, {'@': 34})}, 346: {27: (0, 304)}, 347: {143: (1, {'@': 14}), 159: (1, {'@': 14}), 152: (1, {'@': 14}), 157: (1, {'@': 14}), 139: (1, {'@': 14}), 83: (1, {'@': 14}), 94: (1, {'@': 14}), 97: (1, {'@': 14}), 124: (1, {'@': 14}), 118: (1, {'@': 14}), 130: (1, {'@': 14}), 133: (1, {'@': 14}), 25: (1, {'@': 14}), 137: (1, {'@': 14}), 96: (1, {'@': 14}), 95: (1, {'@': 14}), 89: (1, {'@': 14}), 113: (1, {'@': 14}), 4: (1, {'@': 14}), 154: (1, {'@': 14}), 127: (1, {'@': 14}), 102: (1, {'@': 14}), 86: (1, {'@': 14}), 141: (1, {'@': 14}), 148: (1, {'@': 14}), 126: (1, {'@': 14}), 144: (1, {'@': 14}), 79: (1, {'@': 14}), 101: (1, {'@': 14}), 142: (1, {'@': 14}), 162: (1, {'@': 14}), 117: (1, {'@': 14}), 98: (1, {'@': 14}), 105: (1, {'@': 14}), 112: (1, {'@': 14}), 146: (1, {'@': 14}), 123: (1, {'@': 14}), 147: (1, {'@': 14}), 138: (1, {'@': 14}), 151: (1, {'@': 14}), 8: (1, {'@': 14}), 81: (1, {'@': 14}), 129: (1, {'@': 14}), 120: (1, {'@': 14}), 116: (1, {'@': 14}), 131: (1, {'@': 14}), 119: (1, {'@': 14}), 99: (1, {'@': 14}), 135: (1, {'@': 14}), 93: (1, {'@': 14}), 122: (1, {'@': 14}), 90: (1, {'@': 14}), 111: (1, {'@': 14}), 108: (1, {'@': 14}), 54: (1, {'@': 14}), 156: (1, {'@': 14}), 160: (1, {'@': 14}), 92: (1, {'@': 14}), 158: (1, {'@': 14}), 150: (1, {'@': 14}), 153: (1, {'@': 14}), 145: (1, {'@': 14}), 109: (1, {'@': 14}), 114: (1, {'@': 14}), 110: (1, {'@': 14}), 72: (1, {'@': 14}), 132: (1, {'@': 14}), 161: (1, {'@': 14}), 140: (1, {'@': 14}), 125: (1, {'@': 14}), 136: (1, {'@': 14}), 134: (1, {'@': 14}), 115: (1, {'@': 14}), 87: (1, {'@': 14}), 85: (1, {'@': 14}), 121: (1, {'@': 14}), 155: (1, {'@': 14}), 77: (1, {'@': 14}), 88: (1, {'@': 14}), 103: (1, {'@': 14}), 100: (1, {'@': 14}), 163: (1, {'@': 14}), 80: (1, {'@': 14}), 107: (1, {'@': 14}), 26: (1, {'@': 14})}, 348: {28: (0, 499), 1: (0, 360), 65: (0, 14), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 23: (0, 393), 3: (0, 473), 24: (0, 410), 66: (0, 17), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 22: (0, 394), 2: (0, 252), 36: (0, 445)}, 349: {9: (0, 2), 1: (0, 360), 2: (0, 252), 3: (0, 473), 4: (0, 486), 5: (0, 472), 6: (0, 355), 7: (0, 374), 8: (0, 332), 10: (0, 362)}, 350: {37: (0, 71), 12: (1, {'@': 330}), 13: (1, {'@': 330}), 14: (1, {'@': 330}), 15: (1, {'@': 330}), 16: (1, {'@': 330}), 1: (1, {'@': 330}), 17: (1, {'@': 330}), 18: (1, {'@': 330}), 19: (1, {'@': 330}), 20: (1, {'@': 330}), 7: (1, {'@': 330})}, 351: {25: (1, {'@': 80}), 26: (1, {'@': 80}), 7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309})}, 352: {25: (1, {'@': 242}), 26: (1, {'@': 242}), 27: (1, {'@': 242})}, 353: {25: (1, {'@': 184}), 26: (1, {'@': 184})}, 354: {38: (0, 174), 6: (0, 156)}, 355: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 454), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252), 36: (0, 392)}, 356: {11: (0, 368)}, 357: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 41: (0, 164), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 45: (0, 414), 0: (0, 166), 46: (0, 532), 7: (0, 374), 36: (0, 168), 47: (0, 520), 48: (0, 408), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 49: (0, 491), 2: (0, 252), 50: (0, 481), 51: (0, 369)}, 358: {11: (0, 306)}, 359: {66: (0, 115), 23: (0, 416), 65: (0, 218), 22: (0, 220), 21: (0, 223), 71: (0, 225), 24: (0, 469)}, 360: {1: (0, 360), 2: (0, 252), 3: (0, 473), 4: (0, 486), 5: (0, 472), 6: (0, 355), 7: (0, 374), 8: (0, 332), 9: (0, 418), 10: (0, 362)}, 361: {25: (1, {'@': 285}), 26: (1, {'@': 285}), 27: (1, {'@': 285})}, 362: {12: (1, {'@': 329}), 13: (1, {'@': 329}), 14: (1, {'@': 329}), 15: (1, {'@': 329}), 16: (1, {'@': 329}), 1: (1, {'@': 329}), 17: (1, {'@': 329}), 18: (1, {'@': 329}), 37: (1, {'@': 329}), 19: (1, {'@': 329}), 20: (1, {'@': 329}), 7: (1, {'@': 329}), 25: (1, {'@': 329}), 26: (1, {'@': 329}), 27: (1, {'@': 329}), 11: (1, {'@': 329}), 67: (1, {'@': 329})}, 363: {9: (0, 197), 6: (0, 199), 166: (0, 205), 38: (0, 207)}, 364: {27: (0, 47)}, 365: {21: (0, 484), 23: (0, 416), 22: (0, 394), 24: (0, 469)}, 366: {67: (0, 110)}, 367: {27: (0, 357)}, 368: {27: (0, 28)}, 369: {25: (1, {'@': 277}), 26: (1, {'@': 277}), 27: (1, {'@': 277})}, 370: {22: (0, 399), 0: (0, 203), 23: (0, 416), 69: (0, 279), 65: (0, 459), 66: (0, 447), 71: (0, 153), 24: (0, 469), 21: (0, 501)}, 371: {25: (1, {'@': 134}), 26: (1, {'@': 134}), 7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309})}, 372: {166: (0, 158), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 159), 38: (0, 9), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252), 36: (0, 161)}, 373: {25: (1, {'@': 128}), 26: (1, {'@': 128})}, 374: {1: (0, 360), 2: (0, 252), 3: (0, 473), 9: (0, 379), 5: (0, 472), 7: (0, 374), 8: (0, 332), 6: (0, 355), 10: (0, 385), 4: (0, 486)}, 375: {27: (0, 319)}, 376: {27: (0, 372)}, 377: {28: (0, 499), 1: (0, 349), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 102), 10: (0, 382), 34: (0, 456), 8: (0, 332), 36: (0, 118), 35: (0, 200), 2: (0, 252)}, 378: {25: (1, {'@': 173}), 26: (1, {'@': 173})}, 379: {12: (1, {'@': 330}), 13: (1, {'@': 330}), 14: (1, {'@': 330}), 15: (1, {'@': 330}), 16: (1, {'@': 330}), 1: (1, {'@': 330}), 17: (1, {'@': 330}), 18: (1, {'@': 330}), 37: (1, {'@': 330}), 19: (1, {'@': 330}), 20: (1, {'@': 330}), 7: (1, {'@': 330}), 25: (1, {'@': 330}), 26: (1, {'@': 330}), 27: (1, {'@': 330}), 11: (1, {'@': 330}), 67: (1, {'@': 330})}, 380: {25: (1, {'@': 154}), 26: (1, {'@': 154})}, 381: {27: (0, 436)}, 382: {19: (1, {'@': 308}), 12: (1, {'@': 327}), 13: (1, {'@': 327}), 14: (1, {'@': 327}), 15: (1, {'@': 327}), 16: (1, {'@': 327}), 1: (1, {'@': 327}), 17: (1, {'@': 327}), 18: (1, {'@': 327}), 37: (1, {'@': 327}), 20: (1, {'@': 327}), 7: (1, {'@': 327}), 25: (1, {'@': 327}), 26: (1, {'@': 327}), 27: (1, {'@': 327}), 11: (1, {'@': 327}), 67: (1, {'@': 327})}, 383: {27: (0, 313)}, 384: {25: (1, {'@': 129}), 26: (1, {'@': 129})}, 385: {12: (1, {'@': 328}), 13: (1, {'@': 328}), 14: (1, {'@': 328}), 15: (1, {'@': 328}), 16: (1, {'@': 328}), 1: (1, {'@': 328}), 17: (1, {'@': 328}), 18: (1, {'@': 328}), 37: (1, {'@': 328}), 19: (1, {'@': 328}), 20: (1, {'@': 328}), 7: (1, {'@': 328}), 25: (1, {'@': 328}), 26: (1, {'@': 328}), 27: (1, {'@': 328}), 11: (1, {'@': 328}), 67: (1, {'@': 328})}, 386: {9: (0, 95), 1: (0, 360), 10: (0, 382), 2: (0, 252), 3: (0, 473), 68: (0, 193), 6: (0, 355), 34: (0, 456), 7: (0, 374), 5: (0, 472), 8: (0, 332), 4: (0, 486), 35: (0, 175)}, 387: {28: (0, 499), 1: (0, 317), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 120), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252), 36: (0, 130)}, 388: {27: (1, {'@': 299}), 25: (1, {'@': 299}), 26: (1, {'@': 299})}, 389: {73: (0, 311)}, 390: {27: (1, {'@': 298}), 25: (1, {'@': 298}), 26: (1, {'@': 298})}, 391: {37: (0, 375)}, 392: {11: (0, 339)}, 393: {28: (0, 499), 1: (0, 24), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 33), 36: (0, 44), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 394: {37: (0, 405)}, 395: {16: (0, 78), 12: (0, 446), 13: (0, 386)}, 396: {25: (1, {'@': 131}), 26: (1, {'@': 131})}, 397: {27: (0, 343)}, 398: {27: (1, {'@': 300}), 25: (1, {'@': 300}), 26: (1, {'@': 300})}, 399: {25: (1, {'@': 288}), 26: (1, {'@': 288}), 27: (1, {'@': 288})}, 400: {25: (1, {'@': 178}), 26: (1, {'@': 178})}, 401: {71: (0, 358)}, 402: {25: (1, {'@': 19}), 26: (1, {'@': 19})}, 403: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 186), 30: (0, 178), 4: (0, 486), 3: (0, 473), 33: (0, 395), 7: (0, 374), 9: (0, 177), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 404: {29: (0, 188), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 30: (0, 178), 4: (0, 486), 3: (0, 473), 33: (0, 395), 7: (0, 374), 9: (0, 177), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 405: {25: (1, {'@': 240}), 26: (1, {'@': 240}), 27: (1, {'@': 240})}, 406: {27: (0, 455)}, 407: {37: (0, 397)}, 408: {25: (1, {'@': 275}), 26: (1, {'@': 275}), 27: (1, {'@': 275})}, 409: {143: (1, {'@': 13}), 159: (1, {'@': 13}), 152: (1, {'@': 13}), 157: (1, {'@': 13}), 139: (1, {'@': 13}), 83: (1, {'@': 13}), 94: (1, {'@': 13}), 97: (1, {'@': 13}), 124: (1, {'@': 13}), 118: (1, {'@': 13}), 130: (1, {'@': 13}), 133: (1, {'@': 13}), 25: (1, {'@': 13}), 137: (1, {'@': 13}), 96: (1, {'@': 13}), 95: (1, {'@': 13}), 89: (1, {'@': 13}), 113: (1, {'@': 13}), 4: (1, {'@': 13}), 154: (1, {'@': 13}), 127: (1, {'@': 13}), 102: (1, {'@': 13}), 86: (1, {'@': 13}), 141: (1, {'@': 13}), 148: (1, {'@': 13}), 126: (1, {'@': 13}), 144: (1, {'@': 13}), 79: (1, {'@': 13}), 101: (1, {'@': 13}), 142: (1, {'@': 13}), 162: (1, {'@': 13}), 117: (1, {'@': 13}), 98: (1, {'@': 13}), 105: (1, {'@': 13}), 112: (1, {'@': 13}), 146: (1, {'@': 13}), 123: (1, {'@': 13}), 147: (1, {'@': 13}), 138: (1, {'@': 13}), 151: (1, {'@': 13}), 8: (1, {'@': 13}), 81: (1, {'@': 13}), 129: (1, {'@': 13}), 120: (1, {'@': 13}), 116: (1, {'@': 13}), 131: (1, {'@': 13}), 119: (1, {'@': 13}), 99: (1, {'@': 13}), 135: (1, {'@': 13}), 93: (1, {'@': 13}), 122: (1, {'@': 13}), 90: (1, {'@': 13}), 111: (1, {'@': 13}), 108: (1, {'@': 13}), 54: (1, {'@': 13}), 156: (1, {'@': 13}), 160: (1, {'@': 13}), 92: (1, {'@': 13}), 158: (1, {'@': 13}), 150: (1, {'@': 13}), 153: (1, {'@': 13}), 145: (1, {'@': 13}), 109: (1, {'@': 13}), 114: (1, {'@': 13}), 110: (1, {'@': 13}), 72: (1, {'@': 13}), 132: (1, {'@': 13}), 161: (1, {'@': 13}), 140: (1, {'@': 13}), 125: (1, {'@': 13}), 136: (1, {'@': 13}), 134: (1, {'@': 13}), 115: (1, {'@': 13}), 87: (1, {'@': 13}), 85: (1, {'@': 13}), 121: (1, {'@': 13}), 155: (1, {'@': 13}), 77: (1, {'@': 13}), 88: (1, {'@': 13}), 103: (1, {'@': 13}), 100: (1, {'@': 13}), 163: (1, {'@': 13}), 80: (1, {'@': 13}), 107: (1, {'@': 13}), 26: (1, {'@': 13})}, 410: {1: (0, 1), 28: (0, 499), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 36: (0, 49), 7: (0, 54), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 411: {27: (0, 523)}, 412: {14: (0, 413), 15: (0, 426), 18: (0, 403), 17: (0, 404), 20: (0, 421)}, 413: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 139), 30: (0, 178), 4: (0, 486), 3: (0, 473), 33: (0, 395), 7: (0, 374), 9: (0, 177), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 414: {25: (1, {'@': 280}), 26: (1, {'@': 280}), 27: (1, {'@': 280})}, 415: {0: (0, 224)}, 416: {27: (1, {'@': 291}), 25: (1, {'@': 291}), 26: (1, {'@': 291}), 37: (1, {'@': 291}), 11: (1, {'@': 291})}, 417: {25: (1, {'@': 172}), 26: (1, {'@': 172})}, 418: {12: (1, {'@': 331}), 13: (1, {'@': 331}), 14: (1, {'@': 331}), 15: (1, {'@': 331}), 16: (1, {'@': 331}), 1: (1, {'@': 331}), 17: (1, {'@': 331}), 18: (1, {'@': 331}), 37: (1, {'@': 331}), 19: (1, {'@': 331}), 20: (1, {'@': 331}), 7: (1, {'@': 331}), 25: (1, {'@': 331}), 26: (1, {'@': 331}), 27: (1, {'@': 331}), 11: (1, {'@': 331}), 67: (1, {'@': 331})}, 419: {7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309}), 25: (1, {'@': 176}), 26: (1, {'@': 176})}, 420: {25: (1, {'@': 133}), 26: (1, {'@': 133})}, 421: {29: (0, 189), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 30: (0, 178), 4: (0, 486), 3: (0, 473), 33: (0, 395), 7: (0, 374), 9: (0, 177), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 422: {27: (0, 477)}, 423: {26: (0, 518), 25: (0, 485)}, 424: {27: (1, {'@': 297}), 25: (1, {'@': 297}), 26: (1, {'@': 297})}, 425: {27: (0, 38)}, 426: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 185), 30: (0, 178), 4: (0, 486), 3: (0, 473), 33: (0, 395), 7: (0, 374), 9: (0, 177), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 427: {27: (0, 40), 25: (1, {'@': 68}), 26: (1, {'@': 68})}, 428: {25: (1, {'@': 18}), 26: (1, {'@': 18})}, 429: {71: (0, 422), 42: (0, 538), 38: (0, 537), 0: (0, 531), 6: (0, 495), 166: (0, 525), 47: (0, 520), 52: (0, 507), 21: (0, 501), 50: (0, 481), 53: (0, 462), 65: (0, 459), 41: (0, 441), 9: (0, 438), 39: (0, 430), 23: (0, 416), 45: (0, 414), 69: (0, 376), 167: (0, 406), 22: (0, 399), 51: (0, 369), 43: (0, 367), 44: (0, 361), 48: (0, 408), 66: (0, 447), 24: (0, 469), 49: (0, 491), 40: (0, 498), 46: (0, 532), 168: (0, 519)}, 430: {25: (1, {'@': 284}), 26: (1, {'@': 284}), 27: (1, {'@': 284})}, 431: {25: (1, {'@': 151}), 26: (1, {'@': 151})}, 432: {11: (0, 66)}, 433: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 47: (0, 452), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252), 36: (0, 445)}, 434: {27: (1, {'@': 292}), 25: (1, {'@': 292}), 26: (1, {'@': 292})}, 435: {38: (0, 524), 39: (0, 430), 40: (0, 498), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 163), 29: (0, 490), 30: (0, 509), 9: (0, 351), 4: (0, 486), 0: (0, 141), 31: (0, 488), 32: (0, 412), 41: (0, 143), 3: (0, 473), 44: (0, 361), 33: (0, 395), 45: (0, 414), 7: (0, 374), 46: (0, 532), 47: (0, 520), 42: (0, 146), 48: (0, 408), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 36: (0, 147), 52: (0, 150), 2: (0, 252), 49: (0, 491), 53: (0, 462), 51: (0, 369), 50: (0, 481), 43: (0, 151)}, 436: {38: (0, 524), 39: (0, 430), 40: (0, 498), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 163), 29: (0, 490), 30: (0, 509), 9: (0, 241), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 0: (0, 264), 44: (0, 361), 41: (0, 267), 33: (0, 395), 45: (0, 414), 7: (0, 374), 46: (0, 532), 47: (0, 520), 48: (0, 408), 10: (0, 382), 34: (0, 456), 8: (0, 332), 42: (0, 270), 35: (0, 200), 52: (0, 271), 43: (0, 272), 49: (0, 491), 2: (0, 252), 50: (0, 481), 51: (0, 369), 53: (0, 462), 36: (0, 273)}, 437: {33: (0, 154), 1: (0, 360), 5: (0, 472), 6: (0, 355), 4: (0, 486), 3: (0, 473), 7: (0, 374), 9: (0, 155), 10: (0, 382), 34: (0, 456), 8: (0, 332), 28: (0, 170), 35: (0, 200), 2: (0, 252)}, 438: {27: (0, 370)}, 439: {11: (0, 162)}, 440: {9: (0, 95), 1: (0, 360), 10: (0, 382), 2: (0, 252), 3: (0, 473), 6: (0, 355), 34: (0, 456), 7: (0, 374), 5: (0, 472), 8: (0, 332), 68: (0, 194), 4: (0, 486), 35: (0, 175)}, 441: {27: (0, 435)}, 442: {27: (0, 478)}, 443: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 454), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 23: (0, 416), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 22: (0, 505), 35: (0, 200), 24: (0, 469), 2: (0, 252), 36: (0, 392), 21: (0, 439)}, 444: {25: (1, {'@': 132}), 26: (1, {'@': 132})}, 445: {37: (0, 342)}, 446: {9: (0, 95), 1: (0, 360), 10: (0, 382), 2: (0, 252), 3: (0, 473), 6: (0, 355), 34: (0, 456), 7: (0, 374), 5: (0, 472), 8: (0, 332), 68: (0, 192), 4: (0, 486), 35: (0, 175)}, 447: {25: (1, {'@': 286}), 26: (1, {'@': 286}), 27: (1, {'@': 286})}, 448: {25: (1, {'@': 274}), 26: (1, {'@': 274})}, 449: {25: (1, {'@': 60}), 26: (1, {'@': 60})}, 450: {25: (1, {'@': 142}), 26: (1, {'@': 142})}, 451: {1: (0, 360), 5: (0, 472), 6: (0, 355), 4: (0, 486), 3: (0, 473), 33: (0, 172), 7: (0, 374), 9: (0, 155), 10: (0, 382), 34: (0, 456), 8: (0, 332), 28: (0, 170), 35: (0, 200), 2: (0, 252)}, 452: {37: (0, 346)}, 453: {25: (1, {'@': 87}), 26: (1, {'@': 87})}, 454: {11: (0, 72), 7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309})}, 455: {0: (0, 345)}, 456: {19: (0, 440)}, 457: {25: (1, {'@': 77}), 26: (1, {'@': 77})}, 458: {25: (1, {'@': 65}), 26: (1, {'@': 65})}, 459: {25: (1, {'@': 287}), 26: (1, {'@': 287}), 27: (1, {'@': 287})}, 460: {7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309}), 25: (1, {'@': 61}), 26: (1, {'@': 61})}, 461: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 36: (0, 5), 33: (0, 395), 41: (0, 19), 45: (0, 414), 7: (0, 374), 46: (0, 532), 47: (0, 520), 48: (0, 408), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 0: (0, 22), 49: (0, 491), 2: (0, 252), 50: (0, 481), 51: (0, 369)}, 462: {25: (1, {'@': 282}), 26: (1, {'@': 282}), 27: (1, {'@': 282})}, 463: {25: (1, {'@': 272}), 26: (1, {'@': 272})}, 464: {27: (0, 98), 25: (1, {'@': 53}), 26: (1, {'@': 53})}, 465: {25: (1, {'@': 271}), 26: (1, {'@': 271})}, 466: {27: (0, 53), 25: (1, {'@': 51}), 26: (1, {'@': 51})}, 467: {25: (1, {'@': 130}), 26: (1, {'@': 130})}, 468: {7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309}), 25: (1, {'@': 177}), 26: (1, {'@': 177})}, 469: {27: (1, {'@': 290}), 25: (1, {'@': 290}), 26: (1, {'@': 290}), 37: (1, {'@': 290}), 11: (1, {'@': 290})}, 470: {27: (0, 64)}, 471: {7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 25: (1, {'@': 231}), 27: (1, {'@': 231}), 26: (1, {'@': 231}), 19: (1, {'@': 309})}, 472: {12: (1, {'@': 332}), 13: (1, {'@': 332}), 14: (1, {'@': 332}), 15: (1, {'@': 332}), 16: (1, {'@': 332}), 1: (1, {'@': 332}), 17: (1, {'@': 332}), 18: (1, {'@': 332}), 37: (1, {'@': 332}), 19: (1, {'@': 332}), 20: (1, {'@': 332}), 7: (1, {'@': 332}), 25: (1, {'@': 332}), 26: (1, {'@': 332}), 27: (1, {'@': 332}), 11: (1, {'@': 332}), 67: (1, {'@': 332})}, 473: {12: (1, {'@': 338}), 13: (1, {'@': 338}), 14: (1, {'@': 338}), 15: (1, {'@': 338}), 16: (1, {'@': 338}), 1: (1, {'@': 338}), 17: (1, {'@': 338}), 18: (1, {'@': 338}), 37: (1, {'@': 338}), 19: (1, {'@': 338}), 20: (1, {'@': 338}), 7: (1, {'@': 338}), 25: (1, {'@': 338}), 26: (1, {'@': 338}), 27: (1, {'@': 338}), 11: (1, {'@': 338}), 67: (1, {'@': 338})}, 474: {25: (1, {'@': 269}), 26: (1, {'@': 269})}, 475: {25: (1, {'@': 230}), 27: (1, {'@': 230}), 26: (1, {'@': 230})}, 476: {25: (1, {'@': 137}), 26: (1, {'@': 137})}, 477: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 8), 38: (0, 9), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 23: (0, 416), 166: (0, 13), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 24: (0, 469), 35: (0, 200), 2: (0, 252), 22: (0, 15), 21: (0, 16), 36: (0, 18)}, 478: {0: (0, 61)}, 479: {166: (0, 442), 6: (0, 522), 9: (0, 411), 38: (0, 433)}, 480: {54: (1, {'@': 4}), 25: (1, {'@': 4})}, 481: {25: (1, {'@': 281}), 26: (1, {'@': 281}), 27: (1, {'@': 281})}, 482: {65: (0, 244), 71: (0, 237), 22: (0, 234), 66: (0, 232)}, 483: {25: (1, {'@': 78}), 26: (1, {'@': 78})}, 484: {37: (0, 114)}, 485: {143: (1, {'@': 12}), 159: (1, {'@': 12}), 152: (1, {'@': 12}), 157: (1, {'@': 12}), 139: (1, {'@': 12}), 83: (1, {'@': 12}), 94: (1, {'@': 12}), 97: (1, {'@': 12}), 124: (1, {'@': 12}), 118: (1, {'@': 12}), 130: (1, {'@': 12}), 133: (1, {'@': 12}), 25: (1, {'@': 12}), 137: (1, {'@': 12}), 96: (1, {'@': 12}), 95: (1, {'@': 12}), 89: (1, {'@': 12}), 113: (1, {'@': 12}), 4: (1, {'@': 12}), 154: (1, {'@': 12}), 127: (1, {'@': 12}), 102: (1, {'@': 12}), 86: (1, {'@': 12}), 141: (1, {'@': 12}), 148: (1, {'@': 12}), 126: (1, {'@': 12}), 144: (1, {'@': 12}), 79: (1, {'@': 12}), 101: (1, {'@': 12}), 142: (1, {'@': 12}), 162: (1, {'@': 12}), 117: (1, {'@': 12}), 98: (1, {'@': 12}), 105: (1, {'@': 12}), 112: (1, {'@': 12}), 146: (1, {'@': 12}), 123: (1, {'@': 12}), 147: (1, {'@': 12}), 138: (1, {'@': 12}), 151: (1, {'@': 12}), 8: (1, {'@': 12}), 81: (1, {'@': 12}), 129: (1, {'@': 12}), 120: (1, {'@': 12}), 116: (1, {'@': 12}), 131: (1, {'@': 12}), 119: (1, {'@': 12}), 99: (1, {'@': 12}), 135: (1, {'@': 12}), 93: (1, {'@': 12}), 122: (1, {'@': 12}), 90: (1, {'@': 12}), 111: (1, {'@': 12}), 108: (1, {'@': 12}), 54: (1, {'@': 12}), 156: (1, {'@': 12}), 160: (1, {'@': 12}), 92: (1, {'@': 12}), 158: (1, {'@': 12}), 150: (1, {'@': 12}), 153: (1, {'@': 12}), 145: (1, {'@': 12}), 109: (1, {'@': 12}), 114: (1, {'@': 12}), 110: (1, {'@': 12}), 72: (1, {'@': 12}), 132: (1, {'@': 12}), 161: (1, {'@': 12}), 140: (1, {'@': 12}), 125: (1, {'@': 12}), 136: (1, {'@': 12}), 134: (1, {'@': 12}), 115: (1, {'@': 12}), 87: (1, {'@': 12}), 85: (1, {'@': 12}), 121: (1, {'@': 12}), 155: (1, {'@': 12}), 77: (1, {'@': 12}), 88: (1, {'@': 12}), 103: (1, {'@': 12}), 100: (1, {'@': 12}), 163: (1, {'@': 12}), 80: (1, {'@': 12}), 107: (1, {'@': 12}), 26: (1, {'@': 12})}, 486: {12: (1, {'@': 336}), 13: (1, {'@': 336}), 14: (1, {'@': 336}), 15: (1, {'@': 336}), 16: (1, {'@': 336}), 1: (1, {'@': 336}), 17: (1, {'@': 336}), 18: (1, {'@': 336}), 37: (1, {'@': 336}), 19: (1, {'@': 336}), 20: (1, {'@': 336}), 7: (1, {'@': 336}), 25: (1, {'@': 336}), 26: (1, {'@': 336}), 27: (1, {'@': 336}), 11: (1, {'@': 336}), 67: (1, {'@': 336})}, 487: {25: (1, {'@': 229}), 27: (1, {'@': 229}), 26: (1, {'@': 229})}, 488: {14: (1, {'@': 302}), 15: (1, {'@': 302}), 17: (1, {'@': 302}), 18: (1, {'@': 302}), 20: (1, {'@': 302}), 37: (1, {'@': 312}), 25: (1, {'@': 312}), 26: (1, {'@': 312}), 27: (1, {'@': 312}), 11: (1, {'@': 312}), 67: (1, {'@': 312})}, 489: {4: (0, 283)}, 490: {1: (0, 437), 7: (0, 451)}, 491: {25: (1, {'@': 278}), 26: (1, {'@': 278}), 27: (1, {'@': 278})}, 492: {143: (1, {'@': 15}), 159: (1, {'@': 15}), 152: (1, {'@': 15}), 157: (1, {'@': 15}), 139: (1, {'@': 15}), 83: (1, {'@': 15}), 94: (1, {'@': 15}), 97: (1, {'@': 15}), 124: (1, {'@': 15}), 118: (1, {'@': 15}), 130: (1, {'@': 15}), 133: (1, {'@': 15}), 25: (1, {'@': 15}), 137: (1, {'@': 15}), 96: (1, {'@': 15}), 95: (1, {'@': 15}), 89: (1, {'@': 15}), 113: (1, {'@': 15}), 4: (1, {'@': 15}), 154: (1, {'@': 15}), 127: (1, {'@': 15}), 102: (1, {'@': 15}), 86: (1, {'@': 15}), 141: (1, {'@': 15}), 148: (1, {'@': 15}), 126: (1, {'@': 15}), 144: (1, {'@': 15}), 79: (1, {'@': 15}), 101: (1, {'@': 15}), 142: (1, {'@': 15}), 162: (1, {'@': 15}), 117: (1, {'@': 15}), 98: (1, {'@': 15}), 105: (1, {'@': 15}), 112: (1, {'@': 15}), 146: (1, {'@': 15}), 123: (1, {'@': 15}), 147: (1, {'@': 15}), 138: (1, {'@': 15}), 151: (1, {'@': 15}), 8: (1, {'@': 15}), 81: (1, {'@': 15}), 129: (1, {'@': 15}), 120: (1, {'@': 15}), 116: (1, {'@': 15}), 131: (1, {'@': 15}), 119: (1, {'@': 15}), 99: (1, {'@': 15}), 135: (1, {'@': 15}), 93: (1, {'@': 15}), 122: (1, {'@': 15}), 90: (1, {'@': 15}), 111: (1, {'@': 15}), 108: (1, {'@': 15}), 54: (1, {'@': 15}), 156: (1, {'@': 15}), 160: (1, {'@': 15}), 92: (1, {'@': 15}), 158: (1, {'@': 15}), 150: (1, {'@': 15}), 153: (1, {'@': 15}), 145: (1, {'@': 15}), 109: (1, {'@': 15}), 114: (1, {'@': 15}), 110: (1, {'@': 15}), 72: (1, {'@': 15}), 132: (1, {'@': 15}), 161: (1, {'@': 15}), 140: (1, {'@': 15}), 125: (1, {'@': 15}), 136: (1, {'@': 15}), 134: (1, {'@': 15}), 115: (1, {'@': 15}), 87: (1, {'@': 15}), 85: (1, {'@': 15}), 121: (1, {'@': 15}), 155: (1, {'@': 15}), 77: (1, {'@': 15}), 88: (1, {'@': 15}), 103: (1, {'@': 15}), 100: (1, {'@': 15}), 163: (1, {'@': 15}), 80: (1, {'@': 15}), 107: (1, {'@': 15}), 26: (1, {'@': 15})}, 493: {25: (1, {'@': 239}), 26: (1, {'@': 239}), 27: (1, {'@': 239})}, 494: {25: (1, {'@': 171}), 26: (1, {'@': 171})}, 495: {23: (0, 377), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 24: (0, 387), 9: (0, 454), 4: (0, 486), 65: (0, 506), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 22: (0, 505), 35: (0, 200), 2: (0, 252), 66: (0, 356), 36: (0, 392)}, 496: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 181), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 36: (0, 182), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252)}, 497: {143: (1, {'@': 9}), 152: (1, {'@': 9}), 157: (1, {'@': 9}), 83: (1, {'@': 9}), 97: (1, {'@': 9}), 124: (1, {'@': 9}), 130: (1, {'@': 9}), 133: (1, {'@': 9}), 25: (1, {'@': 9}), 137: (1, {'@': 9}), 96: (1, {'@': 9}), 89: (1, {'@': 9}), 4: (1, {'@': 9}), 154: (1, {'@': 9}), 127: (1, {'@': 9}), 102: (1, {'@': 9}), 126: (1, {'@': 9}), 144: (1, {'@': 9}), 101: (1, {'@': 9}), 162: (1, {'@': 9}), 117: (1, {'@': 9}), 98: (1, {'@': 9}), 105: (1, {'@': 9}), 112: (1, {'@': 9}), 123: (1, {'@': 9}), 147: (1, {'@': 9}), 8: (1, {'@': 9}), 81: (1, {'@': 9}), 120: (1, {'@': 9}), 116: (1, {'@': 9}), 135: (1, {'@': 9}), 93: (1, {'@': 9}), 122: (1, {'@': 9}), 90: (1, {'@': 9}), 111: (1, {'@': 9}), 54: (1, {'@': 9}), 150: (1, {'@': 9}), 109: (1, {'@': 9}), 114: (1, {'@': 9}), 110: (1, {'@': 9}), 72: (1, {'@': 9}), 161: (1, {'@': 9}), 140: (1, {'@': 9}), 134: (1, {'@': 9}), 87: (1, {'@': 9}), 155: (1, {'@': 9}), 107: (1, {'@': 9}), 26: (1, {'@': 9}), 159: (1, {'@': 9}), 139: (1, {'@': 9}), 94: (1, {'@': 9}), 118: (1, {'@': 9}), 95: (1, {'@': 9}), 113: (1, {'@': 9}), 86: (1, {'@': 9}), 141: (1, {'@': 9}), 148: (1, {'@': 9}), 79: (1, {'@': 9}), 142: (1, {'@': 9}), 146: (1, {'@': 9}), 138: (1, {'@': 9}), 151: (1, {'@': 9}), 129: (1, {'@': 9}), 131: (1, {'@': 9}), 119: (1, {'@': 9}), 99: (1, {'@': 9}), 108: (1, {'@': 9}), 156: (1, {'@': 9}), 160: (1, {'@': 9}), 92: (1, {'@': 9}), 158: (1, {'@': 9}), 153: (1, {'@': 9}), 145: (1, {'@': 9}), 132: (1, {'@': 9}), 125: (1, {'@': 9}), 136: (1, {'@': 9}), 115: (1, {'@': 9}), 85: (1, {'@': 9}), 121: (1, {'@': 9}), 77: (1, {'@': 9}), 88: (1, {'@': 9}), 103: (1, {'@': 9}), 100: (1, {'@': 9}), 163: (1, {'@': 9}), 80: (1, {'@': 9})}, 498: {25: (1, {'@': 283}), 26: (1, {'@': 283}), 27: (1, {'@': 283})}, 499: {14: (1, {'@': 321}), 15: (1, {'@': 321}), 1: (1, {'@': 321}), 17: (1, {'@': 321}), 18: (1, {'@': 321}), 37: (1, {'@': 321}), 20: (1, {'@': 321}), 7: (1, {'@': 321}), 12: (1, {'@': 306}), 13: (1, {'@': 306}), 16: (1, {'@': 306}), 25: (1, {'@': 321}), 26: (1, {'@': 321}), 27: (1, {'@': 321}), 11: (1, {'@': 321}), 67: (1, {'@': 321})}, 500: {27: (0, 31)}, 501: {25: (1, {'@': 289}), 26: (1, {'@': 289}), 27: (1, {'@': 289})}, 502: {25: (1, {'@': 270}), 26: (1, {'@': 270})}, 503: {71: (0, 236), 66: (0, 227), 65: (0, 230), 22: (0, 239)}, 504: {25: (1, {'@': 179}), 26: (1, {'@': 179})}, 505: {11: (0, 493)}, 506: {11: (0, 243)}, 507: {27: (0, 83)}, 508: {25: (1, {'@': 62}), 26: (1, {'@': 62})}, 509: {7: (1, {'@': 304}), 1: (1, {'@': 304}), 37: (1, {'@': 318}), 20: (1, {'@': 318}), 14: (1, {'@': 318}), 15: (1, {'@': 318}), 17: (1, {'@': 318}), 18: (1, {'@': 318}), 25: (1, {'@': 318}), 26: (1, {'@': 318}), 27: (1, {'@': 318}), 11: (1, {'@': 318}), 67: (1, {'@': 318})}, 510: {77: (0, 479), 78: (0, 423), 79: (0, 429), 80: (0, 296), 81: (0, 206), 83: (0, 226), 84: (0, 90), 85: (0, 107), 86: (0, 3), 87: (0, 111), 88: (0, 136), 89: (0, 75), 90: (0, 259), 91: (0, 58), 92: (0, 326), 93: (0, 183), 94: (0, 21), 95: (0, 26), 96: (0, 265), 97: (0, 291), 98: (0, 32), 4: (0, 201), 99: (0, 240), 100: (0, 37), 101: (0, 171), 102: (0, 215), 103: (0, 52), 8: (0, 219), 104: (0, 295), 105: (0, 337), 106: (0, 250), 107: (0, 344), 108: (0, 208), 169: (0, 257), 109: (0, 262), 110: (0, 275), 111: (0, 91), 112: (0, 124), 113: (0, 285), 82: (0, 288), 114: (0, 129), 115: (0, 140), 116: (0, 299), 117: (0, 167), 118: (0, 315), 119: (0, 318), 120: (0, 187), 121: (0, 191), 122: (0, 204), 123: (0, 184), 124: (0, 314), 125: (0, 235), 126: (0, 121), 127: (0, 145), 128: (0, 347), 129: (0, 284), 130: (0, 325), 131: (0, 287), 72: (0, 305), 132: (0, 308), 133: (0, 292), 134: (0, 245), 135: (0, 202), 136: (0, 196), 137: (0, 266), 138: (0, 278), 139: (0, 255), 140: (0, 268), 141: (0, 242), 142: (0, 261), 143: (0, 221), 144: (0, 228), 145: (0, 134), 146: (0, 97), 147: (0, 113), 170: (0, 173), 149: (0, 101), 148: (0, 157), 150: (0, 176), 151: (0, 138), 152: (0, 160), 153: (0, 117), 154: (0, 148), 155: (0, 126), 156: (0, 297), 157: (0, 341), 158: (0, 104), 159: (0, 165), 160: (0, 301), 161: (0, 282), 162: (0, 217), 163: (0, 212), 25: (1, {'@': 16}), 26: (1, {'@': 16}), 54: (1, {'@': 3})}, 511: {25: (1, {'@': 67}), 26: (1, {'@': 67}), 7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309})}, 512: {25: (1, {'@': 66}), 26: (1, {'@': 66})}, 513: {25: (1, {'@': 75}), 26: (1, {'@': 75})}, 514: {7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309})}, 515: {25: (0, 231), 54: (1, {'@': 1})}, 516: {27: (1, {'@': 293}), 25: (1, {'@': 293}), 26: (1, {'@': 293})}, 517: {25: (1, {'@': 174}), 26: (1, {'@': 174}), 7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309})}, 518: {77: (0, 479), 79: (0, 429), 80: (0, 296), 81: (0, 206), 83: (0, 226), 84: (0, 90), 85: (0, 107), 86: (0, 3), 87: (0, 111), 88: (0, 136), 89: (0, 75), 149: (0, 428), 90: (0, 259), 91: (0, 58), 92: (0, 326), 93: (0, 183), 94: (0, 21), 95: (0, 26), 96: (0, 265), 97: (0, 291), 98: (0, 32), 99: (0, 240), 100: (0, 37), 101: (0, 171), 102: (0, 215), 103: (0, 52), 8: (0, 219), 104: (0, 295), 105: (0, 337), 107: (0, 344), 108: (0, 208), 110: (0, 275), 111: (0, 91), 112: (0, 124), 113: (0, 285), 114: (0, 129), 115: (0, 140), 116: (0, 299), 117: (0, 167), 118: (0, 315), 119: (0, 318), 120: (0, 187), 121: (0, 191), 122: (0, 204), 123: (0, 184), 124: (0, 314), 125: (0, 235), 126: (0, 121), 127: (0, 145), 129: (0, 284), 130: (0, 325), 131: (0, 287), 72: (0, 305), 25: (0, 409), 132: (0, 308), 133: (0, 292), 135: (0, 202), 136: (0, 196), 137: (0, 266), 138: (0, 278), 139: (0, 255), 140: (0, 268), 141: (0, 242), 142: (0, 261), 143: (0, 221), 144: (0, 228), 145: (0, 134), 146: (0, 97), 147: (0, 113), 148: (0, 157), 150: (0, 176), 4: (0, 402), 151: (0, 138), 152: (0, 160), 153: (0, 117), 154: (0, 148), 155: (0, 126), 156: (0, 297), 157: (0, 341), 158: (0, 104), 159: (0, 165), 160: (0, 301), 161: (0, 282), 162: (0, 217), 163: (0, 212)}, 519: {27: (0, 415)}, 520: {25: (1, {'@': 279}), 26: (1, {'@': 279}), 27: (1, {'@': 279})}, 521: {25: (1, {'@': 235}), 27: (1, {'@': 235}), 26: (1, {'@': 235})}, 522: {28: (0, 499), 1: (0, 360), 47: (0, 432), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 454), 4: (0, 486), 31: (0, 488), 32: (0, 412), 3: (0, 473), 33: (0, 395), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 2: (0, 252), 36: (0, 392)}, 523: {0: (0, 330)}, 524: {24: (0, 410), 23: (0, 393), 22: (0, 394)}, 525: {27: (0, 149)}, 526: {27: (0, 496)}, 527: {7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309}), 25: (1, {'@': 236}), 27: (1, {'@': 236}), 26: (1, {'@': 236})}, 528: {27: (1, {'@': 295}), 25: (1, {'@': 295}), 26: (1, {'@': 295})}, 529: {27: (0, 98), 25: (1, {'@': 52}), 26: (1, {'@': 52})}, 530: {25: (1, {'@': 273}), 26: (1, {'@': 273})}, 531: {27: (0, 536)}, 532: {25: (1, {'@': 276}), 26: (1, {'@': 276}), 27: (1, {'@': 276})}, 533: {27: (0, 482)}, 534: {25: (1, {'@': 76}), 26: (1, {'@': 76})}, 535: {7: (1, {'@': 305}), 1: (1, {'@': 305}), 14: (1, {'@': 303}), 15: (1, {'@': 303}), 17: (1, {'@': 303}), 18: (1, {'@': 303}), 20: (1, {'@': 303}), 12: (1, {'@': 307}), 13: (1, {'@': 307}), 16: (1, {'@': 307}), 19: (1, {'@': 309}), 25: (1, {'@': 175}), 26: (1, {'@': 175})}, 536: {38: (0, 348), 0: (0, 36), 39: (0, 430), 40: (0, 498), 166: (0, 65), 28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 59), 29: (0, 490), 30: (0, 509), 9: (0, 76), 4: (0, 486), 31: (0, 488), 32: (0, 412), 36: (0, 79), 3: (0, 473), 44: (0, 361), 33: (0, 395), 52: (0, 80), 45: (0, 414), 168: (0, 82), 7: (0, 374), 46: (0, 532), 47: (0, 520), 48: (0, 408), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 41: (0, 84), 49: (0, 491), 2: (0, 252), 50: (0, 481), 51: (0, 369), 53: (0, 462), 43: (0, 86), 167: (0, 87), 42: (0, 89)}, 537: {28: (0, 499), 1: (0, 360), 5: (0, 472), 6: (0, 355), 29: (0, 490), 30: (0, 509), 9: (0, 514), 4: (0, 486), 31: (0, 488), 32: (0, 412), 23: (0, 393), 3: (0, 473), 24: (0, 410), 66: (0, 407), 33: (0, 395), 65: (0, 391), 7: (0, 374), 10: (0, 382), 34: (0, 456), 8: (0, 332), 35: (0, 200), 22: (0, 394), 2: (0, 252), 36: (0, 445)}, 538: {27: (0, 461)}}, 'start_states': {'start': 510}, 'end_states': {'start': 173}}, '__type__': 'ParsingFrontend'}, 'rules': [{'@': 0}, {'@': 1}, {'@': 2}, {'@': 3}, {'@': 4}, {'@': 5}, {'@': 6}, {'@': 7}, {'@': 8}, {'@': 9}, {'@': 10}, {'@': 11}, {'@': 12}, {'@': 13}, {'@': 14}, {'@': 15}, {'@': 16}, {'@': 17}, {'@': 18}, {'@': 19}, {'@': 20}, {'@': 21}, {'@': 22}, {'@': 23}, {'@': 24}, {'@': 25}, {'@': 26}, {'@': 27}, {'@': 28}, {'@': 29}, {'@': 30}, {'@': 31}, {'@': 32}, {'@': 33}, {'@': 34}, {'@': 35}, {'@': 36}, {'@': 37}, {'@': 38}, {'@': 39}, {'@': 40}, {'@': 41}, {'@': 42}, {'@': 43}, {'@': 44}, {'@': 45}, {'@': 46}, {'@': 47}, {'@': 48}, {'@': 49}, {'@': 50}, {'@': 51}, {'@': 52}, {'@': 53}, {'@': 54}, {'@': 55}, {'@': 56}, {'@': 57}, {'@': 58}, {'@': 59}, {'@': 60}, {'@': 61}, {'@': 62}, {'@': 63}, {'@': 64}, {'@': 65}, {'@': 66}, {'@': 67}, {'@': 68}, {'@': 69}, {'@': 70}, {'@': 71}, {'@': 72}, {'@': 73}, {'@': 74}, {'@': 75}, {'@': 76}, {'@': 77}, {'@': 78}, {'@': 79}, {'@': 80}, {'@': 81}, {'@': 82}, {'@': 83}, {'@': 84}, {'@': 85}, {'@': 86}, {'@': 87}, {'@': 88}, {'@': 89}, {'@': 90}, {'@': 91}, {'@': 92}, {'@': 93}, {'@': 94}, {'@': 95}, {'@': 96}, {'@': 97}, {'@': 98}, {'@': 99}, {'@': 100}, {'@': 101}, {'@': 102}, {'@': 103}, {'@': 104}, {'@': 105}, {'@': 106}, {'@': 107}, {'@': 108}, {'@': 109}, {'@': 110}, {'@': 111}, {'@': 112}, {'@': 113}, {'@': 114}, {'@': 115}, {'@': 116}, {'@': 117}, {'@': 118}, {'@': 119}, {'@': 120}, {'@': 121}, {'@': 122}, {'@': 123}, {'@': 124}, {'@': 125}, {'@': 126}, {'@': 127}, {'@': 128}, {'@': 129}, {'@': 130}, {'@': 131}, {'@': 132}, {'@': 133}, {'@': 134}, {'@': 135}, {'@': 136}, {'@': 137}, {'@': 138}, {'@': 139}, {'@': 140}, {'@': 141}, {'@': 142}, {'@': 143}, {'@': 144}, {'@': 145}, {'@': 146}, {'@': 147}, {'@': 148}, {'@': 149}, {'@': 150}, {'@': 151}, {'@': 152}, {'@': 153}, {'@': 154}, {'@': 155}, {'@': 156}, {'@': 157}, {'@': 158}, {'@': 159}, {'@': 160}, {'@': 161}, {'@': 162}, {'@': 163}, {'@': 164}, {'@': 165}, {'@': 166}, {'@': 167}, {'@': 168}, {'@': 169}, {'@': 170}, {'@': 171}, {'@': 172}, {'@': 173}, {'@': 174}, {'@': 175}, {'@': 176}, {'@': 177}, {'@': 178}, {'@': 179}, {'@': 180}, {'@': 181}, {'@': 182}, {'@': 183}, {'@': 184}, {'@': 185}, {'@': 186}, {'@': 187}, {'@': 188}, {'@': 189}, {'@': 190}, {'@': 191}, {'@': 192}, {'@': 193}, {'@': 194}, {'@': 195}, {'@': 196}, {'@': 197}, {'@': 198}, {'@': 199}, {'@': 200}, {'@': 201}, {'@': 202}, {'@': 203}, {'@': 204}, {'@': 205}, {'@': 206}, {'@': 207}, {'@': 208}, {'@': 209}, {'@': 210}, {'@': 211}, {'@': 212}, {'@': 213}, {'@': 214}, {'@': 215}, {'@': 216}, {'@': 217}, {'@': 218}, {'@': 219}, {'@': 220}, {'@': 221}, {'@': 222}, {'@': 223}, {'@': 224}, {'@': 225}, {'@': 226}, {'@': 227}, {'@': 228}, {'@': 229}, {'@': 230}, {'@': 231}, {'@': 232}, {'@': 233}, {'@': 234}, {'@': 235}, {'@': 236}, {'@': 237}, {'@': 238}, {'@': 239}, {'@': 240}, {'@': 241}, {'@': 242}, {'@': 243}, {'@': 244}, {'@': 245}, {'@': 246}, {'@': 247}, {'@': 248}, {'@': 249}, {'@': 250}, {'@': 251}, {'@': 252}, {'@': 253}, {'@': 254}, {'@': 255}, {'@': 256}, {'@': 257}, {'@': 258}, {'@': 259}, {'@': 260}, {'@': 261}, {'@': 262}, {'@': 263}, {'@': 264}, {'@': 265}, {'@': 266}, {'@': 267}, {'@': 268}, {'@': 269}, {'@': 270}, {'@': 271}, {'@': 272}, {'@': 273}, {'@': 274}, {'@': 275}, {'@': 276}, {'@': 277}, {'@': 278}, {'@': 279}, {'@': 280}, {'@': 281}, {'@': 282}, {'@': 283}, {'@': 284}, {'@': 285}, {'@': 286}, {'@': 287}, {'@': 288}, {'@': 289}, {'@': 290}, {'@': 291}, {'@': 292}, {'@': 293}, {'@': 294}, {'@': 295}, {'@': 296}, {'@': 297}, {'@': 298}, {'@': 299}, {'@': 300}, {'@': 301}, {'@': 302}, {'@': 303}, {'@': 304}, {'@': 305}, {'@': 306}, {'@': 307}, {'@': 308}, {'@': 309}, {'@': 310}, {'@': 311}, {'@': 312}, {'@': 313}, {'@': 314}, {'@': 315}, {'@': 316}, {'@': 317}, {'@': 318}, {'@': 319}, {'@': 320}, {'@': 321}, {'@': 322}, {'@': 323}, {'@': 324}, {'@': 325}, {'@': 326}, {'@': 327}, {'@': 328}, {'@': 329}, {'@': 330}, {'@': 331}, {'@': 332}, {'@': 333}, {'@': 334}, {'@': 335}, {'@': 336}, {'@': 337}, {'@': 338}], 'options': {'debug': False, 'strict': False, 'keep_all_tokens': False, 'tree_class': None, 'cache': False, 'cache_grammar': False, 'postlex': None, 'parser': 'lalr', 'lexer': 'contextual', 'transformer': None, 'start': ['start'], 'priority': 'normal', 'ambiguity': 'auto', 'regex': False, 'propagate_positions': False, 'lexer_callbacks': {}, 'maybe_placeholders': False, 'edit_terminals': None, 'g_regex_flags': 0, 'use_bytes': False, 'ordered_sets': True, 'import_paths': [], 'source_path': None, '_plugins': {}}, '__type__': 'Lark'} +) +MEMO = ( +{0: {'origin': {'name': 'start', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 1: {'origin': {'name': 'start', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'endline', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 2: {'origin': {'name': 'start', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'endline', '__type__': 'NonTerminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 3: {'origin': {'name': 'start', '__type__': 'NonTerminal'}, 'expansion': [], 'order': 3, 'alias': 'empty_program', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 4: {'origin': {'name': 'endline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'END', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 5: {'origin': {'name': 'endline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'endline', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 6: {'origin': {'name': 'endline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'END', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'program_endline2', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 7: {'origin': {'name': 'endline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'END', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'program_endline2', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 8: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'line', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 9: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'line', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 10: {'origin': {'name': 'line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQU', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'def_label', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 11: {'origin': {'name': 'line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQU', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'def_label', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 12: {'origin': {'name': 'line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'asms', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'line_asm', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 13: {'origin': {'name': 'line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'asms', '__type__': 'NonTerminal'}, {'name': 'CO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'line_asm', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 14: {'origin': {'name': 'line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'preproc_line', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'preprocessor_line', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 15: {'origin': {'name': 'preproc_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': '_INIT', 'filter_out': True, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'preproc_line_init', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 16: {'origin': {'name': 'asms', '__type__': 'NonTerminal'}, 'expansion': [], 'order': 0, 'alias': 'asms_empty', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 17: {'origin': {'name': 'asms', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'asm', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'asms_asm', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 18: {'origin': {'name': 'asms', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'asms', '__type__': 'NonTerminal'}, {'name': 'CO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'asm', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'asms_asms_asm', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 19: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'asm_label', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 20: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INTEGER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'asm_label', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 21: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 22: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 23: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 24: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 25: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}], 'order': 6, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 26: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 7, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 27: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 8, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 28: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 9, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 29: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 10, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 30: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 11, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 31: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'I', 'filter_out': False, '__type__': 'Terminal'}], 'order': 12, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 32: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'I', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 13, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 33: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'R', 'filter_out': False, '__type__': 'Terminal'}], 'order': 14, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 34: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'R', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 15, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 35: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 16, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 36: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 17, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 37: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 18, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 38: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg_bcde', '__type__': 'NonTerminal'}], 'order': 19, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 39: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 20, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 40: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 21, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 41: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 22, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 42: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 23, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 43: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 24, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 44: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 25, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 45: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 26, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 46: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 27, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 47: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 28, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 48: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PROC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 29, 'alias': 'proc_scope', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 49: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ENDP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 30, 'alias': 'endp_scope', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 50: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LOCAL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'id_list', '__type__': 'NonTerminal'}], 'order': 31, 'alias': 'local_labels', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 51: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DEFB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_list', '__type__': 'NonTerminal'}], 'order': 32, 'alias': 'defb_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 52: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DEFS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'number_list', '__type__': 'NonTerminal'}], 'order': 33, 'alias': 'defs_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 53: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DEFW', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'number_list', '__type__': 'NonTerminal'}], 'order': 34, 'alias': 'defw_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 54: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 35, 'alias': 'asm_ldind_r8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 55: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 36, 'alias': 'asm_ldind_r8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 56: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 37, 'alias': 'asm_ldr8_ind', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 57: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 38, 'alias': 'asm_ldr8_ind', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 58: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'AF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'AF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'APO', 'filter_out': False, '__type__': 'Terminal'}], 'order': 39, 'alias': 'ex_af_af', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 59: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 40, 'alias': 'ex_de_hl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 60: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ORG', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 41, 'alias': 'org', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 61: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ORG', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 42, 'alias': 'org', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 62: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NAMESPACE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 43, 'alias': 'namespace', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 63: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PUSH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NAMESPACE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 44, 'alias': 'push_namespace', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 64: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PUSH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NAMESPACE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 45, 'alias': 'push_namespace', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 65: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'POP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NAMESPACE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 46, 'alias': 'pop_namespace', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 66: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ALIGN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 47, 'alias': 'align', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 67: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ALIGN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 48, 'alias': 'align', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 68: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INCBIN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}], 'order': 49, 'alias': 'incbin', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 69: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INCBIN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 50, 'alias': 'incbin', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 70: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INCBIN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 51, 'alias': 'incbin', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 71: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}], 'order': 52, 'alias': 'ex_sp_reg8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 72: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}], 'order': 53, 'alias': 'ex_sp_reg8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 73: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 54, 'alias': 'ex_sp_reg8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 74: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 55, 'alias': 'ex_sp_reg8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 75: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inc_reg', '__type__': 'NonTerminal'}], 'order': 56, 'alias': 'incdec', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 76: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DEC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inc_reg', '__type__': 'NonTerminal'}], 'order': 57, 'alias': 'incdec', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 77: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 58, 'alias': 'incdeci', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 78: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DEC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 59, 'alias': 'incdeci', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 79: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 60, 'alias': 'ld_reg_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 80: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 61, 'alias': 'ld_reg_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 81: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 62, 'alias': 'ld_reg_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 82: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 63, 'alias': 'ld_reg_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 83: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 64, 'alias': 'ld_reg_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 84: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 65, 'alias': 'ld_reg_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 85: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 66, 'alias': 'ld_reg_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 86: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 67, 'alias': 'ld_reg_val_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 87: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 68, 'alias': 'jp_hl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 88: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 69, 'alias': 'jp_hl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 89: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 70, 'alias': 'jp_hl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 90: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 71, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 91: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 72, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 92: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 73, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 93: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 74, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 94: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 75, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 95: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 76, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 96: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 77, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 97: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 78, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 98: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 79, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 99: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 80, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 100: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 81, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 101: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 82, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 102: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 83, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 103: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 84, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 104: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 85, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 105: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 86, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 106: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 87, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 107: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 88, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 108: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 89, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 109: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 90, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 110: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 91, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 111: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 92, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 112: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 93, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 113: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 94, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 114: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 95, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 115: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 96, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 116: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 97, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 117: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 98, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 118: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}], 'order': 99, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 119: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 100, 'alias': 'arith_a_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 120: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 101, 'alias': 'arith_a_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 121: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 102, 'alias': 'arith_a_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 122: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 103, 'alias': 'arith_a_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 123: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 104, 'alias': 'arith_a_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 124: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 105, 'alias': 'arith_a_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 125: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 106, 'alias': 'arith_a_reg_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 126: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 107, 'alias': 'arith_a_reg_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 127: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 108, 'alias': 'arith_a_reg_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 128: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitwiseop', '__type__': 'NonTerminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 109, 'alias': 'bitwiseop_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 129: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitwiseop', '__type__': 'NonTerminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 110, 'alias': 'bitwiseop_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 130: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitwiseop', '__type__': 'NonTerminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 111, 'alias': 'bitwiseop_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 131: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitwiseop', '__type__': 'NonTerminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 112, 'alias': 'bitwiseop_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 132: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitwiseop', '__type__': 'NonTerminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 113, 'alias': 'bitwiseop_reg_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 133: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitwiseop', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 114, 'alias': 'bitwise_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 134: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitwiseop', '__type__': 'NonTerminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 115, 'alias': 'bitwise_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 135: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PUSH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'AF', 'filter_out': False, '__type__': 'Terminal'}], 'order': 116, 'alias': 'push_pop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 136: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PUSH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16', '__type__': 'NonTerminal'}], 'order': 117, 'alias': 'push_pop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 137: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'POP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'AF', 'filter_out': False, '__type__': 'Terminal'}], 'order': 118, 'alias': 'push_pop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 138: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'POP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16', '__type__': 'NonTerminal'}], 'order': 119, 'alias': 'push_pop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 139: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 120, 'alias': 'ld_addr_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 140: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16', '__type__': 'NonTerminal'}], 'order': 121, 'alias': 'ld_addr_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 141: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 122, 'alias': 'ld_addr_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 142: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 123, 'alias': 'ld_addr_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 143: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16', '__type__': 'NonTerminal'}], 'order': 124, 'alias': 'ld_addr_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 144: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 125, 'alias': 'ld_addr_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 145: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 126, 'alias': 'ld_reg_addr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 146: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 127, 'alias': 'ld_reg_addr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 147: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 128, 'alias': 'ld_reg_addr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 148: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}], 'order': 129, 'alias': 'ld_reg_addr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 149: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}], 'order': 130, 'alias': 'ld_reg_addr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 150: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}], 'order': 131, 'alias': 'ld_reg_addr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 151: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'rotation', '__type__': 'NonTerminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 132, 'alias': 'rotate', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 152: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'rotation', '__type__': 'NonTerminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 133, 'alias': 'rotate', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 153: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'rotation', '__type__': 'NonTerminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 134, 'alias': 'rotate', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 154: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'rotation', '__type__': 'NonTerminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 135, 'alias': 'rotate_ix', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 155: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 136, 'alias': 'bit', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 156: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 137, 'alias': 'bit', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 157: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 138, 'alias': 'bit', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 158: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 139, 'alias': 'bit', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 159: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 140, 'alias': 'bit', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 160: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 141, 'alias': 'bit', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 161: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 142, 'alias': 'bit_ix', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 162: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 143, 'alias': 'bit_ix', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 163: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'jp_flags', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 144, 'alias': 'jp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 164: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'jp_flags', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 145, 'alias': 'jp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 165: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CALL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'jp_flags', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 146, 'alias': 'jp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 166: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CALL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'jp_flags', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 147, 'alias': 'jp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 167: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'jp_flags', '__type__': 'NonTerminal'}], 'order': 148, 'alias': 'ret', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 168: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'jr_flags', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 149, 'alias': 'jr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 169: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'jr_flags', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 150, 'alias': 'jr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 170: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 151, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 171: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 152, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 172: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CALL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 153, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 173: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DJNZ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 154, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 174: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 155, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 175: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 156, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 176: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CALL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 157, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 177: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DJNZ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 158, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 178: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RST', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 159, 'alias': 'rst', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 179: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IM', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 160, 'alias': 'im', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 180: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 161, 'alias': 'in_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 181: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 162, 'alias': 'in_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 182: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 163, 'alias': 'in_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 183: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 164, 'alias': 'in_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 184: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 165, 'alias': 'out_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 185: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 166, 'alias': 'out_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 186: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 167, 'alias': 'out_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 187: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 168, 'alias': 'out_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 188: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}], 'order': 169, 'alias': 'in_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 189: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 170, 'alias': 'in_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 190: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 171, 'alias': 'out_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 191: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 172, 'alias': 'out_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 192: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NOP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 173, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 193: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EXX', 'filter_out': False, '__type__': 'Terminal'}], 'order': 174, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 194: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CCF', 'filter_out': False, '__type__': 'Terminal'}], 'order': 175, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 195: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SCF', 'filter_out': False, '__type__': 'Terminal'}], 'order': 176, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 196: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LDIR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 177, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 197: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LDI', 'filter_out': False, '__type__': 'Terminal'}], 'order': 178, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 198: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LDDR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 179, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 199: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LDD', 'filter_out': False, '__type__': 'Terminal'}], 'order': 180, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 200: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CPIR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 181, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 201: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CPI', 'filter_out': False, '__type__': 'Terminal'}], 'order': 182, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 202: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CPDR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 183, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 203: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CPD', 'filter_out': False, '__type__': 'Terminal'}], 'order': 184, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 204: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DAA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 185, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 205: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NEG', 'filter_out': False, '__type__': 'Terminal'}], 'order': 186, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 206: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CPL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 187, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 207: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'HALT', 'filter_out': False, '__type__': 'Terminal'}], 'order': 188, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 208: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EI', 'filter_out': False, '__type__': 'Terminal'}], 'order': 189, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 209: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DI', 'filter_out': False, '__type__': 'Terminal'}], 'order': 190, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 210: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUTD', 'filter_out': False, '__type__': 'Terminal'}], 'order': 191, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 211: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUTI', 'filter_out': False, '__type__': 'Terminal'}], 'order': 192, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 212: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OTDR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 193, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 213: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OTIR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 194, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 214: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IND', 'filter_out': False, '__type__': 'Terminal'}], 'order': 195, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 215: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INI', 'filter_out': False, '__type__': 'Terminal'}], 'order': 196, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 216: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INDR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 197, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 217: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INIR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 198, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 218: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RET', 'filter_out': False, '__type__': 'Terminal'}], 'order': 199, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 219: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RETI', 'filter_out': False, '__type__': 'Terminal'}], 'order': 200, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 220: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RETN', 'filter_out': False, '__type__': 'Terminal'}], 'order': 201, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 221: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RLA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 202, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 222: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RLCA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 203, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 223: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RRA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 204, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 224: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RRCA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 205, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 225: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RLD', 'filter_out': False, '__type__': 'Terminal'}], 'order': 206, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 226: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RRD', 'filter_out': False, '__type__': 'Terminal'}], 'order': 207, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 227: {'origin': {'name': 'id_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'idlist', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 228: {'origin': {'name': 'id_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'id_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'idlist_id', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 229: {'origin': {'name': 'expr_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'expr_list_from_string', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 230: {'origin': {'name': 'expr_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'expr_list_from_num', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 231: {'origin': {'name': 'expr_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'expr_list_from_num', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 232: {'origin': {'name': 'expr_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'expr_list_plus_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 233: {'origin': {'name': 'expr_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'expr_list_plus_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 234: {'origin': {'name': 'expr_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'expr_list_plus_string', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 235: {'origin': {'name': 'number_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'number_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 236: {'origin': {'name': 'number_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'number_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 237: {'origin': {'name': 'number_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'number_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'number_list_number', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 238: {'origin': {'name': 'number_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'number_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'number_list_number', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 239: {'origin': {'name': 'reg8_hl', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'reg8_hl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 240: {'origin': {'name': 'reg8_hl', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'reg8_hl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 241: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 242: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IY', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 243: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 244: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 245: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IY', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 246: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IY', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 247: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 6, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 248: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IY', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 7, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 249: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 8, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 250: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 9, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 251: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IY', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 10, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 252: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IY', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 11, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 253: {'origin': {'name': 'bitwiseop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'bitwise', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 254: {'origin': {'name': 'bitwiseop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'AND', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'bitwise', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 255: {'origin': {'name': 'bitwiseop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'XOR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'bitwise', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 256: {'origin': {'name': 'bitwiseop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SUB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'bitwise', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 257: {'origin': {'name': 'bitwiseop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'bitwise', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 258: {'origin': {'name': 'bitop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BIT', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'bitop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 259: {'origin': {'name': 'bitop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RES', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'bitop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 260: {'origin': {'name': 'bitop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SET', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'bitop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 261: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 262: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 263: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RRC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 264: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RLC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 265: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 266: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 267: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SRA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 6, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 268: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SRL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 7, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 269: {'origin': {'name': 'inc_reg', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'reg_inc', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 270: {'origin': {'name': 'inc_reg', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'reg_inc', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 271: {'origin': {'name': 'inc_reg', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'reg16', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'reg_inc', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 272: {'origin': {'name': 'inc_reg', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'reg_inc', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 273: {'origin': {'name': 'inc_reg', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'reg_inc', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 274: {'origin': {'name': 'inc_reg', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 5, 'alias': 'reg_inc', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 275: {'origin': {'name': 'reg8', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'H', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'reg8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 276: {'origin': {'name': 'reg8', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'L', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'reg8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 277: {'origin': {'name': 'reg8', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'reg_bcde', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'reg8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 278: {'origin': {'name': 'reg_bcde', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'B', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'reg_bcde', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 279: {'origin': {'name': 'reg_bcde', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'C', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'reg_bcde', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 280: {'origin': {'name': 'reg_bcde', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'D', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'reg_bcde', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 281: {'origin': {'name': 'reg_bcde', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'E', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'reg_bcde', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 282: {'origin': {'name': 'reg8i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IXH', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'reg8i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 283: {'origin': {'name': 'reg8i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IXL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'reg8i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 284: {'origin': {'name': 'reg8i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IYH', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'reg8i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 285: {'origin': {'name': 'reg8i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IYL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'reg8i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 286: {'origin': {'name': 'reg16', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'reg16', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 287: {'origin': {'name': 'reg16', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'reg16', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 288: {'origin': {'name': 'reg16', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'reg16', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 289: {'origin': {'name': 'reg16', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'reg16i', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'reg16', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 290: {'origin': {'name': 'reg16i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IX', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'reg16i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 291: {'origin': {'name': 'reg16i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IY', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'reg16i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 292: {'origin': {'name': 'jp_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'P', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'jpflags_other', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 293: {'origin': {'name': 'jp_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'M', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'jpflags_other', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 294: {'origin': {'name': 'jp_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PO', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'jpflags_other', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 295: {'origin': {'name': 'jp_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'jpflags_other', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 296: {'origin': {'name': 'jp_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'jr_flags', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'jpflags_other', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 297: {'origin': {'name': 'jr_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'Z', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'jr_flags', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 298: {'origin': {'name': 'jr_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'C', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'jr_flags', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 299: {'origin': {'name': 'jr_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NZ', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'jr_flags', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 300: {'origin': {'name': 'jr_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'jr_flags', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 301: {'origin': {'name': 'mem_indir', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'mem_indir', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 302: {'origin': {'name': 'expr_bitwise_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bitwise', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 303: {'origin': {'name': 'expr_bitwise_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 304: {'origin': {'name': 'expr_add_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_add', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 305: {'origin': {'name': 'expr_add_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 306: {'origin': {'name': 'expr_mul_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_mul', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 307: {'origin': {'name': 'expr_mul_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 308: {'origin': {'name': 'expr_unary_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_unary', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 309: {'origin': {'name': 'expr_unary_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 310: {'origin': {'name': 'expr_pow_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_pow', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 311: {'origin': {'name': 'expr_pow_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 312: {'origin': {'name': 'expr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bitwise', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 313: {'origin': {'name': 'expr_bitwise', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bitwise_operand', '__type__': 'NonTerminal'}, {'name': 'RSHIFT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_add_operand', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 314: {'origin': {'name': 'expr_bitwise', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bitwise_operand', '__type__': 'NonTerminal'}, {'name': 'LSHIFT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_add_operand', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 315: {'origin': {'name': 'expr_bitwise', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bitwise_operand', '__type__': 'NonTerminal'}, {'name': 'BAND', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_add_operand', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 316: {'origin': {'name': 'expr_bitwise', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bitwise_operand', '__type__': 'NonTerminal'}, {'name': 'BOR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_add_operand', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 317: {'origin': {'name': 'expr_bitwise', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bitwise_operand', '__type__': 'NonTerminal'}, {'name': 'BXOR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_add_operand', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 318: {'origin': {'name': 'expr_bitwise', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_add', '__type__': 'NonTerminal'}], 'order': 5, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 319: {'origin': {'name': 'expr_add', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_add_operand', '__type__': 'NonTerminal'}, {'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_mul_operand', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 320: {'origin': {'name': 'expr_add', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_add_operand', '__type__': 'NonTerminal'}, {'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_mul_operand', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 321: {'origin': {'name': 'expr_add', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_mul', '__type__': 'NonTerminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 322: {'origin': {'name': 'expr_mul', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_mul_operand', '__type__': 'NonTerminal'}, {'name': 'MUL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_pow_operand', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 323: {'origin': {'name': 'expr_mul', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_mul_operand', '__type__': 'NonTerminal'}, {'name': 'DIV', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_pow_operand', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 324: {'origin': {'name': 'expr_mul', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_mul_operand', '__type__': 'NonTerminal'}, {'name': 'MOD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_pow_operand', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 325: {'origin': {'name': 'expr_mul', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_pow', '__type__': 'NonTerminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 326: {'origin': {'name': 'expr_pow', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_unary_operand', '__type__': 'NonTerminal'}, {'name': 'POW', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_pow_operand', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 327: {'origin': {'name': 'expr_pow', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_unary', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 328: {'origin': {'name': 'expr_unary', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_unary', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_uminus', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 329: {'origin': {'name': 'expr_unary', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_unary', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'expr_uplus', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 330: {'origin': {'name': 'expr_unary', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'expr_uminus', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 331: {'origin': {'name': 'expr_unary', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'expr_uplus', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 332: {'origin': {'name': 'expr_unary', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_atom', '__type__': 'NonTerminal'}], 'order': 4, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 333: {'origin': {'name': 'pexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'expr_lprp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 334: {'origin': {'name': 'pexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'expr_lprp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 335: {'origin': {'name': 'expr_atom', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INTEGER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'expr_int', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 336: {'origin': {'name': 'expr_atom', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'expr_label', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 337: {'origin': {'name': 'expr_atom', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LPP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RPP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'expr_paren', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 338: {'origin': {'name': 'expr_atom', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADDR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'expr_addr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}} +) +Shift = 0 +Reduce = 1 +def Lark_StandAlone(**kwargs): + return Lark._load_from_dict(DATA, MEMO, **kwargs) diff --git a/src/zxbasm/asmparse_zxnext_standalone.py b/src/zxbasm/asmparse_zxnext_standalone.py new file mode 100644 index 000000000..c3210de5b --- /dev/null +++ b/src/zxbasm/asmparse_zxnext_standalone.py @@ -0,0 +1,3574 @@ +# The file was automatically generated by Lark v1.3.1 +__version__ = "1.3.1" + +# +# +# Lark Stand-alone Generator Tool +# ---------------------------------- +# Generates a stand-alone LALR(1) parser +# +# Git: https://github.com/erezsh/lark +# Author: Erez Shinan (erezshin@gmail.com) +# +# +# >>> LICENSE +# +# This tool and its generated code use a separate license from Lark, +# and are subject to the terms of the Mozilla Public License, v. 2.0. +# If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +# If you wish to purchase a commercial license for this tool and its +# generated code, you may contact me via email or otherwise. +# +# If MPL2 is incompatible with your free or open-source project, +# contact me and we'll work it out. +# +# + +from copy import deepcopy +from abc import ABC, abstractmethod +from types import ModuleType +from typing import ( + TypeVar, Generic, Type, Tuple, List, Dict, Iterator, Collection, Callable, Optional, FrozenSet, Any, + Union, Iterable, IO, TYPE_CHECKING, overload, Sequence, + Pattern as REPattern, ClassVar, Set, Mapping +) + + +class LarkError(Exception): + pass + + +class ConfigurationError(LarkError, ValueError): + pass + + +def assert_config(value, options: Collection, msg='Got %r, expected one of %s'): + if value not in options: + raise ConfigurationError(msg % (value, options)) + + +class GrammarError(LarkError): + pass + + +class ParseError(LarkError): + pass + + +class LexError(LarkError): + pass + +T = TypeVar('T') + +class UnexpectedInput(LarkError): + #-- + line: int + column: int + pos_in_stream = None + state: Any + _terminals_by_name = None + interactive_parser: 'InteractiveParser' + + def get_context(self, text: str, span: int=40) -> str: + #-- + pos = self.pos_in_stream or 0 + start = max(pos - span, 0) + end = pos + span + if not isinstance(text, bytes): + before = text[start:pos].rsplit('\n', 1)[-1] + after = text[pos:end].split('\n', 1)[0] + return before + after + '\n' + ' ' * len(before.expandtabs()) + '^\n' + else: + before = text[start:pos].rsplit(b'\n', 1)[-1] + after = text[pos:end].split(b'\n', 1)[0] + return (before + after + b'\n' + b' ' * len(before.expandtabs()) + b'^\n').decode("ascii", "backslashreplace") + + def match_examples(self, parse_fn: 'Callable[[str], Tree]', + examples: Union[Mapping[T, Iterable[str]], Iterable[Tuple[T, Iterable[str]]]], + token_type_match_fallback: bool=False, + use_accepts: bool=True + ) -> Optional[T]: + #-- + assert self.state is not None, "Not supported for this exception" + + if isinstance(examples, Mapping): + examples = examples.items() + + candidate = (None, False) + for i, (label, example) in enumerate(examples): + assert not isinstance(example, str), "Expecting a list" + + for j, malformed in enumerate(example): + try: + parse_fn(malformed) + except UnexpectedInput as ut: + if ut.state == self.state: + if ( + use_accepts + and isinstance(self, UnexpectedToken) + and isinstance(ut, UnexpectedToken) + and ut.accepts != self.accepts + ): + logger.debug("Different accepts with same state[%d]: %s != %s at example [%s][%s]" % + (self.state, self.accepts, ut.accepts, i, j)) + continue + if ( + isinstance(self, (UnexpectedToken, UnexpectedEOF)) + and isinstance(ut, (UnexpectedToken, UnexpectedEOF)) + ): + if ut.token == self.token: ## + + logger.debug("Exact Match at example [%s][%s]" % (i, j)) + return label + + if token_type_match_fallback: + ## + + if (ut.token.type == self.token.type) and not candidate[-1]: + logger.debug("Token Type Fallback at example [%s][%s]" % (i, j)) + candidate = label, True + + if candidate[0] is None: + logger.debug("Same State match at example [%s][%s]" % (i, j)) + candidate = label, False + + return candidate[0] + + def _format_expected(self, expected): + if self._terminals_by_name: + d = self._terminals_by_name + expected = [d[t_name].user_repr() if t_name in d else t_name for t_name in expected] + return "Expected one of: \n\t* %s\n" % '\n\t* '.join(expected) + + +class UnexpectedEOF(ParseError, UnexpectedInput): + #-- + expected: 'List[Token]' + + def __init__(self, expected, state=None, terminals_by_name=None): + super(UnexpectedEOF, self).__init__() + + self.expected = expected + self.state = state + from .lexer import Token + self.token = Token("", "") ## + + self.pos_in_stream = -1 + self.line = -1 + self.column = -1 + self._terminals_by_name = terminals_by_name + + + def __str__(self): + message = "Unexpected end-of-input. " + message += self._format_expected(self.expected) + return message + + +class UnexpectedCharacters(LexError, UnexpectedInput): + #-- + + allowed: Set[str] + considered_tokens: Set[Any] + + def __init__(self, seq, lex_pos, line, column, allowed=None, considered_tokens=None, state=None, token_history=None, + terminals_by_name=None, considered_rules=None): + super(UnexpectedCharacters, self).__init__() + + ## + + self.line = line + self.column = column + self.pos_in_stream = lex_pos + self.state = state + self._terminals_by_name = terminals_by_name + + self.allowed = allowed + self.considered_tokens = considered_tokens + self.considered_rules = considered_rules + self.token_history = token_history + + if isinstance(seq, bytes): + self.char = seq[lex_pos:lex_pos + 1].decode("ascii", "backslashreplace") + else: + self.char = seq[lex_pos] + self._context = self.get_context(seq) + + + def __str__(self): + message = "No terminal matches '%s' in the current parser context, at line %d col %d" % (self.char, self.line, self.column) + message += '\n\n' + self._context + if self.allowed: + message += self._format_expected(self.allowed) + if self.token_history: + message += '\nPrevious tokens: %s\n' % ', '.join(repr(t) for t in self.token_history) + return message + + +class UnexpectedToken(ParseError, UnexpectedInput): + #-- + + expected: Set[str] + considered_rules: Set[str] + + def __init__(self, token, expected, considered_rules=None, state=None, interactive_parser=None, terminals_by_name=None, token_history=None): + super(UnexpectedToken, self).__init__() + + ## + + self.line = getattr(token, 'line', '?') + self.column = getattr(token, 'column', '?') + self.pos_in_stream = getattr(token, 'start_pos', None) + self.state = state + + self.token = token + self.expected = expected ## + + self._accepts = NO_VALUE + self.considered_rules = considered_rules + self.interactive_parser = interactive_parser + self._terminals_by_name = terminals_by_name + self.token_history = token_history + + + @property + def accepts(self) -> Set[str]: + if self._accepts is NO_VALUE: + self._accepts = self.interactive_parser and self.interactive_parser.accepts() + return self._accepts + + def __str__(self): + message = ("Unexpected token %r at line %s, column %s.\n%s" + % (self.token, self.line, self.column, self._format_expected(self.accepts or self.expected))) + if self.token_history: + message += "Previous tokens: %r\n" % self.token_history + + return message + + + +class VisitError(LarkError): + #-- + + obj: 'Union[Tree, Token]' + orig_exc: Exception + + def __init__(self, rule, obj, orig_exc): + message = 'Error trying to process rule "%s":\n\n%s' % (rule, orig_exc) + super(VisitError, self).__init__(message) + + self.rule = rule + self.obj = obj + self.orig_exc = orig_exc + + +class MissingVariableError(LarkError): + pass + + +import sys, re +import logging +from dataclasses import dataclass +from typing import Generic, AnyStr + +logger: logging.Logger = logging.getLogger("lark") +logger.addHandler(logging.StreamHandler()) +## + +## + +logger.setLevel(logging.CRITICAL) + + +NO_VALUE = object() + +T = TypeVar("T") + + +def classify(seq: Iterable, key: Optional[Callable] = None, value: Optional[Callable] = None) -> Dict: + d: Dict[Any, Any] = {} + for item in seq: + k = key(item) if (key is not None) else item + v = value(item) if (value is not None) else item + try: + d[k].append(v) + except KeyError: + d[k] = [v] + return d + + +def _deserialize(data: Any, namespace: Dict[str, Any], memo: Dict) -> Any: + if isinstance(data, dict): + if '__type__' in data: ## + + class_ = namespace[data['__type__']] + return class_.deserialize(data, memo) + elif '@' in data: + return memo[data['@']] + return {key:_deserialize(value, namespace, memo) for key, value in data.items()} + elif isinstance(data, list): + return [_deserialize(value, namespace, memo) for value in data] + return data + + +_T = TypeVar("_T", bound="Serialize") + +class Serialize: + #-- + + def memo_serialize(self, types_to_memoize: List) -> Any: + memo = SerializeMemoizer(types_to_memoize) + return self.serialize(memo), memo.serialize() + + def serialize(self, memo = None) -> Dict[str, Any]: + if memo and memo.in_types(self): + return {'@': memo.memoized.get(self)} + + fields = getattr(self, '__serialize_fields__') + res = {f: _serialize(getattr(self, f), memo) for f in fields} + res['__type__'] = type(self).__name__ + if hasattr(self, '_serialize'): + self._serialize(res, memo) + return res + + @classmethod + def deserialize(cls: Type[_T], data: Dict[str, Any], memo: Dict[int, Any]) -> _T: + namespace = getattr(cls, '__serialize_namespace__', []) + namespace = {c.__name__:c for c in namespace} + + fields = getattr(cls, '__serialize_fields__') + + if '@' in data: + return memo[data['@']] + + inst = cls.__new__(cls) + for f in fields: + try: + setattr(inst, f, _deserialize(data[f], namespace, memo)) + except KeyError as e: + raise KeyError("Cannot find key for class", cls, e) + + if hasattr(inst, '_deserialize'): + inst._deserialize() + + return inst + + +class SerializeMemoizer(Serialize): + #-- + + __serialize_fields__ = 'memoized', + + def __init__(self, types_to_memoize: List) -> None: + self.types_to_memoize = tuple(types_to_memoize) + self.memoized = Enumerator() + + def in_types(self, value: Serialize) -> bool: + return isinstance(value, self.types_to_memoize) + + def serialize(self) -> Dict[int, Any]: ## + + return _serialize(self.memoized.reversed(), None) + + @classmethod + def deserialize(cls, data: Dict[int, Any], namespace: Dict[str, Any], memo: Dict[Any, Any]) -> Dict[int, Any]: ## + + return _deserialize(data, namespace, memo) + + +try: + import regex + _has_regex = True +except ImportError: + _has_regex = False + +if sys.version_info >= (3, 11): + import re._parser as sre_parse + import re._constants as sre_constants +else: + import sre_parse + import sre_constants + +categ_pattern = re.compile(r'\\p{[A-Za-z_]+}') + +def get_regexp_width(expr: str) -> Union[Tuple[int, int], List[int]]: + if _has_regex: + ## + + ## + + ## + + regexp_final = re.sub(categ_pattern, 'A', expr) + else: + if re.search(categ_pattern, expr): + raise ImportError('`regex` module must be installed in order to use Unicode categories.', expr) + regexp_final = expr + try: + ## + + return [int(x) for x in sre_parse.parse(regexp_final).getwidth()] + except sre_constants.error: + if not _has_regex: + raise ValueError(expr) + else: + ## + + ## + + c = regex.compile(regexp_final) + ## + + ## + + MAXWIDTH = getattr(sre_parse, "MAXWIDTH", sre_constants.MAXREPEAT) + if c.match('') is None: + ## + + return 1, int(MAXWIDTH) + else: + return 0, int(MAXWIDTH) + + +@dataclass(frozen=True) +class TextSlice(Generic[AnyStr]): + #-- + text: AnyStr + start: int + end: int + + def __post_init__(self): + if not isinstance(self.text, (str, bytes)): + raise TypeError("text must be str or bytes") + + if self.start < 0: + object.__setattr__(self, 'start', self.start + len(self.text)) + assert self.start >=0 + + if self.end is None: + object.__setattr__(self, 'end', len(self.text)) + elif self.end < 0: + object.__setattr__(self, 'end', self.end + len(self.text)) + assert self.end <= len(self.text) + + @classmethod + def cast_from(cls, text: 'TextOrSlice') -> 'TextSlice[AnyStr]': + if isinstance(text, TextSlice): + return text + + return cls(text, 0, len(text)) + + def is_complete_text(self): + return self.start == 0 and self.end == len(self.text) + + def __len__(self): + return self.end - self.start + + def count(self, substr: AnyStr): + return self.text.count(substr, self.start, self.end) + + def rindex(self, substr: AnyStr): + return self.text.rindex(substr, self.start, self.end) + + +TextOrSlice = Union[AnyStr, 'TextSlice[AnyStr]'] +LarkInput = Union[AnyStr, TextSlice[AnyStr], Any] + + + +class Meta: + + empty: bool + line: int + column: int + start_pos: int + end_line: int + end_column: int + end_pos: int + orig_expansion: 'List[TerminalDef]' + match_tree: bool + + def __init__(self): + self.empty = True + + +_Leaf_T = TypeVar("_Leaf_T") +Branch = Union[_Leaf_T, 'Tree[_Leaf_T]'] + + +class Tree(Generic[_Leaf_T]): + #-- + + data: str + children: 'List[Branch[_Leaf_T]]' + + def __init__(self, data: str, children: 'List[Branch[_Leaf_T]]', meta: Optional[Meta]=None) -> None: + self.data = data + self.children = children + self._meta = meta + + @property + def meta(self) -> Meta: + if self._meta is None: + self._meta = Meta() + return self._meta + + def __repr__(self): + return 'Tree(%r, %r)' % (self.data, self.children) + + __match_args__ = ("data", "children") + + def _pretty_label(self): + return self.data + + def _pretty(self, level, indent_str): + yield f'{indent_str*level}{self._pretty_label()}' + if len(self.children) == 1 and not isinstance(self.children[0], Tree): + yield f'\t{self.children[0]}\n' + else: + yield '\n' + for n in self.children: + if isinstance(n, Tree): + yield from n._pretty(level+1, indent_str) + else: + yield f'{indent_str*(level+1)}{n}\n' + + def pretty(self, indent_str: str=' ') -> str: + #-- + return ''.join(self._pretty(0, indent_str)) + + def __rich__(self, parent:Optional['rich.tree.Tree']=None) -> 'rich.tree.Tree': + #-- + return self._rich(parent) + + def _rich(self, parent): + if parent: + tree = parent.add(f'[bold]{self.data}[/bold]') + else: + import rich.tree + tree = rich.tree.Tree(self.data) + + for c in self.children: + if isinstance(c, Tree): + c._rich(tree) + else: + tree.add(f'[green]{c}[/green]') + + return tree + + def __eq__(self, other): + try: + return self.data == other.data and self.children == other.children + except AttributeError: + return False + + def __ne__(self, other): + return not (self == other) + + def __hash__(self) -> int: + return hash((self.data, tuple(self.children))) + + def iter_subtrees(self) -> 'Iterator[Tree[_Leaf_T]]': + #-- + queue = [self] + subtrees = dict() + for subtree in queue: + subtrees[id(subtree)] = subtree + queue += [c for c in reversed(subtree.children) + if isinstance(c, Tree) and id(c) not in subtrees] + + del queue + return reversed(list(subtrees.values())) + + def iter_subtrees_topdown(self): + #-- + stack = [self] + stack_append = stack.append + stack_pop = stack.pop + while stack: + node = stack_pop() + if not isinstance(node, Tree): + continue + yield node + for child in reversed(node.children): + stack_append(child) + + def find_pred(self, pred: 'Callable[[Tree[_Leaf_T]], bool]') -> 'Iterator[Tree[_Leaf_T]]': + #-- + return filter(pred, self.iter_subtrees()) + + def find_data(self, data: str) -> 'Iterator[Tree[_Leaf_T]]': + #-- + return self.find_pred(lambda t: t.data == data) + + +from functools import wraps, update_wrapper +from inspect import getmembers, getmro + +_Return_T = TypeVar('_Return_T') +_Return_V = TypeVar('_Return_V') +_Leaf_T = TypeVar('_Leaf_T') +_Leaf_U = TypeVar('_Leaf_U') +_R = TypeVar('_R') +_FUNC = Callable[..., _Return_T] +_DECORATED = Union[_FUNC, type] + +class _DiscardType: + #-- + + def __repr__(self): + return "lark.visitors.Discard" + +Discard = _DiscardType() + +## + + +class _Decoratable: + #-- + + @classmethod + def _apply_v_args(cls, visit_wrapper): + mro = getmro(cls) + assert mro[0] is cls + libmembers = {name for _cls in mro[1:] for name, _ in getmembers(_cls)} + for name, value in getmembers(cls): + + ## + + if name.startswith('_') or (name in libmembers and name not in cls.__dict__): + continue + if not callable(value): + continue + + ## + + if isinstance(cls.__dict__[name], _VArgsWrapper): + continue + + setattr(cls, name, _VArgsWrapper(cls.__dict__[name], visit_wrapper)) + return cls + + def __class_getitem__(cls, _): + return cls + + +class Transformer(_Decoratable, ABC, Generic[_Leaf_T, _Return_T]): + #-- + __visit_tokens__ = True ## + + + def __init__(self, visit_tokens: bool=True) -> None: + self.__visit_tokens__ = visit_tokens + + def _call_userfunc(self, tree, new_children=None): + ## + + children = new_children if new_children is not None else tree.children + try: + f = getattr(self, tree.data) + except AttributeError: + return self.__default__(tree.data, children, tree.meta) + else: + try: + wrapper = getattr(f, 'visit_wrapper', None) + if wrapper is not None: + return f.visit_wrapper(f, tree.data, children, tree.meta) + else: + return f(children) + except GrammarError: + raise + except Exception as e: + raise VisitError(tree.data, tree, e) + + def _call_userfunc_token(self, token): + try: + f = getattr(self, token.type) + except AttributeError: + return self.__default_token__(token) + else: + try: + return f(token) + except GrammarError: + raise + except Exception as e: + raise VisitError(token.type, token, e) + + def _transform_children(self, children): + for c in children: + if isinstance(c, Tree): + res = self._transform_tree(c) + elif self.__visit_tokens__ and isinstance(c, Token): + res = self._call_userfunc_token(c) + else: + res = c + + if res is not Discard: + yield res + + def _transform_tree(self, tree): + children = list(self._transform_children(tree.children)) + return self._call_userfunc(tree, children) + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + #-- + res = list(self._transform_children([tree])) + if not res: + return None ## + + assert len(res) == 1 + return res[0] + + def __mul__( + self: 'Transformer[_Leaf_T, Tree[_Leaf_U]]', + other: 'Union[Transformer[_Leaf_U, _Return_V], TransformerChain[_Leaf_U, _Return_V,]]' + ) -> 'TransformerChain[_Leaf_T, _Return_V]': + #-- + return TransformerChain(self, other) + + def __default__(self, data, children, meta): + #-- + return Tree(data, children, meta) + + def __default_token__(self, token): + #-- + return token + + +def merge_transformers(base_transformer=None, **transformers_to_merge): + #-- + if base_transformer is None: + base_transformer = Transformer() + for prefix, transformer in transformers_to_merge.items(): + for method_name in dir(transformer): + method = getattr(transformer, method_name) + if not callable(method): + continue + if method_name.startswith("_") or method_name == "transform": + continue + prefixed_method = prefix + "__" + method_name + if hasattr(base_transformer, prefixed_method): + raise AttributeError("Cannot merge: method '%s' appears more than once" % prefixed_method) + + setattr(base_transformer, prefixed_method, method) + + return base_transformer + + +class InlineTransformer(Transformer): ## + + def _call_userfunc(self, tree, new_children=None): + ## + + children = new_children if new_children is not None else tree.children + try: + f = getattr(self, tree.data) + except AttributeError: + return self.__default__(tree.data, children, tree.meta) + else: + return f(*children) + + +class TransformerChain(Generic[_Leaf_T, _Return_T]): + + transformers: 'Tuple[Union[Transformer, TransformerChain], ...]' + + def __init__(self, *transformers: 'Union[Transformer, TransformerChain]') -> None: + self.transformers = transformers + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + for t in self.transformers: + tree = t.transform(tree) + return cast(_Return_T, tree) + + def __mul__( + self: 'TransformerChain[_Leaf_T, Tree[_Leaf_U]]', + other: 'Union[Transformer[_Leaf_U, _Return_V], TransformerChain[_Leaf_U, _Return_V]]' + ) -> 'TransformerChain[_Leaf_T, _Return_V]': + return TransformerChain(*self.transformers + (other,)) + + +class Transformer_InPlace(Transformer[_Leaf_T, _Return_T]): + #-- + def _transform_tree(self, tree): ## + + return self._call_userfunc(tree) + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + for subtree in tree.iter_subtrees(): + subtree.children = list(self._transform_children(subtree.children)) + + return self._transform_tree(tree) + + +class Transformer_NonRecursive(Transformer[_Leaf_T, _Return_T]): + #-- + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + ## + + rev_postfix = [] + q: List[Branch[_Leaf_T]] = [tree] + while q: + t = q.pop() + rev_postfix.append(t) + if isinstance(t, Tree): + q += t.children + + ## + + stack: List = [] + for x in reversed(rev_postfix): + if isinstance(x, Tree): + size = len(x.children) + if size: + args = stack[-size:] + del stack[-size:] + else: + args = [] + + res = self._call_userfunc(x, args) + if res is not Discard: + stack.append(res) + + elif self.__visit_tokens__ and isinstance(x, Token): + res = self._call_userfunc_token(x) + if res is not Discard: + stack.append(res) + else: + stack.append(x) + + result, = stack ## + + ## + + ## + + ## + + return cast(_Return_T, result) + + +class Transformer_InPlaceRecursive(Transformer[_Leaf_T, _Return_T]): + #-- + def _transform_tree(self, tree): + tree.children = list(self._transform_children(tree.children)) + return self._call_userfunc(tree) + + +## + + +class VisitorBase: + def _call_userfunc(self, tree): + return getattr(self, tree.data, self.__default__)(tree) + + def __default__(self, tree): + #-- + return tree + + def __class_getitem__(cls, _): + return cls + + +class Visitor(VisitorBase, ABC, Generic[_Leaf_T]): + #-- + + def visit(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + for subtree in tree.iter_subtrees(): + self._call_userfunc(subtree) + return tree + + def visit_topdown(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + for subtree in tree.iter_subtrees_topdown(): + self._call_userfunc(subtree) + return tree + + +class Visitor_Recursive(VisitorBase, Generic[_Leaf_T]): + #-- + + def visit(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + for child in tree.children: + if isinstance(child, Tree): + self.visit(child) + + self._call_userfunc(tree) + return tree + + def visit_topdown(self,tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + self._call_userfunc(tree) + + for child in tree.children: + if isinstance(child, Tree): + self.visit_topdown(child) + + return tree + + +class Interpreter(_Decoratable, ABC, Generic[_Leaf_T, _Return_T]): + #-- + + def visit(self, tree: Tree[_Leaf_T]) -> _Return_T: + ## + + ## + + ## + + return self._visit_tree(tree) + + def _visit_tree(self, tree: Tree[_Leaf_T]): + f = getattr(self, tree.data) + wrapper = getattr(f, 'visit_wrapper', None) + if wrapper is not None: + return f.visit_wrapper(f, tree.data, tree.children, tree.meta) + else: + return f(tree) + + def visit_children(self, tree: Tree[_Leaf_T]) -> List: + return [self._visit_tree(child) if isinstance(child, Tree) else child + for child in tree.children] + + def __getattr__(self, name): + return self.__default__ + + def __default__(self, tree): + return self.visit_children(tree) + + +_InterMethod = Callable[[Type[Interpreter], _Return_T], _R] + +def visit_children_decor(func: _InterMethod) -> _InterMethod: + #-- + @wraps(func) + def inner(cls, tree): + values = cls.visit_children(tree) + return func(cls, values) + return inner + +## + + +def _apply_v_args(obj, visit_wrapper): + try: + _apply = obj._apply_v_args + except AttributeError: + return _VArgsWrapper(obj, visit_wrapper) + else: + return _apply(visit_wrapper) + + +class _VArgsWrapper: + #-- + base_func: Callable + + def __init__(self, func: Callable, visit_wrapper: Callable[[Callable, str, list, Any], Any]): + if isinstance(func, _VArgsWrapper): + func = func.base_func + self.base_func = func + self.visit_wrapper = visit_wrapper + update_wrapper(self, func) + + def __call__(self, *args, **kwargs): + return self.base_func(*args, **kwargs) + + def __get__(self, instance, owner=None): + try: + ## + + ## + + g = type(self.base_func).__get__ + except AttributeError: + return self + else: + return _VArgsWrapper(g(self.base_func, instance, owner), self.visit_wrapper) + + def __set_name__(self, owner, name): + try: + f = type(self.base_func).__set_name__ + except AttributeError: + return + else: + f(self.base_func, owner, name) + + +def _vargs_inline(f, _data, children, _meta): + return f(*children) +def _vargs_meta_inline(f, _data, children, meta): + return f(meta, *children) +def _vargs_meta(f, _data, children, meta): + return f(meta, children) +def _vargs_tree(f, data, children, meta): + return f(Tree(data, children, meta)) + + +def v_args(inline: bool = False, meta: bool = False, tree: bool = False, wrapper: Optional[Callable] = None) -> Callable[[_DECORATED], _DECORATED]: + #-- + if tree and (meta or inline): + raise ValueError("Visitor functions cannot combine 'tree' with 'meta' or 'inline'.") + + func = None + if meta: + if inline: + func = _vargs_meta_inline + else: + func = _vargs_meta + elif inline: + func = _vargs_inline + elif tree: + func = _vargs_tree + + if wrapper is not None: + if func is not None: + raise ValueError("Cannot use 'wrapper' along with 'tree', 'meta' or 'inline'.") + func = wrapper + + def _visitor_args_dec(obj): + return _apply_v_args(obj, func) + return _visitor_args_dec + + + +TOKEN_DEFAULT_PRIORITY = 0 + + +class Symbol(Serialize): + __slots__ = ('name',) + + name: str + is_term: ClassVar[bool] = NotImplemented + + def __init__(self, name: str) -> None: + self.name = name + + def __eq__(self, other): + if not isinstance(other, Symbol): + return NotImplemented + return self.is_term == other.is_term and self.name == other.name + + def __ne__(self, other): + return not (self == other) + + def __hash__(self): + return hash(self.name) + + def __repr__(self): + return '%s(%r)' % (type(self).__name__, self.name) + + fullrepr = property(__repr__) + + def renamed(self, f): + return type(self)(f(self.name)) + + +class Terminal(Symbol): + __serialize_fields__ = 'name', 'filter_out' + + is_term: ClassVar[bool] = True + + def __init__(self, name: str, filter_out: bool = False) -> None: + self.name = name + self.filter_out = filter_out + + @property + def fullrepr(self): + return '%s(%r, %r)' % (type(self).__name__, self.name, self.filter_out) + + def renamed(self, f): + return type(self)(f(self.name), self.filter_out) + + +class NonTerminal(Symbol): + __serialize_fields__ = 'name', + + is_term: ClassVar[bool] = False + + def serialize(self, memo=None) -> Dict[str, Any]: + ## + + ## + + return {'name': str(self.name), '__type__': 'NonTerminal'} + + +class RuleOptions(Serialize): + __serialize_fields__ = 'keep_all_tokens', 'expand1', 'priority', 'template_source', 'empty_indices' + + keep_all_tokens: bool + expand1: bool + priority: Optional[int] + template_source: Optional[str] + empty_indices: Tuple[bool, ...] + + def __init__(self, keep_all_tokens: bool=False, expand1: bool=False, priority: Optional[int]=None, template_source: Optional[str]=None, empty_indices: Tuple[bool, ...]=()) -> None: + self.keep_all_tokens = keep_all_tokens + self.expand1 = expand1 + self.priority = priority + self.template_source = template_source + self.empty_indices = empty_indices + + def __repr__(self): + return 'RuleOptions(%r, %r, %r, %r)' % ( + self.keep_all_tokens, + self.expand1, + self.priority, + self.template_source + ) + + +class Rule(Serialize): + #-- + __slots__ = ('origin', 'expansion', 'alias', 'options', 'order', '_hash') + + __serialize_fields__ = 'origin', 'expansion', 'order', 'alias', 'options' + __serialize_namespace__ = Terminal, NonTerminal, RuleOptions + + origin: NonTerminal + expansion: Sequence[Symbol] + order: int + alias: Optional[str] + options: RuleOptions + _hash: int + + def __init__(self, origin: NonTerminal, expansion: Sequence[Symbol], + order: int=0, alias: Optional[str]=None, options: Optional[RuleOptions]=None): + self.origin = origin + self.expansion = expansion + self.alias = alias + self.order = order + self.options = options or RuleOptions() + self._hash = hash((self.origin, tuple(self.expansion))) + + def _deserialize(self): + self._hash = hash((self.origin, tuple(self.expansion))) + + def __str__(self): + return '<%s : %s>' % (self.origin.name, ' '.join(x.name for x in self.expansion)) + + def __repr__(self): + return 'Rule(%r, %r, %r, %r)' % (self.origin, self.expansion, self.alias, self.options) + + def __hash__(self): + return self._hash + + def __eq__(self, other): + if not isinstance(other, Rule): + return False + return self.origin == other.origin and self.expansion == other.expansion + + + +from contextlib import suppress +from copy import copy + +try: ## + + has_interegular = bool(interegular) +except NameError: + has_interegular = False + +class Pattern(Serialize, ABC): + #-- + + value: str + flags: Collection[str] + raw: Optional[str] + type: ClassVar[str] + + def __init__(self, value: str, flags: Collection[str] = (), raw: Optional[str] = None) -> None: + self.value = value + self.flags = frozenset(flags) + self.raw = raw + + def __repr__(self): + return repr(self.to_regexp()) + + ## + + def __hash__(self): + return hash((type(self), self.value, self.flags)) + + def __eq__(self, other): + return type(self) == type(other) and self.value == other.value and self.flags == other.flags + + @abstractmethod + def to_regexp(self) -> str: + raise NotImplementedError() + + @property + @abstractmethod + def min_width(self) -> int: + raise NotImplementedError() + + @property + @abstractmethod + def max_width(self) -> int: + raise NotImplementedError() + + def _get_flags(self, value): + for f in self.flags: + value = ('(?%s:%s)' % (f, value)) + return value + + +class PatternStr(Pattern): + __serialize_fields__ = 'value', 'flags', 'raw' + + type: ClassVar[str] = "str" + + def to_regexp(self) -> str: + return self._get_flags(re.escape(self.value)) + + @property + def min_width(self) -> int: + return len(self.value) + + @property + def max_width(self) -> int: + return len(self.value) + + +class PatternRE(Pattern): + __serialize_fields__ = 'value', 'flags', 'raw', '_width' + + type: ClassVar[str] = "re" + + def to_regexp(self) -> str: + return self._get_flags(self.value) + + _width = None + def _get_width(self): + if self._width is None: + self._width = get_regexp_width(self.to_regexp()) + return self._width + + @property + def min_width(self) -> int: + return self._get_width()[0] + + @property + def max_width(self) -> int: + return self._get_width()[1] + + +class TerminalDef(Serialize): + #-- + __serialize_fields__ = 'name', 'pattern', 'priority' + __serialize_namespace__ = PatternStr, PatternRE + + name: str + pattern: Pattern + priority: int + + def __init__(self, name: str, pattern: Pattern, priority: int = TOKEN_DEFAULT_PRIORITY) -> None: + assert isinstance(pattern, Pattern), pattern + self.name = name + self.pattern = pattern + self.priority = priority + + def __repr__(self): + return '%s(%r, %r)' % (type(self).__name__, self.name, self.pattern) + + def user_repr(self) -> str: + if self.name.startswith('__'): ## + + return self.pattern.raw or self.name + else: + return self.name + +_T = TypeVar('_T', bound="Token") + +class Token(str): + #-- + __slots__ = ('type', 'start_pos', 'value', 'line', 'column', 'end_line', 'end_column', 'end_pos') + + __match_args__ = ('type', 'value') + + type: str + start_pos: Optional[int] + value: Any + line: Optional[int] + column: Optional[int] + end_line: Optional[int] + end_column: Optional[int] + end_pos: Optional[int] + + + @overload + def __new__( + cls, + type: str, + value: Any, + start_pos: Optional[int] = None, + line: Optional[int] = None, + column: Optional[int] = None, + end_line: Optional[int] = None, + end_column: Optional[int] = None, + end_pos: Optional[int] = None + ) -> 'Token': + ... + + @overload + def __new__( + cls, + type_: str, + value: Any, + start_pos: Optional[int] = None, + line: Optional[int] = None, + column: Optional[int] = None, + end_line: Optional[int] = None, + end_column: Optional[int] = None, + end_pos: Optional[int] = None + ) -> 'Token': ... + + def __new__(cls, *args, **kwargs): + if "type_" in kwargs: + warnings.warn("`type_` is deprecated use `type` instead", DeprecationWarning) + + if "type" in kwargs: + raise TypeError("Error: using both 'type' and the deprecated 'type_' as arguments.") + kwargs["type"] = kwargs.pop("type_") + + return cls._future_new(*args, **kwargs) + + + @classmethod + def _future_new(cls, type, value, start_pos=None, line=None, column=None, end_line=None, end_column=None, end_pos=None): + inst = super(Token, cls).__new__(cls, value) + + inst.type = type + inst.start_pos = start_pos + inst.value = value + inst.line = line + inst.column = column + inst.end_line = end_line + inst.end_column = end_column + inst.end_pos = end_pos + return inst + + @overload + def update(self, type: Optional[str] = None, value: Optional[Any] = None) -> 'Token': + ... + + @overload + def update(self, type_: Optional[str] = None, value: Optional[Any] = None) -> 'Token': + ... + + def update(self, *args, **kwargs): + if "type_" in kwargs: + warnings.warn("`type_` is deprecated use `type` instead", DeprecationWarning) + + if "type" in kwargs: + raise TypeError("Error: using both 'type' and the deprecated 'type_' as arguments.") + kwargs["type"] = kwargs.pop("type_") + + return self._future_update(*args, **kwargs) + + def _future_update(self, type: Optional[str] = None, value: Optional[Any] = None) -> 'Token': + return Token.new_borrow_pos( + type if type is not None else self.type, + value if value is not None else self.value, + self + ) + + @classmethod + def new_borrow_pos(cls: Type[_T], type_: str, value: Any, borrow_t: 'Token') -> _T: + return cls(type_, value, borrow_t.start_pos, borrow_t.line, borrow_t.column, borrow_t.end_line, borrow_t.end_column, borrow_t.end_pos) + + def __reduce__(self): + return (self.__class__, (self.type, self.value, self.start_pos, self.line, self.column)) + + def __repr__(self): + return 'Token(%r, %r)' % (self.type, self.value) + + def __deepcopy__(self, memo): + return Token(self.type, self.value, self.start_pos, self.line, self.column) + + def __eq__(self, other): + if isinstance(other, Token) and self.type != other.type: + return False + + return str.__eq__(self, other) + + __hash__ = str.__hash__ + + +class LineCounter: + #-- + + __slots__ = 'char_pos', 'line', 'column', 'line_start_pos', 'newline_char' + + def __init__(self, newline_char): + self.newline_char = newline_char + self.char_pos = 0 + self.line = 1 + self.column = 1 + self.line_start_pos = 0 + + def __eq__(self, other): + if not isinstance(other, LineCounter): + return NotImplemented + + return self.char_pos == other.char_pos and self.newline_char == other.newline_char + + def feed(self, token: TextOrSlice, test_newline=True): + #-- + if test_newline: + newlines = token.count(self.newline_char) + if newlines: + self.line += newlines + self.line_start_pos = self.char_pos + token.rindex(self.newline_char) + 1 + + self.char_pos += len(token) + self.column = self.char_pos - self.line_start_pos + 1 + + +class UnlessCallback: + def __init__(self, scanner: 'Scanner'): + self.scanner = scanner + + def __call__(self, t: Token): + res = self.scanner.fullmatch(t.value) + if res is not None: + t.type = res + return t + + +class CallChain: + def __init__(self, callback1, callback2, cond): + self.callback1 = callback1 + self.callback2 = callback2 + self.cond = cond + + def __call__(self, t): + t2 = self.callback1(t) + return self.callback2(t) if self.cond(t2) else t2 + + +def _get_match(re_, regexp, s, flags): + m = re_.match(regexp, s, flags) + if m: + return m.group(0) + +def _create_unless(terminals, g_regex_flags, re_, use_bytes): + tokens_by_type = classify(terminals, lambda t: type(t.pattern)) + assert len(tokens_by_type) <= 2, tokens_by_type.keys() + embedded_strs = set() + callback = {} + for retok in tokens_by_type.get(PatternRE, []): + unless = [] + for strtok in tokens_by_type.get(PatternStr, []): + if strtok.priority != retok.priority: + continue + s = strtok.pattern.value + if s == _get_match(re_, retok.pattern.to_regexp(), s, g_regex_flags): + unless.append(strtok) + if strtok.pattern.flags <= retok.pattern.flags: + embedded_strs.add(strtok) + if unless: + callback[retok.name] = UnlessCallback(Scanner(unless, g_regex_flags, re_, use_bytes=use_bytes)) + + new_terminals = [t for t in terminals if t not in embedded_strs] + return new_terminals, callback + + +class Scanner: + def __init__(self, terminals, g_regex_flags, re_, use_bytes): + self.terminals = terminals + self.g_regex_flags = g_regex_flags + self.re_ = re_ + self.use_bytes = use_bytes + + self.allowed_types = {t.name for t in self.terminals} + + self._mres = self._build_mres(terminals, len(terminals)) + + def _build_mres(self, terminals, max_size): + ## + + ## + + ## + + mres = [] + while terminals: + pattern = u'|'.join(u'(?P<%s>%s)' % (t.name, t.pattern.to_regexp()) for t in terminals[:max_size]) + if self.use_bytes: + pattern = pattern.encode('latin-1') + try: + mre = self.re_.compile(pattern, self.g_regex_flags) + except AssertionError: ## + + return self._build_mres(terminals, max_size // 2) + + mres.append(mre) + terminals = terminals[max_size:] + return mres + + def match(self, text: TextSlice, pos): + for mre in self._mres: + m = mre.match(text.text, pos, text.end) + if m: + return m.group(0), m.lastgroup + + + def fullmatch(self, text: str) -> Optional[str]: + for mre in self._mres: + m = mre.fullmatch(text) + if m: + return m.lastgroup + return None + +def _regexp_has_newline(r: str): + #-- + return '\n' in r or '\\n' in r or '\\s' in r or '[^' in r or ('(?s' in r and '.' in r) + + +class LexerState: + #-- + + __slots__ = 'text', 'line_ctr', 'last_token' + + text: TextSlice + line_ctr: LineCounter + last_token: Optional[Token] + + def __init__(self, text: TextSlice, line_ctr: Optional[LineCounter] = None, last_token: Optional[Token]=None): + if isinstance(text, TextSlice): + if line_ctr is None: + line_ctr = LineCounter(b'\n' if isinstance(text.text, bytes) else '\n') + + if text.start > 0: + ## + + line_ctr.feed(TextSlice(text.text, 0, text.start)) + + if not (text.start <= line_ctr.char_pos <= text.end): + raise ValueError("LineCounter.char_pos is out of bounds") + + self.text = text + if line_ctr is None: + line_ctr = LineCounter(b'\n' if isinstance(text, bytes) or (hasattr(text, 'text') and isinstance(text.text, bytes)) else '\n') + self.line_ctr = line_ctr + self.last_token = last_token + + + def __eq__(self, other): + if not isinstance(other, LexerState): + return NotImplemented + + return self.text == other.text and self.line_ctr == other.line_ctr and self.last_token == other.last_token + + def __copy__(self): + return type(self)(self.text, copy(self.line_ctr), self.last_token) + + +class LexerThread: + #-- + + def __init__(self, lexer: 'Lexer', lexer_state: Optional[LexerState]): + self.lexer = lexer + self.state = lexer_state + + @classmethod + def from_text(cls, lexer: 'Lexer', text_or_slice: TextOrSlice) -> 'LexerThread': + text = TextSlice.cast_from(text_or_slice) + return cls(lexer, LexerState(text)) + + @classmethod + def from_custom_input(cls, lexer: 'Lexer', text: Any) -> 'LexerThread': + return cls(lexer, LexerState(text)) + + def lex(self, parser_state): + if self.state is None: + raise TypeError("Cannot lex: No text assigned to lexer state") + return self.lexer.lex(self.state, parser_state) + + def __copy__(self): + return type(self)(self.lexer, copy(self.state)) + + _Token = Token + + +_Callback = Callable[[Token], Token] + +class Lexer(ABC): + #-- + @abstractmethod + def lex(self, lexer_state: LexerState, parser_state: Any) -> Iterator[Token]: + return NotImplemented + + def make_lexer_state(self, text: str): + #-- + return LexerState(TextSlice.cast_from(text)) + + +def _check_regex_collisions(terminal_to_regexp: Dict[TerminalDef, str], comparator, strict_mode, max_collisions_to_show=8): + if not comparator: + comparator = interegular.Comparator.from_regexes(terminal_to_regexp) + + ## + + ## + + max_time = 2 if strict_mode else 0.2 + + ## + + if comparator.count_marked_pairs() >= max_collisions_to_show: + return + for group in classify(terminal_to_regexp, lambda t: t.priority).values(): + for a, b in comparator.check(group, skip_marked=True): + assert a.priority == b.priority + ## + + comparator.mark(a, b) + + ## + + message = f"Collision between Terminals {a.name} and {b.name}. " + try: + example = comparator.get_example_overlap(a, b, max_time).format_multiline() + except ValueError: + ## + + example = "No example could be found fast enough. However, the collision does still exists" + if strict_mode: + raise LexError(f"{message}\n{example}") + logger.warning("%s The lexer will choose between them arbitrarily.\n%s", message, example) + if comparator.count_marked_pairs() >= max_collisions_to_show: + logger.warning("Found 8 regex collisions, will not check for more.") + return + + +class AbstractBasicLexer(Lexer): + terminals_by_name: Dict[str, TerminalDef] + + @abstractmethod + def __init__(self, conf: 'LexerConf', comparator=None) -> None: + ... + + @abstractmethod + def next_token(self, lex_state: LexerState, parser_state: Any = None) -> Token: + ... + + def lex(self, state: LexerState, parser_state: Any) -> Iterator[Token]: + with suppress(EOFError): + while True: + yield self.next_token(state, parser_state) + + +class BasicLexer(AbstractBasicLexer): + terminals: Collection[TerminalDef] + ignore_types: FrozenSet[str] + newline_types: FrozenSet[str] + user_callbacks: Dict[str, _Callback] + callback: Dict[str, _Callback] + re: ModuleType + + def __init__(self, conf: 'LexerConf', comparator=None) -> None: + terminals = list(conf.terminals) + assert all(isinstance(t, TerminalDef) for t in terminals), terminals + + self.re = conf.re_module + + if not conf.skip_validation: + ## + + terminal_to_regexp = {} + for t in terminals: + regexp = t.pattern.to_regexp() + try: + self.re.compile(regexp, conf.g_regex_flags) + except self.re.error: + raise LexError("Cannot compile token %s: %s" % (t.name, t.pattern)) + + if t.pattern.min_width == 0: + raise LexError("Lexer does not allow zero-width terminals. (%s: %s)" % (t.name, t.pattern)) + if t.pattern.type == "re": + terminal_to_regexp[t] = regexp + + if not (set(conf.ignore) <= {t.name for t in terminals}): + raise LexError("Ignore terminals are not defined: %s" % (set(conf.ignore) - {t.name for t in terminals})) + + if has_interegular: + _check_regex_collisions(terminal_to_regexp, comparator, conf.strict) + elif conf.strict: + raise LexError("interegular must be installed for strict mode. Use `pip install 'lark[interegular]'`.") + + ## + + self.newline_types = frozenset(t.name for t in terminals if _regexp_has_newline(t.pattern.to_regexp())) + self.ignore_types = frozenset(conf.ignore) + + terminals.sort(key=lambda x: (-x.priority, -x.pattern.max_width, -len(x.pattern.value), x.name)) + self.terminals = terminals + self.user_callbacks = conf.callbacks + self.g_regex_flags = conf.g_regex_flags + self.use_bytes = conf.use_bytes + self.terminals_by_name = conf.terminals_by_name + + self._scanner: Optional[Scanner] = None + + def _build_scanner(self) -> Scanner: + terminals, self.callback = _create_unless(self.terminals, self.g_regex_flags, self.re, self.use_bytes) + assert all(self.callback.values()) + + for type_, f in self.user_callbacks.items(): + if type_ in self.callback: + ## + + self.callback[type_] = CallChain(self.callback[type_], f, lambda t: t.type == type_) + else: + self.callback[type_] = f + + return Scanner(terminals, self.g_regex_flags, self.re, self.use_bytes) + + @property + def scanner(self) -> Scanner: + if self._scanner is None: + self._scanner = self._build_scanner() + return self._scanner + + def match(self, text, pos): + return self.scanner.match(text, pos) + + def next_token(self, lex_state: LexerState, parser_state: Any = None) -> Token: + line_ctr = lex_state.line_ctr + while line_ctr.char_pos < lex_state.text.end: + res = self.match(lex_state.text, line_ctr.char_pos) + if not res: + allowed = self.scanner.allowed_types - self.ignore_types + if not allowed: + allowed = {""} + raise UnexpectedCharacters(lex_state.text.text, line_ctr.char_pos, line_ctr.line, line_ctr.column, + allowed=allowed, token_history=lex_state.last_token and [lex_state.last_token], + state=parser_state, terminals_by_name=self.terminals_by_name) + + value, type_ = res + + ignored = type_ in self.ignore_types + t = None + if not ignored or type_ in self.callback: + t = Token(type_, value, line_ctr.char_pos, line_ctr.line, line_ctr.column) + line_ctr.feed(value, type_ in self.newline_types) + if t is not None: + t.end_line = line_ctr.line + t.end_column = line_ctr.column + t.end_pos = line_ctr.char_pos + if t.type in self.callback: + t = self.callback[t.type](t) + if not ignored: + if not isinstance(t, Token): + raise LexError("Callbacks must return a token (returned %r)" % t) + lex_state.last_token = t + return t + + ## + + raise EOFError(self) + + +class ContextualLexer(Lexer): + lexers: Dict[int, AbstractBasicLexer] + root_lexer: AbstractBasicLexer + + BasicLexer: Type[AbstractBasicLexer] = BasicLexer + + def __init__(self, conf: 'LexerConf', states: Dict[int, Collection[str]], always_accept: Collection[str]=()) -> None: + terminals = list(conf.terminals) + terminals_by_name = conf.terminals_by_name + + trad_conf = copy(conf) + trad_conf.terminals = terminals + + if has_interegular and not conf.skip_validation: + comparator = interegular.Comparator.from_regexes({t: t.pattern.to_regexp() for t in terminals}) + else: + comparator = None + lexer_by_tokens: Dict[FrozenSet[str], AbstractBasicLexer] = {} + self.lexers = {} + for state, accepts in states.items(): + key = frozenset(accepts) + try: + lexer = lexer_by_tokens[key] + except KeyError: + accepts = set(accepts) | set(conf.ignore) | set(always_accept) + lexer_conf = copy(trad_conf) + lexer_conf.terminals = [terminals_by_name[n] for n in accepts if n in terminals_by_name] + lexer = self.BasicLexer(lexer_conf, comparator) + lexer_by_tokens[key] = lexer + + self.lexers[state] = lexer + + assert trad_conf.terminals is terminals + trad_conf.skip_validation = True ## + + self.root_lexer = self.BasicLexer(trad_conf, comparator) + + def lex(self, lexer_state: LexerState, parser_state: 'ParserState') -> Iterator[Token]: + try: + while True: + lexer = self.lexers[parser_state.position] + yield lexer.next_token(lexer_state, parser_state) + except EOFError: + pass + except UnexpectedCharacters as e: + ## + + ## + + try: + last_token = lexer_state.last_token ## + + token = self.root_lexer.next_token(lexer_state, parser_state) + raise UnexpectedToken(token, e.allowed, state=parser_state, token_history=[last_token], terminals_by_name=self.root_lexer.terminals_by_name) + except UnexpectedCharacters: + raise e ## + + + + +_ParserArgType: 'TypeAlias' = 'Literal["earley", "lalr", "cyk", "auto"]' +_LexerArgType: 'TypeAlias' = 'Union[Literal["auto", "basic", "contextual", "dynamic", "dynamic_complete"], Type[Lexer]]' +_LexerCallback = Callable[[Token], Token] +ParserCallbacks = Dict[str, Callable] + +class LexerConf(Serialize): + __serialize_fields__ = 'terminals', 'ignore', 'g_regex_flags', 'use_bytes', 'lexer_type' + __serialize_namespace__ = TerminalDef, + + terminals: Collection[TerminalDef] + re_module: ModuleType + ignore: Collection[str] + postlex: 'Optional[PostLex]' + callbacks: Dict[str, _LexerCallback] + g_regex_flags: int + skip_validation: bool + use_bytes: bool + lexer_type: Optional[_LexerArgType] + strict: bool + + def __init__(self, terminals: Collection[TerminalDef], re_module: ModuleType, ignore: Collection[str]=(), postlex: 'Optional[PostLex]'=None, + callbacks: Optional[Dict[str, _LexerCallback]]=None, g_regex_flags: int=0, skip_validation: bool=False, use_bytes: bool=False, strict: bool=False): + self.terminals = terminals + self.terminals_by_name = {t.name: t for t in self.terminals} + assert len(self.terminals) == len(self.terminals_by_name) + self.ignore = ignore + self.postlex = postlex + self.callbacks = callbacks or {} + self.g_regex_flags = g_regex_flags + self.re_module = re_module + self.skip_validation = skip_validation + self.use_bytes = use_bytes + self.strict = strict + self.lexer_type = None + + def _deserialize(self): + self.terminals_by_name = {t.name: t for t in self.terminals} + + def __deepcopy__(self, memo=None): + return type(self)( + deepcopy(self.terminals, memo), + self.re_module, + deepcopy(self.ignore, memo), + deepcopy(self.postlex, memo), + deepcopy(self.callbacks, memo), + deepcopy(self.g_regex_flags, memo), + deepcopy(self.skip_validation, memo), + deepcopy(self.use_bytes, memo), + ) + +class ParserConf(Serialize): + __serialize_fields__ = 'rules', 'start', 'parser_type' + + rules: List['Rule'] + callbacks: ParserCallbacks + start: List[str] + parser_type: _ParserArgType + + def __init__(self, rules: List['Rule'], callbacks: ParserCallbacks, start: List[str]): + assert isinstance(start, list) + self.rules = rules + self.callbacks = callbacks + self.start = start + + +from functools import partial, wraps +from itertools import product + + +class ExpandSingleChild: + def __init__(self, node_builder): + self.node_builder = node_builder + + def __call__(self, children): + if len(children) == 1: + return children[0] + else: + return self.node_builder(children) + + + +class PropagatePositions: + def __init__(self, node_builder, node_filter=None): + self.node_builder = node_builder + self.node_filter = node_filter + + def __call__(self, children): + res = self.node_builder(children) + + if isinstance(res, Tree): + ## + + ## + + ## + + ## + + + res_meta = res.meta + + first_meta = self._pp_get_meta(children) + if first_meta is not None: + if not hasattr(res_meta, 'line'): + ## + + res_meta.line = getattr(first_meta, 'container_line', first_meta.line) + res_meta.column = getattr(first_meta, 'container_column', first_meta.column) + res_meta.start_pos = getattr(first_meta, 'container_start_pos', first_meta.start_pos) + res_meta.empty = False + + res_meta.container_line = getattr(first_meta, 'container_line', first_meta.line) + res_meta.container_column = getattr(first_meta, 'container_column', first_meta.column) + res_meta.container_start_pos = getattr(first_meta, 'container_start_pos', first_meta.start_pos) + + last_meta = self._pp_get_meta(reversed(children)) + if last_meta is not None: + if not hasattr(res_meta, 'end_line'): + res_meta.end_line = getattr(last_meta, 'container_end_line', last_meta.end_line) + res_meta.end_column = getattr(last_meta, 'container_end_column', last_meta.end_column) + res_meta.end_pos = getattr(last_meta, 'container_end_pos', last_meta.end_pos) + res_meta.empty = False + + res_meta.container_end_line = getattr(last_meta, 'container_end_line', last_meta.end_line) + res_meta.container_end_column = getattr(last_meta, 'container_end_column', last_meta.end_column) + res_meta.container_end_pos = getattr(last_meta, 'container_end_pos', last_meta.end_pos) + + return res + + def _pp_get_meta(self, children): + for c in children: + if self.node_filter is not None and not self.node_filter(c): + continue + if isinstance(c, Tree): + if not c.meta.empty: + return c.meta + elif isinstance(c, Token): + return c + elif hasattr(c, '__lark_meta__'): + return c.__lark_meta__() + +def make_propagate_positions(option): + if callable(option): + return partial(PropagatePositions, node_filter=option) + elif option is True: + return PropagatePositions + elif option is False: + return None + + raise ConfigurationError('Invalid option for propagate_positions: %r' % option) + + +class ChildFilter: + def __init__(self, to_include, append_none, node_builder): + self.node_builder = node_builder + self.to_include = to_include + self.append_none = append_none + + def __call__(self, children): + filtered = [] + + for i, to_expand, add_none in self.to_include: + if add_none: + filtered += [None] * add_none + if to_expand: + filtered += children[i].children + else: + filtered.append(children[i]) + + if self.append_none: + filtered += [None] * self.append_none + + return self.node_builder(filtered) + + +class ChildFilterLALR(ChildFilter): + #-- + + def __call__(self, children): + filtered = [] + for i, to_expand, add_none in self.to_include: + if add_none: + filtered += [None] * add_none + if to_expand: + if filtered: + filtered += children[i].children + else: ## + + filtered = children[i].children + else: + filtered.append(children[i]) + + if self.append_none: + filtered += [None] * self.append_none + + return self.node_builder(filtered) + + +class ChildFilterLALR_NoPlaceholders(ChildFilter): + #-- + def __init__(self, to_include, node_builder): + self.node_builder = node_builder + self.to_include = to_include + + def __call__(self, children): + filtered = [] + for i, to_expand in self.to_include: + if to_expand: + if filtered: + filtered += children[i].children + else: ## + + filtered = children[i].children + else: + filtered.append(children[i]) + return self.node_builder(filtered) + + +def _should_expand(sym): + return not sym.is_term and sym.name.startswith('_') + + +def maybe_create_child_filter(expansion, keep_all_tokens, ambiguous, _empty_indices: List[bool]): + ## + + if _empty_indices: + assert _empty_indices.count(False) == len(expansion) + s = ''.join(str(int(b)) for b in _empty_indices) + empty_indices = [len(ones) for ones in s.split('0')] + assert len(empty_indices) == len(expansion)+1, (empty_indices, len(expansion)) + else: + empty_indices = [0] * (len(expansion)+1) + + to_include = [] + nones_to_add = 0 + for i, sym in enumerate(expansion): + nones_to_add += empty_indices[i] + if keep_all_tokens or not (sym.is_term and sym.filter_out): + to_include.append((i, _should_expand(sym), nones_to_add)) + nones_to_add = 0 + + nones_to_add += empty_indices[len(expansion)] + + if _empty_indices or len(to_include) < len(expansion) or any(to_expand for i, to_expand,_ in to_include): + if _empty_indices or ambiguous: + return partial(ChildFilter if ambiguous else ChildFilterLALR, to_include, nones_to_add) + else: + ## + + return partial(ChildFilterLALR_NoPlaceholders, [(i, x) for i,x,_ in to_include]) + + +class AmbiguousExpander: + #-- + def __init__(self, to_expand, tree_class, node_builder): + self.node_builder = node_builder + self.tree_class = tree_class + self.to_expand = to_expand + + def __call__(self, children): + def _is_ambig_tree(t): + return hasattr(t, 'data') and t.data == '_ambig' + + ## + + ## + + ## + + ## + + ambiguous = [] + for i, child in enumerate(children): + if _is_ambig_tree(child): + if i in self.to_expand: + ambiguous.append(i) + + child.expand_kids_by_data('_ambig') + + if not ambiguous: + return self.node_builder(children) + + expand = [child.children if i in ambiguous else (child,) for i, child in enumerate(children)] + return self.tree_class('_ambig', [self.node_builder(list(f)) for f in product(*expand)]) + + +def maybe_create_ambiguous_expander(tree_class, expansion, keep_all_tokens): + to_expand = [i for i, sym in enumerate(expansion) + if keep_all_tokens or ((not (sym.is_term and sym.filter_out)) and _should_expand(sym))] + if to_expand: + return partial(AmbiguousExpander, to_expand, tree_class) + + +class AmbiguousIntermediateExpander: + #-- + + def __init__(self, tree_class, node_builder): + self.node_builder = node_builder + self.tree_class = tree_class + + def __call__(self, children): + def _is_iambig_tree(child): + return hasattr(child, 'data') and child.data == '_iambig' + + def _collapse_iambig(children): + #-- + + ## + + ## + + if children and _is_iambig_tree(children[0]): + iambig_node = children[0] + result = [] + for grandchild in iambig_node.children: + collapsed = _collapse_iambig(grandchild.children) + if collapsed: + for child in collapsed: + child.children += children[1:] + result += collapsed + else: + new_tree = self.tree_class('_inter', grandchild.children + children[1:]) + result.append(new_tree) + return result + + collapsed = _collapse_iambig(children) + if collapsed: + processed_nodes = [self.node_builder(c.children) for c in collapsed] + return self.tree_class('_ambig', processed_nodes) + + return self.node_builder(children) + + + +def inplace_transformer(func): + @wraps(func) + def f(children): + ## + + tree = Tree(func.__name__, children) + return func(tree) + return f + + +def apply_visit_wrapper(func, name, wrapper): + if wrapper is _vargs_meta or wrapper is _vargs_meta_inline: + raise NotImplementedError("Meta args not supported for internal transformer; use YourTransformer().transform(parser.parse()) instead") + + @wraps(func) + def f(children): + return wrapper(func, name, children, None) + return f + + +class ParseTreeBuilder: + def __init__(self, rules, tree_class, propagate_positions=False, ambiguous=False, maybe_placeholders=False): + self.tree_class = tree_class + self.propagate_positions = propagate_positions + self.ambiguous = ambiguous + self.maybe_placeholders = maybe_placeholders + + self.rule_builders = list(self._init_builders(rules)) + + def _init_builders(self, rules): + propagate_positions = make_propagate_positions(self.propagate_positions) + + for rule in rules: + options = rule.options + keep_all_tokens = options.keep_all_tokens + expand_single_child = options.expand1 + + wrapper_chain = list(filter(None, [ + (expand_single_child and not rule.alias) and ExpandSingleChild, + maybe_create_child_filter(rule.expansion, keep_all_tokens, self.ambiguous, options.empty_indices if self.maybe_placeholders else None), + propagate_positions, + self.ambiguous and maybe_create_ambiguous_expander(self.tree_class, rule.expansion, keep_all_tokens), + self.ambiguous and partial(AmbiguousIntermediateExpander, self.tree_class) + ])) + + yield rule, wrapper_chain + + def create_callback(self, transformer=None): + callbacks = {} + + default_handler = getattr(transformer, '__default__', None) + if default_handler: + def default_callback(data, children): + return default_handler(data, children, None) + else: + default_callback = self.tree_class + + for rule, wrapper_chain in self.rule_builders: + + user_callback_name = rule.alias or rule.options.template_source or rule.origin.name + try: + f = getattr(transformer, user_callback_name) + wrapper = getattr(f, 'visit_wrapper', None) + if wrapper is not None: + f = apply_visit_wrapper(f, user_callback_name, wrapper) + elif isinstance(transformer, Transformer_InPlace): + f = inplace_transformer(f) + except AttributeError: + f = partial(default_callback, user_callback_name) + + for w in wrapper_chain: + f = w(f) + + if rule in callbacks: + raise GrammarError("Rule '%s' already exists" % (rule,)) + + callbacks[rule] = f + + return callbacks + + + +class Action: + def __init__(self, name): + self.name = name + def __str__(self): + return self.name + def __repr__(self): + return str(self) + +Shift = Action('Shift') +Reduce = Action('Reduce') + +StateT = TypeVar("StateT") + +class ParseTableBase(Generic[StateT]): + states: Dict[StateT, Dict[str, Tuple]] + start_states: Dict[str, StateT] + end_states: Dict[str, StateT] + + def __init__(self, states, start_states, end_states): + self.states = states + self.start_states = start_states + self.end_states = end_states + + def serialize(self, memo): + tokens = Enumerator() + + states = { + state: {tokens.get(token): ((1, arg.serialize(memo)) if action is Reduce else (0, arg)) + for token, (action, arg) in actions.items()} + for state, actions in self.states.items() + } + + return { + 'tokens': tokens.reversed(), + 'states': states, + 'start_states': self.start_states, + 'end_states': self.end_states, + } + + @classmethod + def deserialize(cls, data, memo): + tokens = data['tokens'] + states = { + state: {tokens[token]: ((Reduce, Rule.deserialize(arg, memo)) if action==1 else (Shift, arg)) + for token, (action, arg) in actions.items()} + for state, actions in data['states'].items() + } + return cls(states, data['start_states'], data['end_states']) + +class ParseTable(ParseTableBase['State']): + #-- + pass + + +class IntParseTable(ParseTableBase[int]): + #-- + + @classmethod + def from_ParseTable(cls, parse_table: ParseTable): + enum = list(parse_table.states) + state_to_idx: Dict['State', int] = {s:i for i,s in enumerate(enum)} + int_states = {} + + for s, la in parse_table.states.items(): + la = {k:(v[0], state_to_idx[v[1]]) if v[0] is Shift else v + for k,v in la.items()} + int_states[ state_to_idx[s] ] = la + + + start_states = {start:state_to_idx[s] for start, s in parse_table.start_states.items()} + end_states = {start:state_to_idx[s] for start, s in parse_table.end_states.items()} + return cls(int_states, start_states, end_states) + + + +class ParseConf(Generic[StateT]): + __slots__ = 'parse_table', 'callbacks', 'start', 'start_state', 'end_state', 'states' + + parse_table: ParseTableBase[StateT] + callbacks: ParserCallbacks + start: str + + start_state: StateT + end_state: StateT + states: Dict[StateT, Dict[str, tuple]] + + def __init__(self, parse_table: ParseTableBase[StateT], callbacks: ParserCallbacks, start: str): + self.parse_table = parse_table + + self.start_state = self.parse_table.start_states[start] + self.end_state = self.parse_table.end_states[start] + self.states = self.parse_table.states + + self.callbacks = callbacks + self.start = start + +class ParserState(Generic[StateT]): + __slots__ = 'parse_conf', 'lexer', 'state_stack', 'value_stack' + + parse_conf: ParseConf[StateT] + lexer: LexerThread + state_stack: List[StateT] + value_stack: list + + def __init__(self, parse_conf: ParseConf[StateT], lexer: LexerThread, state_stack=None, value_stack=None): + self.parse_conf = parse_conf + self.lexer = lexer + self.state_stack = state_stack or [self.parse_conf.start_state] + self.value_stack = value_stack or [] + + @property + def position(self) -> StateT: + return self.state_stack[-1] + + ## + + def __eq__(self, other) -> bool: + if not isinstance(other, ParserState): + return NotImplemented + return len(self.state_stack) == len(other.state_stack) and self.position == other.position + + def __copy__(self): + return self.copy() + + def copy(self, deepcopy_values=True) -> 'ParserState[StateT]': + return type(self)( + self.parse_conf, + self.lexer, ## + + copy(self.state_stack), + deepcopy(self.value_stack) if deepcopy_values else copy(self.value_stack), + ) + + def feed_token(self, token: Token, is_end=False) -> Any: + state_stack = self.state_stack + value_stack = self.value_stack + states = self.parse_conf.states + end_state = self.parse_conf.end_state + callbacks = self.parse_conf.callbacks + + while True: + state = state_stack[-1] + try: + action, arg = states[state][token.type] + except KeyError: + expected = {s for s in states[state].keys() if s.isupper()} + raise UnexpectedToken(token, expected, state=self, interactive_parser=None) + + assert arg != end_state + + if action is Shift: + ## + + assert not is_end + state_stack.append(arg) + value_stack.append(token if token.type not in callbacks else callbacks[token.type](token)) + return + else: + ## + + rule = arg + size = len(rule.expansion) + if size: + s = value_stack[-size:] + del state_stack[-size:] + del value_stack[-size:] + else: + s = [] + + value = callbacks[rule](s) if callbacks else s + + _action, new_state = states[state_stack[-1]][rule.origin.name] + assert _action is Shift + state_stack.append(new_state) + value_stack.append(value) + + if is_end and state_stack[-1] == end_state: + return value_stack[-1] + + +class LALR_Parser(Serialize): + def __init__(self, parser_conf: ParserConf, debug: bool=False, strict: bool=False): + analysis = LALR_Analyzer(parser_conf, debug=debug, strict=strict) + analysis.compute_lalr() + callbacks = parser_conf.callbacks + + self._parse_table = analysis.parse_table + self.parser_conf = parser_conf + self.parser = _Parser(analysis.parse_table, callbacks, debug) + + @classmethod + def deserialize(cls, data, memo, callbacks, debug=False): + inst = cls.__new__(cls) + inst._parse_table = IntParseTable.deserialize(data, memo) + inst.parser = _Parser(inst._parse_table, callbacks, debug) + return inst + + def serialize(self, memo: Any = None) -> Dict[str, Any]: + return self._parse_table.serialize(memo) + + def parse_interactive(self, lexer: LexerThread, start: str): + return self.parser.parse(lexer, start, start_interactive=True) + + def parse(self, lexer, start, on_error=None): + try: + return self.parser.parse(lexer, start) + except UnexpectedInput as e: + if on_error is None: + raise + + while True: + if isinstance(e, UnexpectedCharacters): + s = e.interactive_parser.lexer_thread.state + p = s.line_ctr.char_pos + + if not on_error(e): + raise e + + if isinstance(e, UnexpectedCharacters): + ## + + if p == s.line_ctr.char_pos: + s.line_ctr.feed(s.text.text[p:p+1]) + + try: + return e.interactive_parser.resume_parse() + except UnexpectedToken as e2: + if (isinstance(e, UnexpectedToken) + and e.token.type == e2.token.type == '$END' + and e.interactive_parser == e2.interactive_parser): + ## + + raise e2 + e = e2 + except UnexpectedCharacters as e2: + e = e2 + + +class _Parser: + parse_table: ParseTableBase + callbacks: ParserCallbacks + debug: bool + + def __init__(self, parse_table: ParseTableBase, callbacks: ParserCallbacks, debug: bool=False): + self.parse_table = parse_table + self.callbacks = callbacks + self.debug = debug + + def parse(self, lexer: LexerThread, start: str, value_stack=None, state_stack=None, start_interactive=False): + parse_conf = ParseConf(self.parse_table, self.callbacks, start) + parser_state = ParserState(parse_conf, lexer, state_stack, value_stack) + if start_interactive: + return InteractiveParser(self, parser_state, parser_state.lexer) + return self.parse_from_state(parser_state) + + + def parse_from_state(self, state: ParserState, last_token: Optional[Token]=None): + #-- + try: + token = last_token + for token in state.lexer.lex(state): + assert token is not None + state.feed_token(token) + + end_token = Token.new_borrow_pos('$END', '', token) if token else Token('$END', '', 0, 1, 1) + return state.feed_token(end_token, True) + except UnexpectedInput as e: + try: + e.interactive_parser = InteractiveParser(self, state, state.lexer) + except NameError: + pass + raise e + except Exception as e: + if self.debug: + print("") + print("STATE STACK DUMP") + print("----------------") + for i, s in enumerate(state.state_stack): + print('%d)' % i , s) + print("") + + raise + + +class InteractiveParser: + #-- + def __init__(self, parser, parser_state: ParserState, lexer_thread: LexerThread): + self.parser = parser + self.parser_state = parser_state + self.lexer_thread = lexer_thread + self.result = None + + @property + def lexer_state(self) -> LexerThread: + warnings.warn("lexer_state will be removed in subsequent releases. Use lexer_thread instead.", DeprecationWarning) + return self.lexer_thread + + def feed_token(self, token: Token): + #-- + return self.parser_state.feed_token(token, token.type == '$END') + + def iter_parse(self) -> Iterator[Token]: + #-- + for token in self.lexer_thread.lex(self.parser_state): + yield token + self.result = self.feed_token(token) + + def exhaust_lexer(self) -> List[Token]: + #-- + return list(self.iter_parse()) + + + def feed_eof(self, last_token=None): + #-- + eof = Token.new_borrow_pos('$END', '', last_token) if last_token is not None else self.lexer_thread._Token('$END', '', 0, 1, 1) + return self.feed_token(eof) + + + def __copy__(self): + #-- + return self.copy() + + def copy(self, deepcopy_values=True): + return type(self)( + self.parser, + self.parser_state.copy(deepcopy_values=deepcopy_values), + copy(self.lexer_thread), + ) + + def __eq__(self, other): + if not isinstance(other, InteractiveParser): + return False + + return self.parser_state == other.parser_state and self.lexer_thread == other.lexer_thread + + def as_immutable(self): + #-- + p = copy(self) + return ImmutableInteractiveParser(p.parser, p.parser_state, p.lexer_thread) + + def pretty(self): + #-- + out = ["Parser choices:"] + for k, v in self.choices().items(): + out.append('\t- %s -> %r' % (k, v)) + out.append('stack size: %s' % len(self.parser_state.state_stack)) + return '\n'.join(out) + + def choices(self): + #-- + return self.parser_state.parse_conf.parse_table.states[self.parser_state.position] + + def accepts(self): + #-- + accepts = set() + conf_no_callbacks = copy(self.parser_state.parse_conf) + ## + + ## + + conf_no_callbacks.callbacks = {} + for t in self.choices(): + if t.isupper(): ## + + new_cursor = self.copy(deepcopy_values=False) + new_cursor.parser_state.parse_conf = conf_no_callbacks + try: + new_cursor.feed_token(self.lexer_thread._Token(t, '')) + except UnexpectedToken: + pass + else: + accepts.add(t) + return accepts + + def resume_parse(self): + #-- + return self.parser.parse_from_state(self.parser_state, last_token=self.lexer_thread.state.last_token) + + + +class ImmutableInteractiveParser(InteractiveParser): + #-- + + result = None + + def __hash__(self): + return hash((self.parser_state, self.lexer_thread)) + + def feed_token(self, token): + c = copy(self) + c.result = InteractiveParser.feed_token(c, token) + return c + + def exhaust_lexer(self): + #-- + cursor = self.as_mutable() + cursor.exhaust_lexer() + return cursor.as_immutable() + + def as_mutable(self): + #-- + p = copy(self) + return InteractiveParser(p.parser, p.parser_state, p.lexer_thread) + + + +def _wrap_lexer(lexer_class): + future_interface = getattr(lexer_class, '__future_interface__', 0) + if future_interface == 2: + return lexer_class + elif future_interface == 1: + class CustomLexerWrapper1(Lexer): + def __init__(self, lexer_conf): + self.lexer = lexer_class(lexer_conf) + def lex(self, lexer_state, parser_state): + if isinstance(lexer_state.text, TextSlice) and not lexer_state.text.is_complete_text(): + raise TypeError("Interface=1 Custom Lexer don't support TextSlice") + lexer_state.text = lexer_state.text + return self.lexer.lex(lexer_state, parser_state) + return CustomLexerWrapper1 + elif future_interface == 0: + class CustomLexerWrapper0(Lexer): + def __init__(self, lexer_conf): + self.lexer = lexer_class(lexer_conf) + + def lex(self, lexer_state, parser_state): + if isinstance(lexer_state.text, TextSlice): + if not lexer_state.text.is_complete_text(): + raise TypeError("Interface=0 Custom Lexer don't support TextSlice") + return self.lexer.lex(lexer_state.text.text) + return self.lexer.lex(lexer_state.text) + return CustomLexerWrapper0 + else: + raise ValueError(f"Unknown __future_interface__ value {future_interface}, integer 0-2 expected") + + +def _deserialize_parsing_frontend(data, memo, lexer_conf, callbacks, options): + parser_conf = ParserConf.deserialize(data['parser_conf'], memo) + cls = (options and options._plugins.get('LALR_Parser')) or LALR_Parser + parser = cls.deserialize(data['parser'], memo, callbacks, options.debug) + parser_conf.callbacks = callbacks + return ParsingFrontend(lexer_conf, parser_conf, options, parser=parser) + + +_parser_creators: 'Dict[str, Callable[[LexerConf, Any, Any], Any]]' = {} + + +class ParsingFrontend(Serialize): + __serialize_fields__ = 'lexer_conf', 'parser_conf', 'parser' + + lexer_conf: LexerConf + parser_conf: ParserConf + options: Any + + def __init__(self, lexer_conf: LexerConf, parser_conf: ParserConf, options, parser=None): + self.parser_conf = parser_conf + self.lexer_conf = lexer_conf + self.options = options + + ## + + if parser: ## + + self.parser = parser + else: + create_parser = _parser_creators.get(parser_conf.parser_type) + assert create_parser is not None, "{} is not supported in standalone mode".format( + parser_conf.parser_type + ) + self.parser = create_parser(lexer_conf, parser_conf, options) + + ## + + lexer_type = options.lexer if (options and options.lexer) else lexer_conf.lexer_type + self.skip_lexer = False + if lexer_type in ('dynamic', 'dynamic_complete'): + assert lexer_conf.postlex is None + self.skip_lexer = True + return + + if isinstance(lexer_type, type): + assert issubclass(lexer_type, Lexer) or hasattr(lexer_type, 'lex') + self.lexer = _wrap_lexer(lexer_type)(lexer_conf) + elif isinstance(lexer_type, str): + create_lexer = { + 'basic': create_basic_lexer, + 'contextual': create_contextual_lexer, + }[lexer_type] + self.lexer = create_lexer(lexer_conf, self.parser, lexer_conf.postlex, options) + else: + raise TypeError("Bad value for lexer_type: {lexer_type}") + + if lexer_conf.postlex: + self.lexer = PostLexConnector(self.lexer, lexer_conf.postlex) + + def _verify_start(self, start=None): + if start is None: + start_decls = self.parser_conf.start + if len(start_decls) > 1: + raise ConfigurationError("Lark initialized with more than 1 possible start rule. Must specify which start rule to parse", start_decls) + start ,= start_decls + elif start not in self.parser_conf.start: + raise ConfigurationError("Unknown start rule %s. Must be one of %r" % (start, self.parser_conf.start)) + return start + + def _make_lexer_thread(self, text: Optional[LarkInput]) -> Union[LarkInput, LexerThread, None]: + cls = (self.options and self.options._plugins.get('LexerThread')) or LexerThread + if self.skip_lexer: + return text + if text is None: + return cls(self.lexer, None) + if isinstance(text, (str, bytes, TextSlice)): + return cls.from_text(self.lexer, text) + return cls.from_custom_input(self.lexer, text) + + def parse(self, text: Optional[LarkInput], start=None, on_error=None): + if self.lexer_conf.lexer_type in ("dynamic", "dynamic_complete"): + if isinstance(text, TextSlice) and not text.is_complete_text(): + raise TypeError(f"Lexer {self.lexer_conf.lexer_type} does not support text slices.") + + chosen_start = self._verify_start(start) + kw = {} if on_error is None else {'on_error': on_error} + stream = self._make_lexer_thread(text) + return self.parser.parse(stream, chosen_start, **kw) + + def parse_interactive(self, text: Optional[TextOrSlice]=None, start=None): + ## + + ## + + chosen_start = self._verify_start(start) + if self.parser_conf.parser_type != 'lalr': + raise ConfigurationError("parse_interactive() currently only works with parser='lalr' ") + stream = self._make_lexer_thread(text) + return self.parser.parse_interactive(stream, chosen_start) + + +def _validate_frontend_args(parser, lexer) -> None: + assert_config(parser, ('lalr', 'earley', 'cyk')) + if not isinstance(lexer, type): ## + + expected = { + 'lalr': ('basic', 'contextual'), + 'earley': ('basic', 'dynamic', 'dynamic_complete'), + 'cyk': ('basic', ), + }[parser] + assert_config(lexer, expected, 'Parser %r does not support lexer %%r, expected one of %%s' % parser) + + +def _get_lexer_callbacks(transformer, terminals): + result = {} + for terminal in terminals: + callback = getattr(transformer, terminal.name, None) + if callback is not None: + result[terminal.name] = callback + return result + +class PostLexConnector: + def __init__(self, lexer, postlexer): + self.lexer = lexer + self.postlexer = postlexer + + def lex(self, lexer_state, parser_state): + i = self.lexer.lex(lexer_state, parser_state) + return self.postlexer.process(i) + + + +def create_basic_lexer(lexer_conf, parser, postlex, options) -> BasicLexer: + cls = (options and options._plugins.get('BasicLexer')) or BasicLexer + return cls(lexer_conf) + +def create_contextual_lexer(lexer_conf: LexerConf, parser, postlex, options) -> ContextualLexer: + cls = (options and options._plugins.get('ContextualLexer')) or ContextualLexer + parse_table: ParseTableBase[int] = parser._parse_table + states: Dict[int, Collection[str]] = {idx:list(t.keys()) for idx, t in parse_table.states.items()} + always_accept: Collection[str] = postlex.always_accept if postlex else () + return cls(lexer_conf, states, always_accept=always_accept) + +def create_lalr_parser(lexer_conf: LexerConf, parser_conf: ParserConf, options=None) -> LALR_Parser: + debug = options.debug if options else False + strict = options.strict if options else False + cls = (options and options._plugins.get('LALR_Parser')) or LALR_Parser + return cls(parser_conf, debug=debug, strict=strict) + +_parser_creators['lalr'] = create_lalr_parser + + + + +class PostLex(ABC): + @abstractmethod + def process(self, stream: Iterator[Token]) -> Iterator[Token]: + return stream + + always_accept: Iterable[str] = () + +class LarkOptions(Serialize): + #-- + + start: List[str] + debug: bool + strict: bool + transformer: 'Optional[Transformer]' + propagate_positions: Union[bool, str] + maybe_placeholders: bool + cache: Union[bool, str] + cache_grammar: bool + regex: bool + g_regex_flags: int + keep_all_tokens: bool + tree_class: Optional[Callable[[str, List], Any]] + parser: _ParserArgType + lexer: _LexerArgType + ambiguity: 'Literal["auto", "resolve", "explicit", "forest"]' + postlex: Optional[PostLex] + priority: 'Optional[Literal["auto", "normal", "invert"]]' + lexer_callbacks: Dict[str, Callable[[Token], Token]] + use_bytes: bool + ordered_sets: bool + edit_terminals: Optional[Callable[[TerminalDef], TerminalDef]] + import_paths: 'List[Union[str, Callable[[Union[None, str, PackageResource], str], Tuple[str, str]]]]' + source_path: Optional[str] + + OPTIONS_DOC = r""" + **=== General Options ===** + + start + The start symbol. Either a string, or a list of strings for multiple possible starts (Default: "start") + debug + Display debug information and extra warnings. Use only when debugging (Default: ``False``) + When used with Earley, it generates a forest graph as "sppf.png", if 'dot' is installed. + strict + Throw an exception on any potential ambiguity, including shift/reduce conflicts, and regex collisions. + transformer + Applies the transformer to every parse tree (equivalent to applying it after the parse, but faster) + propagate_positions + Propagates positional attributes into the 'meta' attribute of all tree branches. + Sets attributes: (line, column, end_line, end_column, start_pos, end_pos, + container_line, container_column, container_end_line, container_end_column) + Accepts ``False``, ``True``, or a callable, which will filter which nodes to ignore when propagating. + maybe_placeholders + When ``True``, the ``[]`` operator returns ``None`` when not matched. + When ``False``, ``[]`` behaves like the ``?`` operator, and returns no value at all. + (default= ``True``) + cache + Cache the results of the Lark grammar analysis, for x2 to x3 faster loading. LALR only for now. + + - When ``False``, does nothing (default) + - When ``True``, caches to a temporary file in the local directory + - When given a string, caches to the path pointed by the string + cache_grammar + For use with ``cache`` option. When ``True``, the unanalyzed grammar is also included in the cache. + Useful for classes that require the ``Lark.grammar`` to be present (e.g. Reconstructor). + (default= ``False``) + regex + When True, uses the ``regex`` module instead of the stdlib ``re``. + g_regex_flags + Flags that are applied to all terminals (both regex and strings) + keep_all_tokens + Prevent the tree builder from automagically removing "punctuation" tokens (Default: ``False``) + tree_class + Lark will produce trees comprised of instances of this class instead of the default ``lark.Tree``. + + **=== Algorithm Options ===** + + parser + Decides which parser engine to use. Accepts "earley" or "lalr". (Default: "earley"). + (there is also a "cyk" option for legacy) + lexer + Decides whether or not to use a lexer stage + + - "auto" (default): Choose for me based on the parser + - "basic": Use a basic lexer + - "contextual": Stronger lexer (only works with parser="lalr") + - "dynamic": Flexible and powerful (only with parser="earley") + - "dynamic_complete": Same as dynamic, but tries *every* variation of tokenizing possible. + ambiguity + Decides how to handle ambiguity in the parse. Only relevant if parser="earley" + + - "resolve": The parser will automatically choose the simplest derivation + (it chooses consistently: greedy for tokens, non-greedy for rules) + - "explicit": The parser will return all derivations wrapped in "_ambig" tree nodes (i.e. a forest). + - "forest": The parser will return the root of the shared packed parse forest. + + **=== Misc. / Domain Specific Options ===** + + postlex + Lexer post-processing (Default: ``None``) Only works with the basic and contextual lexers. + priority + How priorities should be evaluated - "auto", ``None``, "normal", "invert" (Default: "auto") + lexer_callbacks + Dictionary of callbacks for the lexer. May alter tokens during lexing. Use with caution. + use_bytes + Accept an input of type ``bytes`` instead of ``str``. + ordered_sets + Should Earley use ordered-sets to achieve stable output (~10% slower than regular sets. Default: True) + edit_terminals + A callback for editing the terminals before parse. + import_paths + A List of either paths or loader functions to specify from where grammars are imported + source_path + Override the source of from where the grammar was loaded. Useful for relative imports and unconventional grammar loading + **=== End of Options ===** + """ + if __doc__: + __doc__ += OPTIONS_DOC + + + ## + + ## + + ## + + ## + + ## + + ## + + _defaults: Dict[str, Any] = { + 'debug': False, + 'strict': False, + 'keep_all_tokens': False, + 'tree_class': None, + 'cache': False, + 'cache_grammar': False, + 'postlex': None, + 'parser': 'earley', + 'lexer': 'auto', + 'transformer': None, + 'start': 'start', + 'priority': 'auto', + 'ambiguity': 'auto', + 'regex': False, + 'propagate_positions': False, + 'lexer_callbacks': {}, + 'maybe_placeholders': True, + 'edit_terminals': None, + 'g_regex_flags': 0, + 'use_bytes': False, + 'ordered_sets': True, + 'import_paths': [], + 'source_path': None, + '_plugins': {}, + } + + def __init__(self, options_dict: Dict[str, Any]) -> None: + o = dict(options_dict) + + options = {} + for name, default in self._defaults.items(): + if name in o: + value = o.pop(name) + if isinstance(default, bool) and name not in ('cache', 'use_bytes', 'propagate_positions'): + value = bool(value) + else: + value = default + + options[name] = value + + if isinstance(options['start'], str): + options['start'] = [options['start']] + + self.__dict__['options'] = options + + + assert_config(self.parser, ('earley', 'lalr', 'cyk', None)) + + if self.parser == 'earley' and self.transformer: + raise ConfigurationError('Cannot specify an embedded transformer when using the Earley algorithm. ' + 'Please use your transformer on the resulting parse tree, or use a different algorithm (i.e. LALR)') + + if self.cache_grammar and not self.cache: + raise ConfigurationError('cache_grammar cannot be set when cache is disabled') + + if o: + raise ConfigurationError("Unknown options: %s" % o.keys()) + + def __getattr__(self, name: str) -> Any: + try: + return self.__dict__['options'][name] + except KeyError as e: + raise AttributeError(e) + + def __setattr__(self, name: str, value: str) -> None: + assert_config(name, self.options.keys(), "%r isn't a valid option. Expected one of: %s") + self.options[name] = value + + def serialize(self, memo = None) -> Dict[str, Any]: + return self.options + + @classmethod + def deserialize(cls, data: Dict[str, Any], memo: Dict[int, Union[TerminalDef, Rule]]) -> "LarkOptions": + return cls(data) + + +## + +## + +_LOAD_ALLOWED_OPTIONS = {'lexer', 'postlex', 'transformer', 'lexer_callbacks', 'use_bytes', 'debug', 'g_regex_flags', 'regex', 'propagate_positions', 'tree_class', '_plugins'} + +_VALID_PRIORITY_OPTIONS = ('auto', 'normal', 'invert', None) +_VALID_AMBIGUITY_OPTIONS = ('auto', 'resolve', 'explicit', 'forest') + + +_T = TypeVar('_T', bound="Lark") + +class Lark(Serialize): + #-- + + source_path: str + source_grammar: str + grammar: 'Grammar' + options: LarkOptions + lexer: Lexer + parser: 'ParsingFrontend' + terminals: Collection[TerminalDef] + + __serialize_fields__ = ['parser', 'rules', 'options'] + + def __init__(self, grammar: 'Union[Grammar, str, IO[str]]', **options) -> None: + self.options = LarkOptions(options) + re_module: types.ModuleType + + ## + + if self.options.cache_grammar: + self.__serialize_fields__ = self.__serialize_fields__ + ['grammar'] + + ## + + use_regex = self.options.regex + if use_regex: + if _has_regex: + re_module = regex + else: + raise ImportError('`regex` module must be installed if calling `Lark(regex=True)`.') + else: + re_module = re + + ## + + if self.options.source_path is None: + try: + self.source_path = grammar.name ## + + except AttributeError: + self.source_path = '' + else: + self.source_path = self.options.source_path + + ## + + try: + read = grammar.read ## + + except AttributeError: + pass + else: + grammar = read() + + cache_fn = None + cache_sha256 = None + if isinstance(grammar, str): + self.source_grammar = grammar + if self.options.use_bytes: + if not grammar.isascii(): + raise ConfigurationError("Grammar must be ascii only, when use_bytes=True") + + if self.options.cache: + if self.options.parser != 'lalr': + raise ConfigurationError("cache only works with parser='lalr' for now") + + unhashable = ('transformer', 'postlex', 'lexer_callbacks', 'edit_terminals', '_plugins') + options_str = ''.join(k+str(v) for k, v in options.items() if k not in unhashable) + from . import __version__ + s = grammar + options_str + __version__ + str(sys.version_info[:2]) + cache_sha256 = sha256_digest(s) + + if isinstance(self.options.cache, str): + cache_fn = self.options.cache + else: + if self.options.cache is not True: + raise ConfigurationError("cache argument must be bool or str") + + try: + username = getpass.getuser() + except Exception: + ## + + ## + + ## + + username = "unknown" + + + cache_fn = tempfile.gettempdir() + "/.lark_%s_%s_%s_%s_%s.tmp" % ( + "cache_grammar" if self.options.cache_grammar else "cache", username, cache_sha256, *sys.version_info[:2]) + + old_options = self.options + try: + with FS.open(cache_fn, 'rb') as f: + logger.debug('Loading grammar from cache: %s', cache_fn) + ## + + for name in (set(options) - _LOAD_ALLOWED_OPTIONS): + del options[name] + file_sha256 = f.readline().rstrip(b'\n') + cached_used_files = pickle.load(f) + if file_sha256 == cache_sha256.encode('utf8') and verify_used_files(cached_used_files): + cached_parser_data = pickle.load(f) + self._load(cached_parser_data, **options) + return + except FileNotFoundError: + ## + + pass + except Exception: ## + + logger.exception("Failed to load Lark from cache: %r. We will try to carry on.", cache_fn) + + ## + + ## + + self.options = old_options + + + ## + + self.grammar, used_files = load_grammar(grammar, self.source_path, self.options.import_paths, self.options.keep_all_tokens) + else: + assert isinstance(grammar, Grammar) + self.grammar = grammar + + + if self.options.lexer == 'auto': + if self.options.parser == 'lalr': + self.options.lexer = 'contextual' + elif self.options.parser == 'earley': + if self.options.postlex is not None: + logger.info("postlex can't be used with the dynamic lexer, so we use 'basic' instead. " + "Consider using lalr with contextual instead of earley") + self.options.lexer = 'basic' + else: + self.options.lexer = 'dynamic' + elif self.options.parser == 'cyk': + self.options.lexer = 'basic' + else: + assert False, self.options.parser + lexer = self.options.lexer + if isinstance(lexer, type): + assert issubclass(lexer, Lexer) ## + + else: + assert_config(lexer, ('basic', 'contextual', 'dynamic', 'dynamic_complete')) + if self.options.postlex is not None and 'dynamic' in lexer: + raise ConfigurationError("Can't use postlex with a dynamic lexer. Use basic or contextual instead") + + if self.options.ambiguity == 'auto': + if self.options.parser == 'earley': + self.options.ambiguity = 'resolve' + else: + assert_config(self.options.parser, ('earley', 'cyk'), "%r doesn't support disambiguation. Use one of these parsers instead: %s") + + if self.options.priority == 'auto': + self.options.priority = 'normal' + + if self.options.priority not in _VALID_PRIORITY_OPTIONS: + raise ConfigurationError("invalid priority option: %r. Must be one of %r" % (self.options.priority, _VALID_PRIORITY_OPTIONS)) + if self.options.ambiguity not in _VALID_AMBIGUITY_OPTIONS: + raise ConfigurationError("invalid ambiguity option: %r. Must be one of %r" % (self.options.ambiguity, _VALID_AMBIGUITY_OPTIONS)) + + if self.options.parser is None: + terminals_to_keep = '*' ## + + elif self.options.postlex is not None: + terminals_to_keep = set(self.options.postlex.always_accept) + else: + terminals_to_keep = set() + + ## + + self.terminals, self.rules, self.ignore_tokens = self.grammar.compile(self.options.start, terminals_to_keep) + + if self.options.edit_terminals: + for t in self.terminals: + self.options.edit_terminals(t) + + self._terminals_dict = {t.name: t for t in self.terminals} + + ## + + if self.options.priority == 'invert': + for rule in self.rules: + if rule.options.priority is not None: + rule.options.priority = -rule.options.priority + for term in self.terminals: + term.priority = -term.priority + ## + + ## + + ## + + elif self.options.priority is None: + for rule in self.rules: + if rule.options.priority is not None: + rule.options.priority = None + for term in self.terminals: + term.priority = 0 + + ## + + self.lexer_conf = LexerConf( + self.terminals, re_module, self.ignore_tokens, self.options.postlex, + self.options.lexer_callbacks, self.options.g_regex_flags, use_bytes=self.options.use_bytes, strict=self.options.strict + ) + + if self.options.parser: + self.parser = self._build_parser() + elif lexer: + self.lexer = self._build_lexer() + + if cache_fn: + logger.debug('Saving grammar to cache: %s', cache_fn) + try: + with FS.open(cache_fn, 'wb') as f: + assert cache_sha256 is not None + f.write(cache_sha256.encode('utf8') + b'\n') + pickle.dump(used_files, f) + self.save(f, _LOAD_ALLOWED_OPTIONS) + except IOError as e: + logger.exception("Failed to save Lark to cache: %r.", cache_fn, e) + + if __doc__: + __doc__ += "\n\n" + LarkOptions.OPTIONS_DOC + + def _build_lexer(self, dont_ignore: bool=False) -> BasicLexer: + lexer_conf = self.lexer_conf + if dont_ignore: + from copy import copy + lexer_conf = copy(lexer_conf) + lexer_conf.ignore = () + return BasicLexer(lexer_conf) + + def _prepare_callbacks(self) -> None: + self._callbacks = {} + ## + + if self.options.ambiguity != 'forest': + self._parse_tree_builder = ParseTreeBuilder( + self.rules, + self.options.tree_class or Tree, + self.options.propagate_positions, + self.options.parser != 'lalr' and self.options.ambiguity == 'explicit', + self.options.maybe_placeholders + ) + self._callbacks = self._parse_tree_builder.create_callback(self.options.transformer) + self._callbacks.update(_get_lexer_callbacks(self.options.transformer, self.terminals)) + + def _build_parser(self) -> "ParsingFrontend": + self._prepare_callbacks() + _validate_frontend_args(self.options.parser, self.options.lexer) + parser_conf = ParserConf(self.rules, self._callbacks, self.options.start) + return _construct_parsing_frontend( + self.options.parser, + self.options.lexer, + self.lexer_conf, + parser_conf, + options=self.options + ) + + def save(self, f, exclude_options: Collection[str] = ()) -> None: + #-- + if self.options.parser != 'lalr': + raise NotImplementedError("Lark.save() is only implemented for the LALR(1) parser.") + data, m = self.memo_serialize([TerminalDef, Rule]) + if exclude_options: + data["options"] = {n: v for n, v in data["options"].items() if n not in exclude_options} + pickle.dump({'data': data, 'memo': m}, f, protocol=pickle.HIGHEST_PROTOCOL) + + @classmethod + def load(cls: Type[_T], f) -> _T: + #-- + inst = cls.__new__(cls) + return inst._load(f) + + def _deserialize_lexer_conf(self, data: Dict[str, Any], memo: Dict[int, Union[TerminalDef, Rule]], options: LarkOptions) -> LexerConf: + lexer_conf = LexerConf.deserialize(data['lexer_conf'], memo) + lexer_conf.callbacks = options.lexer_callbacks or {} + lexer_conf.re_module = regex if options.regex else re + lexer_conf.use_bytes = options.use_bytes + lexer_conf.g_regex_flags = options.g_regex_flags + lexer_conf.skip_validation = True + lexer_conf.postlex = options.postlex + return lexer_conf + + def _load(self: _T, f: Any, **kwargs) -> _T: + if isinstance(f, dict): + d = f + else: + d = pickle.load(f) + memo_json = d['memo'] + data = d['data'] + + assert memo_json + memo = SerializeMemoizer.deserialize(memo_json, {'Rule': Rule, 'TerminalDef': TerminalDef}, {}) + if 'grammar' in data: + self.grammar = Grammar.deserialize(data['grammar'], memo) + options = dict(data['options']) + if (set(kwargs) - _LOAD_ALLOWED_OPTIONS) & set(LarkOptions._defaults): + raise ConfigurationError("Some options are not allowed when loading a Parser: {}" + .format(set(kwargs) - _LOAD_ALLOWED_OPTIONS)) + options.update(kwargs) + self.options = LarkOptions.deserialize(options, memo) + self.rules = [Rule.deserialize(r, memo) for r in data['rules']] + self.source_path = '' + _validate_frontend_args(self.options.parser, self.options.lexer) + self.lexer_conf = self._deserialize_lexer_conf(data['parser'], memo, self.options) + self.terminals = self.lexer_conf.terminals + self._prepare_callbacks() + self._terminals_dict = {t.name: t for t in self.terminals} + self.parser = _deserialize_parsing_frontend( + data['parser'], + memo, + self.lexer_conf, + self._callbacks, + self.options, ## + + ) + return self + + @classmethod + def _load_from_dict(cls, data, memo, **kwargs): + inst = cls.__new__(cls) + return inst._load({'data': data, 'memo': memo}, **kwargs) + + @classmethod + def open(cls: Type[_T], grammar_filename: str, rel_to: Optional[str]=None, **options) -> _T: + #-- + if rel_to: + basepath = os.path.dirname(rel_to) + grammar_filename = os.path.join(basepath, grammar_filename) + with open(grammar_filename, encoding='utf8') as f: + return cls(f, **options) + + @classmethod + def open_from_package(cls: Type[_T], package: str, grammar_path: str, search_paths: 'Sequence[str]'=[""], **options) -> _T: + #-- + package_loader = FromPackageLoader(package, search_paths) + full_path, text = package_loader(None, grammar_path) + options.setdefault('source_path', full_path) + options.setdefault('import_paths', []) + options['import_paths'].append(package_loader) + return cls(text, **options) + + def __repr__(self): + return 'Lark(open(%r), parser=%r, lexer=%r, ...)' % (self.source_path, self.options.parser, self.options.lexer) + + + def lex(self, text: TextOrSlice, dont_ignore: bool=False) -> Iterator[Token]: + #-- + lexer: Lexer + if not hasattr(self, 'lexer') or dont_ignore: + lexer = self._build_lexer(dont_ignore) + else: + lexer = self.lexer + lexer_thread = LexerThread.from_text(lexer, text) + stream = lexer_thread.lex(None) + if self.options.postlex: + return self.options.postlex.process(stream) + return stream + + def get_terminal(self, name: str) -> TerminalDef: + #-- + return self._terminals_dict[name] + + def parse_interactive(self, text: Optional[LarkInput]=None, start: Optional[str]=None) -> 'InteractiveParser': + #-- + return self.parser.parse_interactive(text, start=start) + + def parse(self, text: LarkInput, start: Optional[str]=None, on_error: 'Optional[Callable[[UnexpectedInput], bool]]'=None) -> 'ParseTree': + #-- + if on_error is not None and self.options.parser != 'lalr': + raise NotImplementedError("The on_error option is only implemented for the LALR(1) parser.") + return self.parser.parse(text, start=start, on_error=on_error) + + + + +class DedentError(LarkError): + pass + +class Indenter(PostLex, ABC): + #-- + paren_level: int + indent_level: List[int] + + def __init__(self) -> None: + self.paren_level = 0 + self.indent_level = [0] + assert self.tab_len > 0 + + def handle_NL(self, token: Token) -> Iterator[Token]: + if self.paren_level > 0: + return + + yield token + + indent_str = token.rsplit('\n', 1)[1] ## + + indent = indent_str.count(' ') + indent_str.count('\t') * self.tab_len + + if indent > self.indent_level[-1]: + self.indent_level.append(indent) + yield Token.new_borrow_pos(self.INDENT_type, indent_str, token) + else: + while indent < self.indent_level[-1]: + self.indent_level.pop() + yield Token.new_borrow_pos(self.DEDENT_type, indent_str, token) + + if indent != self.indent_level[-1]: + raise DedentError('Unexpected dedent to column %s. Expected dedent to %s' % (indent, self.indent_level[-1])) + + def _process(self, stream): + token = None + for token in stream: + if token.type == self.NL_type: + yield from self.handle_NL(token) + else: + yield token + + if token.type in self.OPEN_PAREN_types: + self.paren_level += 1 + elif token.type in self.CLOSE_PAREN_types: + self.paren_level -= 1 + assert self.paren_level >= 0 + + while len(self.indent_level) > 1: + self.indent_level.pop() + yield Token.new_borrow_pos(self.DEDENT_type, '', token) if token else Token(self.DEDENT_type, '', 0, 0, 0, 0, 0, 0) + + assert self.indent_level == [0], self.indent_level + + def process(self, stream): + self.paren_level = 0 + self.indent_level = [0] + return self._process(stream) + + ## + + @property + def always_accept(self): + return (self.NL_type,) + + @property + @abstractmethod + def NL_type(self) -> str: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def OPEN_PAREN_types(self) -> List[str]: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def CLOSE_PAREN_types(self) -> List[str]: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def INDENT_type(self) -> str: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def DEDENT_type(self) -> str: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def tab_len(self) -> int: + #-- + raise NotImplementedError() + + +class PythonIndenter(Indenter): + #-- + + NL_type = '_NEWLINE' + OPEN_PAREN_types = ['LPAR', 'LSQB', 'LBRACE'] + CLOSE_PAREN_types = ['RPAR', 'RSQB', 'RBRACE'] + INDENT_type = '_INDENT' + DEDENT_type = '_DEDENT' + tab_len = 8 + + +import pickle, zlib, base64 +DATA = ( +{'parser': {'lexer_conf': {'terminals': [], 'ignore': [], 'g_regex_flags': 0, 'use_bytes': False, 'lexer_type': 'contextual', '__type__': 'LexerConf'}, 'parser_conf': {'rules': [{'@': 0}, {'@': 1}, {'@': 2}, {'@': 3}, {'@': 4}, {'@': 5}, {'@': 6}, {'@': 7}, {'@': 8}, {'@': 9}, {'@': 10}, {'@': 11}, {'@': 12}, {'@': 13}, {'@': 14}, {'@': 15}, {'@': 16}, {'@': 17}, {'@': 18}, {'@': 19}, {'@': 20}, {'@': 21}, {'@': 22}, {'@': 23}, {'@': 24}, {'@': 25}, {'@': 26}, {'@': 27}, {'@': 28}, {'@': 29}, {'@': 30}, {'@': 31}, {'@': 32}, {'@': 33}, {'@': 34}, {'@': 35}, {'@': 36}, {'@': 37}, {'@': 38}, {'@': 39}, {'@': 40}, {'@': 41}, {'@': 42}, {'@': 43}, {'@': 44}, {'@': 45}, {'@': 46}, {'@': 47}, {'@': 48}, {'@': 49}, {'@': 50}, {'@': 51}, {'@': 52}, {'@': 53}, {'@': 54}, {'@': 55}, {'@': 56}, {'@': 57}, {'@': 58}, {'@': 59}, {'@': 60}, {'@': 61}, {'@': 62}, {'@': 63}, {'@': 64}, {'@': 65}, {'@': 66}, {'@': 67}, {'@': 68}, {'@': 69}, {'@': 70}, {'@': 71}, {'@': 72}, {'@': 73}, {'@': 74}, {'@': 75}, {'@': 76}, {'@': 77}, {'@': 78}, {'@': 79}, {'@': 80}, {'@': 81}, {'@': 82}, {'@': 83}, {'@': 84}, {'@': 85}, {'@': 86}, {'@': 87}, {'@': 88}, {'@': 89}, {'@': 90}, {'@': 91}, {'@': 92}, {'@': 93}, {'@': 94}, {'@': 95}, {'@': 96}, {'@': 97}, {'@': 98}, {'@': 99}, {'@': 100}, {'@': 101}, {'@': 102}, {'@': 103}, {'@': 104}, {'@': 105}, {'@': 106}, {'@': 107}, {'@': 108}, {'@': 109}, {'@': 110}, {'@': 111}, {'@': 112}, {'@': 113}, {'@': 114}, {'@': 115}, {'@': 116}, {'@': 117}, {'@': 118}, {'@': 119}, {'@': 120}, {'@': 121}, {'@': 122}, {'@': 123}, {'@': 124}, {'@': 125}, {'@': 126}, {'@': 127}, {'@': 128}, {'@': 129}, {'@': 130}, {'@': 131}, {'@': 132}, {'@': 133}, {'@': 134}, {'@': 135}, {'@': 136}, {'@': 137}, {'@': 138}, {'@': 139}, {'@': 140}, {'@': 141}, {'@': 142}, {'@': 143}, {'@': 144}, {'@': 145}, {'@': 146}, {'@': 147}, {'@': 148}, {'@': 149}, {'@': 150}, {'@': 151}, {'@': 152}, {'@': 153}, {'@': 154}, {'@': 155}, {'@': 156}, {'@': 157}, {'@': 158}, {'@': 159}, {'@': 160}, {'@': 161}, {'@': 162}, {'@': 163}, {'@': 164}, {'@': 165}, {'@': 166}, {'@': 167}, {'@': 168}, {'@': 169}, {'@': 170}, {'@': 171}, {'@': 172}, {'@': 173}, {'@': 174}, {'@': 175}, {'@': 176}, {'@': 177}, {'@': 178}, {'@': 179}, {'@': 180}, {'@': 181}, {'@': 182}, {'@': 183}, {'@': 184}, {'@': 185}, {'@': 186}, {'@': 187}, {'@': 188}, {'@': 189}, {'@': 190}, {'@': 191}, {'@': 192}, {'@': 193}, {'@': 194}, {'@': 195}, {'@': 196}, {'@': 197}, {'@': 198}, {'@': 199}, {'@': 200}, {'@': 201}, {'@': 202}, {'@': 203}, {'@': 204}, {'@': 205}, {'@': 206}, {'@': 207}, {'@': 208}, {'@': 209}, {'@': 210}, {'@': 211}, {'@': 212}, {'@': 213}, {'@': 214}, {'@': 215}, {'@': 216}, {'@': 217}, {'@': 218}, {'@': 219}, {'@': 220}, {'@': 221}, {'@': 222}, {'@': 223}, {'@': 224}, {'@': 225}, {'@': 226}, {'@': 227}, {'@': 228}, {'@': 229}, {'@': 230}, {'@': 231}, {'@': 232}, {'@': 233}, {'@': 234}, {'@': 235}, {'@': 236}, {'@': 237}, {'@': 238}, {'@': 239}, {'@': 240}, {'@': 241}, {'@': 242}, {'@': 243}, {'@': 244}, {'@': 245}, {'@': 246}, {'@': 247}, {'@': 248}, {'@': 249}, {'@': 250}, {'@': 251}, {'@': 252}, {'@': 253}, {'@': 254}, {'@': 255}, {'@': 256}, {'@': 257}, {'@': 258}, {'@': 259}, {'@': 260}, {'@': 261}, {'@': 262}, {'@': 263}, {'@': 264}, {'@': 265}, {'@': 266}, {'@': 267}, {'@': 268}, {'@': 269}, {'@': 270}, {'@': 271}, {'@': 272}, {'@': 273}, {'@': 274}, {'@': 275}, {'@': 276}, {'@': 277}, {'@': 278}, {'@': 279}, {'@': 280}, {'@': 281}, {'@': 282}, {'@': 283}, {'@': 284}, {'@': 285}, {'@': 286}, {'@': 287}, {'@': 288}, {'@': 289}, {'@': 290}, {'@': 291}, {'@': 292}, {'@': 293}, {'@': 294}, {'@': 295}, {'@': 296}, {'@': 297}, {'@': 298}, {'@': 299}, {'@': 300}, {'@': 301}, {'@': 302}, {'@': 303}, {'@': 304}, {'@': 305}, {'@': 306}, {'@': 307}, {'@': 308}, {'@': 309}, {'@': 310}, {'@': 311}, {'@': 312}, {'@': 313}, {'@': 314}, {'@': 315}, {'@': 316}, {'@': 317}, {'@': 318}, {'@': 319}, {'@': 320}, {'@': 321}, {'@': 322}, {'@': 323}, {'@': 324}, {'@': 325}, {'@': 326}, {'@': 327}, {'@': 328}, {'@': 329}, {'@': 330}, {'@': 331}, {'@': 332}, {'@': 333}, {'@': 334}, {'@': 335}, {'@': 336}, {'@': 337}, {'@': 338}, {'@': 339}, {'@': 340}, {'@': 341}, {'@': 342}, {'@': 343}, {'@': 344}, {'@': 345}, {'@': 346}, {'@': 347}, {'@': 348}, {'@': 349}, {'@': 350}, {'@': 351}, {'@': 352}, {'@': 353}, {'@': 354}, {'@': 355}, {'@': 356}, {'@': 357}, {'@': 358}, {'@': 359}, {'@': 360}, {'@': 361}, {'@': 362}, {'@': 363}, {'@': 364}, {'@': 365}, {'@': 366}, {'@': 367}, {'@': 368}, {'@': 369}, {'@': 370}, {'@': 371}, {'@': 372}, {'@': 373}, {'@': 374}, {'@': 375}, {'@': 376}, {'@': 377}], 'start': ['start'], 'parser_type': 'lalr', '__type__': 'ParserConf'}, 'parser': {'tokens': {0: 'reg8_i', 1: 'expr_bitwise_operand', 2: 'LB', 3: 'INTEGER', 4: 'R', 5: 'LP', 6: 'expr_unary_operand', 7: 'IXH', 8: 'expr_mul', 9: 'reg8i', 10: 'pexpr', 11: 'I', 12: 'expr_unary', 13: 'MINUS', 14: 'PLUS', 15: 'expr_atom', 16: 'reg8', 17: 'expr_add', 18: 'expr_pow', 19: 'IYL', 20: 'expr_add_operand', 21: 'LPP', 22: 'expr', 23: 'IXL', 24: 'L', 25: 'E', 26: 'B', 27: 'ID', 28: 'ADDR', 29: 'IYH', 30: 'mem_indir', 31: 'H', 32: 'A', 33: 'D', 34: 'expr_mul_operand', 35: 'expr_bitwise', 36: 'reg8_hl', 37: 'reg_bcde', 38: 'C', 39: 'DE', 40: 'CO', 41: 'NEWLINE', 42: 'COMMA', 43: 'jr_flags', 44: 'PE', 45: 'NZ', 46: 'M', 47: 'Z', 48: 'PO', 49: 'jp_flags', 50: 'NC', 51: 'P', 52: 'HL', 53: 'IY', 54: 'reg16', 55: 'inc_reg', 56: 'IX', 57: 'reg16i', 58: 'BC', 59: 'SP', 60: 'number_list', 61: 'STRING', 62: 'LD', 63: 'BIT', 64: 'HALT', 65: 'MUL', 66: 'SET', 67: 'RR', 68: 'ORG', 69: 'LOCAL', 70: 'SWAPNIB', 71: 'ENDP', 72: 'EX', 73: 'SRA', 74: 'RETI', 75: 'OTDR', 76: 'RL', 77: 'BSRA', 78: 'OR', 79: 'TEST', 80: 'DEFS', 81: 'ADC', 82: 'CALL', 83: 'DAA', 84: 'CPL', 85: 'INI', 86: 'LDWS', 87: 'INIR', 88: 'LDDR', 89: 'SCF', 90: 'NOP', 91: '$END', 92: 'LDDRX', 93: 'RRC', 94: 'ALIGN', 95: 'EXX', 96: 'CPDR', 97: 'POP', 98: 'RET', 99: 'BSLA', 100: 'JR', 101: 'SUB', 102: 'DEC', 103: 'DJNZ', 104: 'JP', 105: 'SETAE', 106: 'INC', 107: 'CP', 108: 'OTIR', 109: 'BSRL', 110: 'BSRF', 111: 'INDR', 112: 'XOR', 113: 'CPI', 114: 'IM', 115: 'AND', 116: 'IND', 117: 'SBC', 118: 'LDIR', 119: 'RRCA', 120: 'RRA', 121: 'NAMESPACE', 122: 'RLC', 123: 'NEXTREG', 124: 'LDI', 125: 'PIXELAD', 126: 'ADD', 127: 'END', 128: '_INIT', 129: 'CPIR', 130: 'PIXELDN', 131: 'SLA', 132: 'IN', 133: 'LDD', 134: 'RRD', 135: 'LDIRX', 136: 'RLA', 137: 'EI', 138: 'LDDX', 139: 'RST', 140: 'RLD', 141: 'DEFB', 142: 'RLCA', 143: 'MIRROR', 144: 'LDIX', 145: 'NEG', 146: 'OUTINB', 147: 'SLL', 148: 'BRLC', 149: 'CPD', 150: 'PROC', 151: 'OUTI', 152: 'LDPIRX', 153: 'CCF', 154: 'RES', 155: 'DEFW', 156: 'OUTD', 157: 'OUT', 158: 'RETN', 159: 'PUSH', 160: 'SRL', 161: 'INCBIN', 162: 'DI', 163: 'EQU', 164: 'rotation', 165: 'bitwiseop', 166: 'bitop', 167: 'asm', 168: 'BAND', 169: 'RSHIFT', 170: 'BOR', 171: 'BXOR', 172: 'LSHIFT', 173: 'POW', 174: 'MOD', 175: 'DIV', 176: 'RB', 177: 'AF', 178: 'RP', 179: 'expr_list', 180: 'RPP', 181: 'endline', 182: 'line', 183: 'asms', 184: 'preproc_line', 185: 'expr_pow_operand', 186: 'id_list', 187: 'APO', 188: 'program', 189: 'start'}, 'states': {0: {0: (0, 124), 1: (0, 111), 2: (0, 267), 3: (0, 587), 4: (0, 339), 5: (0, 350), 6: (0, 581), 7: (0, 317), 8: (0, 529), 9: (0, 375), 10: (0, 371), 11: (0, 377), 12: (0, 248), 13: (0, 535), 14: (0, 431), 15: (0, 226), 16: (0, 379), 17: (0, 211), 18: (0, 192), 19: (0, 365), 20: (0, 414), 21: (0, 186), 22: (0, 366), 23: (0, 352), 24: (0, 564), 25: (0, 485), 26: (0, 518), 27: (0, 417), 28: (0, 302), 29: (0, 311), 30: (0, 390), 31: (0, 565), 32: (0, 345), 33: (0, 602), 34: (0, 511), 35: (0, 347), 36: (0, 398), 37: (0, 402), 38: (0, 592)}, 1: {39: (0, 329)}, 2: {40: (1, {'@': 62}), 41: (1, {'@': 62})}, 3: {40: (1, {'@': 217}), 41: (1, {'@': 217})}, 4: {39: (0, 606)}, 5: {40: (1, {'@': 199}), 41: (1, {'@': 199})}, 6: {1: (0, 111), 3: (0, 587), 6: (0, 581), 22: (0, 539), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 519), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 7: {42: (0, 325)}, 8: {40: (1, {'@': 235}), 41: (1, {'@': 235})}, 9: {3: (1, {'@': 295}), 23: (1, {'@': 295}), 19: (1, {'@': 295}), 2: (1, {'@': 295}), 25: (1, {'@': 295}), 31: (1, {'@': 295}), 32: (1, {'@': 295}), 38: (1, {'@': 295}), 27: (1, {'@': 295}), 33: (1, {'@': 295}), 14: (1, {'@': 295}), 5: (1, {'@': 295}), 29: (1, {'@': 295}), 24: (1, {'@': 295}), 7: (1, {'@': 295}), 21: (1, {'@': 295}), 26: (1, {'@': 295}), 28: (1, {'@': 295}), 13: (1, {'@': 295})}, 10: {40: (1, {'@': 237}), 41: (1, {'@': 237})}, 11: {3: (1, {'@': 292}), 23: (1, {'@': 292}), 19: (1, {'@': 292}), 2: (1, {'@': 292}), 25: (1, {'@': 292}), 31: (1, {'@': 292}), 32: (1, {'@': 292}), 38: (1, {'@': 292}), 27: (1, {'@': 292}), 33: (1, {'@': 292}), 14: (1, {'@': 292}), 5: (1, {'@': 292}), 29: (1, {'@': 292}), 24: (1, {'@': 292}), 7: (1, {'@': 292}), 21: (1, {'@': 292}), 26: (1, {'@': 292}), 28: (1, {'@': 292}), 13: (1, {'@': 292})}, 12: {40: (1, {'@': 229}), 41: (1, {'@': 229})}, 13: {24: (1, {'@': 306}), 33: (1, {'@': 306}), 32: (1, {'@': 306}), 2: (1, {'@': 306}), 26: (1, {'@': 306}), 25: (1, {'@': 306}), 38: (1, {'@': 306}), 31: (1, {'@': 306}), 5: (1, {'@': 306})}, 14: {36: (0, 430), 2: (0, 443), 1: (0, 111), 3: (0, 587), 6: (0, 581), 43: (0, 318), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 22: (0, 474), 18: (0, 192), 20: (0, 414), 10: (0, 397), 5: (0, 538), 21: (0, 186), 44: (0, 129), 45: (0, 387), 46: (0, 132), 27: (0, 417), 38: (0, 123), 47: (0, 79), 28: (0, 302), 17: (0, 211), 34: (0, 511), 48: (0, 353), 49: (0, 534), 35: (0, 347), 50: (0, 542), 51: (0, 341)}, 15: {40: (1, {'@': 204}), 41: (1, {'@': 204})}, 16: {2: (0, 537), 5: (0, 590), 52: (0, 541), 7: (0, 317), 53: (0, 577), 19: (0, 365), 54: (0, 399), 38: (0, 592), 55: (0, 505), 9: (0, 451), 23: (0, 352), 24: (0, 564), 25: (0, 485), 26: (0, 518), 56: (0, 463), 36: (0, 300), 29: (0, 311), 32: (0, 238), 16: (0, 437), 0: (0, 400), 31: (0, 565), 39: (0, 470), 57: (0, 558), 58: (0, 524), 33: (0, 602), 37: (0, 402), 59: (0, 449)}, 17: {40: (1, {'@': 65}), 41: (1, {'@': 65})}, 18: {40: (1, {'@': 234}), 41: (1, {'@': 234})}, 19: {40: (1, {'@': 20}), 41: (1, {'@': 20})}, 20: {3: (1, {'@': 296}), 23: (1, {'@': 296}), 19: (1, {'@': 296}), 2: (1, {'@': 296}), 25: (1, {'@': 296}), 31: (1, {'@': 296}), 32: (1, {'@': 296}), 38: (1, {'@': 296}), 27: (1, {'@': 296}), 33: (1, {'@': 296}), 14: (1, {'@': 296}), 5: (1, {'@': 296}), 29: (1, {'@': 296}), 24: (1, {'@': 296}), 7: (1, {'@': 296}), 21: (1, {'@': 296}), 26: (1, {'@': 296}), 28: (1, {'@': 296}), 13: (1, {'@': 296})}, 21: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 60: (0, 204), 12: (0, 248), 14: (0, 431), 15: (0, 226), 22: (0, 493), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 464), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 22: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 22: (0, 471), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 507), 27: (0, 417), 28: (0, 302), 5: (0, 373), 41: (0, 597), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 23: {40: (1, {'@': 196}), 41: (1, {'@': 196})}, 24: {39: (0, 158)}, 25: {3: (1, {'@': 299}), 27: (1, {'@': 299}), 14: (1, {'@': 299}), 21: (1, {'@': 299}), 28: (1, {'@': 299}), 13: (1, {'@': 299}), 5: (1, {'@': 299})}, 26: {40: (1, {'@': 49}), 41: (1, {'@': 49})}, 27: {40: (1, {'@': 223}), 41: (1, {'@': 223})}, 28: {40: (1, {'@': 208}), 41: (1, {'@': 208})}, 29: {40: (1, {'@': 17}), 41: (1, {'@': 17})}, 30: {24: (1, {'@': 300}), 33: (1, {'@': 300}), 32: (1, {'@': 300}), 2: (1, {'@': 300}), 26: (1, {'@': 300}), 25: (1, {'@': 300}), 38: (1, {'@': 300}), 31: (1, {'@': 300}), 5: (1, {'@': 300})}, 31: {24: (1, {'@': 305}), 33: (1, {'@': 305}), 32: (1, {'@': 305}), 2: (1, {'@': 305}), 26: (1, {'@': 305}), 25: (1, {'@': 305}), 38: (1, {'@': 305}), 31: (1, {'@': 305}), 5: (1, {'@': 305})}, 32: {61: (0, 452)}, 33: {40: (1, {'@': 206}), 41: (1, {'@': 206})}, 34: {62: (1, {'@': 8}), 63: (1, {'@': 8}), 64: (1, {'@': 8}), 65: (1, {'@': 8}), 66: (1, {'@': 8}), 67: (1, {'@': 8}), 68: (1, {'@': 8}), 69: (1, {'@': 8}), 70: (1, {'@': 8}), 71: (1, {'@': 8}), 72: (1, {'@': 8}), 73: (1, {'@': 8}), 74: (1, {'@': 8}), 75: (1, {'@': 8}), 76: (1, {'@': 8}), 27: (1, {'@': 8}), 77: (1, {'@': 8}), 78: (1, {'@': 8}), 79: (1, {'@': 8}), 80: (1, {'@': 8}), 81: (1, {'@': 8}), 82: (1, {'@': 8}), 83: (1, {'@': 8}), 84: (1, {'@': 8}), 85: (1, {'@': 8}), 86: (1, {'@': 8}), 87: (1, {'@': 8}), 88: (1, {'@': 8}), 89: (1, {'@': 8}), 90: (1, {'@': 8}), 91: (1, {'@': 8}), 92: (1, {'@': 8}), 93: (1, {'@': 8}), 94: (1, {'@': 8}), 95: (1, {'@': 8}), 96: (1, {'@': 8}), 97: (1, {'@': 8}), 98: (1, {'@': 8}), 99: (1, {'@': 8}), 100: (1, {'@': 8}), 101: (1, {'@': 8}), 102: (1, {'@': 8}), 40: (1, {'@': 8}), 103: (1, {'@': 8}), 104: (1, {'@': 8}), 105: (1, {'@': 8}), 106: (1, {'@': 8}), 107: (1, {'@': 8}), 108: (1, {'@': 8}), 109: (1, {'@': 8}), 110: (1, {'@': 8}), 111: (1, {'@': 8}), 112: (1, {'@': 8}), 113: (1, {'@': 8}), 3: (1, {'@': 8}), 114: (1, {'@': 8}), 115: (1, {'@': 8}), 116: (1, {'@': 8}), 117: (1, {'@': 8}), 118: (1, {'@': 8}), 119: (1, {'@': 8}), 120: (1, {'@': 8}), 121: (1, {'@': 8}), 122: (1, {'@': 8}), 123: (1, {'@': 8}), 124: (1, {'@': 8}), 125: (1, {'@': 8}), 126: (1, {'@': 8}), 127: (1, {'@': 8}), 128: (1, {'@': 8}), 129: (1, {'@': 8}), 130: (1, {'@': 8}), 131: (1, {'@': 8}), 132: (1, {'@': 8}), 133: (1, {'@': 8}), 134: (1, {'@': 8}), 135: (1, {'@': 8}), 136: (1, {'@': 8}), 137: (1, {'@': 8}), 138: (1, {'@': 8}), 139: (1, {'@': 8}), 140: (1, {'@': 8}), 141: (1, {'@': 8}), 142: (1, {'@': 8}), 143: (1, {'@': 8}), 144: (1, {'@': 8}), 145: (1, {'@': 8}), 146: (1, {'@': 8}), 147: (1, {'@': 8}), 148: (1, {'@': 8}), 149: (1, {'@': 8}), 41: (1, {'@': 8}), 150: (1, {'@': 8}), 151: (1, {'@': 8}), 152: (1, {'@': 8}), 153: (1, {'@': 8}), 154: (1, {'@': 8}), 155: (1, {'@': 8}), 156: (1, {'@': 8}), 157: (1, {'@': 8}), 158: (1, {'@': 8}), 159: (1, {'@': 8}), 160: (1, {'@': 8}), 161: (1, {'@': 8}), 162: (1, {'@': 8})}, 35: {40: (1, {'@': 203}), 41: (1, {'@': 203})}, 36: {40: (1, {'@': 215}), 41: (1, {'@': 215})}, 37: {40: (1, {'@': 238}), 41: (1, {'@': 238})}, 38: {33: (0, 508)}, 39: {40: (1, {'@': 202}), 41: (1, {'@': 202})}, 40: {61: (0, 496)}, 41: {}, 42: {40: (1, {'@': 193}), 41: (1, {'@': 193})}, 43: {40: (1, {'@': 233}), 41: (1, {'@': 233})}, 44: {163: (0, 148), 40: (1, {'@': 19}), 41: (1, {'@': 19})}, 45: {40: (1, {'@': 216}), 41: (1, {'@': 216})}, 46: {40: (1, {'@': 220}), 41: (1, {'@': 220})}, 47: {40: (1, {'@': 228}), 41: (1, {'@': 228})}, 48: {3: (1, {'@': 294}), 23: (1, {'@': 294}), 19: (1, {'@': 294}), 2: (1, {'@': 294}), 25: (1, {'@': 294}), 31: (1, {'@': 294}), 32: (1, {'@': 294}), 38: (1, {'@': 294}), 27: (1, {'@': 294}), 33: (1, {'@': 294}), 14: (1, {'@': 294}), 5: (1, {'@': 294}), 29: (1, {'@': 294}), 24: (1, {'@': 294}), 7: (1, {'@': 294}), 21: (1, {'@': 294}), 26: (1, {'@': 294}), 28: (1, {'@': 294}), 13: (1, {'@': 294})}, 49: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 60: (0, 490), 12: (0, 248), 14: (0, 431), 15: (0, 226), 22: (0, 493), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 464), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 50: {40: (1, {'@': 210}), 41: (1, {'@': 210})}, 51: {40: (1, {'@': 64}), 41: (1, {'@': 64})}, 52: {39: (0, 7)}, 53: {40: (1, {'@': 209}), 41: (1, {'@': 209})}, 54: {40: (1, {'@': 219}), 41: (1, {'@': 219})}, 55: {40: (1, {'@': 224}), 41: (1, {'@': 224})}, 56: {62: (0, 556), 164: (0, 460), 121: (0, 499), 117: (0, 472), 100: (0, 78), 123: (0, 276), 74: (0, 54), 126: (0, 58), 165: (0, 469), 92: (0, 551), 166: (0, 548), 97: (0, 334), 154: (0, 309), 157: (0, 446), 160: (0, 514), 108: (0, 523), 139: (0, 461), 142: (0, 244), 143: (0, 386), 136: (0, 155), 79: (0, 284), 132: (0, 532), 135: (0, 448), 114: (0, 344), 93: (0, 185), 116: (0, 161), 81: (0, 107), 88: (0, 143), 159: (0, 322), 150: (0, 83), 89: (0, 252), 122: (0, 427), 69: (0, 441), 145: (0, 166), 129: (0, 210), 109: (0, 235), 82: (0, 259), 101: (0, 9), 77: (0, 1), 95: (0, 42), 104: (0, 14), 102: (0, 75), 113: (0, 97), 64: (0, 120), 131: (0, 86), 72: (0, 90), 151: (0, 94), 141: (0, 103), 103: (0, 115), 153: (0, 67), 147: (0, 31), 68: (0, 6), 138: (0, 57), 140: (0, 64), 105: (0, 62), 111: (0, 45), 94: (0, 61), 137: (0, 28), 85: (0, 36), 99: (0, 52), 107: (0, 20), 87: (0, 3), 86: (0, 12), 80: (0, 21), 167: (0, 408), 83: (0, 15), 110: (0, 4), 70: (0, 8), 133: (0, 5), 130: (0, 10), 73: (0, 13), 124: (0, 66), 148: (0, 24), 125: (0, 37), 156: (0, 50), 119: (0, 55), 98: (0, 65), 112: (0, 48), 158: (0, 46), 155: (0, 49), 162: (0, 53), 78: (0, 11), 106: (0, 16), 76: (0, 63), 146: (0, 18), 3: (0, 19), 118: (0, 23), 66: (0, 25), 71: (0, 26), 120: (0, 27), 67: (0, 30), 161: (0, 32), 84: (0, 33), 149: (0, 35), 27: (0, 237), 65: (0, 38), 41: (0, 240), 96: (0, 39), 144: (0, 47), 152: (0, 43), 63: (0, 175), 75: (0, 473), 134: (0, 285), 90: (0, 457), 115: (0, 411)}, 57: {40: (1, {'@': 231}), 41: (1, {'@': 231})}, 58: {52: (0, 80), 32: (0, 580), 39: (0, 98), 53: (0, 577), 57: (0, 294), 58: (0, 530), 56: (0, 463)}, 59: {40: (0, 56), 41: (0, 512)}, 60: {3: (1, {'@': 14}), 62: (1, {'@': 14}), 63: (1, {'@': 14}), 114: (1, {'@': 14}), 64: (1, {'@': 14}), 65: (1, {'@': 14}), 115: (1, {'@': 14}), 66: (1, {'@': 14}), 67: (1, {'@': 14}), 68: (1, {'@': 14}), 69: (1, {'@': 14}), 70: (1, {'@': 14}), 71: (1, {'@': 14}), 72: (1, {'@': 14}), 116: (1, {'@': 14}), 117: (1, {'@': 14}), 73: (1, {'@': 14}), 118: (1, {'@': 14}), 119: (1, {'@': 14}), 120: (1, {'@': 14}), 74: (1, {'@': 14}), 75: (1, {'@': 14}), 121: (1, {'@': 14}), 122: (1, {'@': 14}), 123: (1, {'@': 14}), 76: (1, {'@': 14}), 124: (1, {'@': 14}), 125: (1, {'@': 14}), 126: (1, {'@': 14}), 127: (1, {'@': 14}), 27: (1, {'@': 14}), 128: (1, {'@': 14}), 77: (1, {'@': 14}), 129: (1, {'@': 14}), 78: (1, {'@': 14}), 130: (1, {'@': 14}), 131: (1, {'@': 14}), 79: (1, {'@': 14}), 80: (1, {'@': 14}), 81: (1, {'@': 14}), 82: (1, {'@': 14}), 83: (1, {'@': 14}), 132: (1, {'@': 14}), 133: (1, {'@': 14}), 134: (1, {'@': 14}), 84: (1, {'@': 14}), 85: (1, {'@': 14}), 161: (1, {'@': 14}), 135: (1, {'@': 14}), 136: (1, {'@': 14}), 86: (1, {'@': 14}), 87: (1, {'@': 14}), 137: (1, {'@': 14}), 88: (1, {'@': 14}), 138: (1, {'@': 14}), 89: (1, {'@': 14}), 90: (1, {'@': 14}), 91: (1, {'@': 14}), 92: (1, {'@': 14}), 93: (1, {'@': 14}), 139: (1, {'@': 14}), 140: (1, {'@': 14}), 94: (1, {'@': 14}), 141: (1, {'@': 14}), 95: (1, {'@': 14}), 142: (1, {'@': 14}), 96: (1, {'@': 14}), 143: (1, {'@': 14}), 144: (1, {'@': 14}), 145: (1, {'@': 14}), 146: (1, {'@': 14}), 147: (1, {'@': 14}), 97: (1, {'@': 14}), 98: (1, {'@': 14}), 148: (1, {'@': 14}), 99: (1, {'@': 14}), 149: (1, {'@': 14}), 41: (1, {'@': 14}), 150: (1, {'@': 14}), 151: (1, {'@': 14}), 152: (1, {'@': 14}), 100: (1, {'@': 14}), 101: (1, {'@': 14}), 102: (1, {'@': 14}), 40: (1, {'@': 14}), 153: (1, {'@': 14}), 103: (1, {'@': 14}), 154: (1, {'@': 14}), 104: (1, {'@': 14}), 105: (1, {'@': 14}), 155: (1, {'@': 14}), 106: (1, {'@': 14}), 107: (1, {'@': 14}), 156: (1, {'@': 14}), 157: (1, {'@': 14}), 158: (1, {'@': 14}), 108: (1, {'@': 14}), 109: (1, {'@': 14}), 110: (1, {'@': 14}), 111: (1, {'@': 14}), 159: (1, {'@': 14}), 160: (1, {'@': 14}), 112: (1, {'@': 14}), 162: (1, {'@': 14}), 113: (1, {'@': 14})}, 61: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 361), 22: (0, 239), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 62: {40: (1, {'@': 239}), 41: (1, {'@': 239})}, 63: {24: (1, {'@': 301}), 33: (1, {'@': 301}), 32: (1, {'@': 301}), 2: (1, {'@': 301}), 26: (1, {'@': 301}), 25: (1, {'@': 301}), 38: (1, {'@': 301}), 31: (1, {'@': 301}), 5: (1, {'@': 301})}, 64: {40: (1, {'@': 225}), 41: (1, {'@': 225})}, 65: {51: (0, 341), 38: (0, 123), 45: (0, 387), 48: (0, 353), 47: (0, 79), 49: (0, 72), 44: (0, 129), 50: (0, 542), 43: (0, 318), 46: (0, 132), 40: (1, {'@': 218}), 41: (1, {'@': 218})}, 66: {40: (1, {'@': 197}), 41: (1, {'@': 197})}, 67: {40: (1, {'@': 194}), 41: (1, {'@': 194})}, 68: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 253), 27: (0, 417), 38: (0, 105), 28: (0, 302), 5: (0, 373), 22: (0, 269), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 69: {40: (1, {'@': 147}), 41: (1, {'@': 147}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 13: (1, {'@': 344}), 14: (1, {'@': 344}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346})}, 70: {176: (0, 563)}, 71: {42: (0, 0)}, 72: {40: (1, {'@': 167}), 41: (1, {'@': 167})}, 73: {53: (0, 577), 57: (0, 255), 56: (0, 463), 52: (0, 265)}, 74: {40: (1, {'@': 84}), 41: (1, {'@': 84})}, 75: {2: (0, 537), 5: (0, 590), 52: (0, 541), 7: (0, 317), 0: (0, 324), 53: (0, 577), 19: (0, 365), 54: (0, 399), 9: (0, 451), 23: (0, 352), 24: (0, 564), 25: (0, 485), 26: (0, 518), 56: (0, 463), 36: (0, 300), 29: (0, 311), 32: (0, 238), 16: (0, 437), 31: (0, 565), 39: (0, 470), 33: (0, 602), 57: (0, 558), 58: (0, 524), 55: (0, 76), 37: (0, 402), 38: (0, 592), 59: (0, 449)}, 76: {40: (1, {'@': 76}), 41: (1, {'@': 76})}, 77: {40: (1, {'@': 37}), 41: (1, {'@': 37})}, 78: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 22: (0, 554), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 45: (0, 387), 21: (0, 186), 10: (0, 147), 43: (0, 140), 38: (0, 123), 27: (0, 417), 47: (0, 79), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347), 50: (0, 542)}, 79: {42: (1, {'@': 336}), 40: (1, {'@': 336}), 41: (1, {'@': 336})}, 80: {42: (0, 444)}, 81: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 253), 27: (0, 417), 28: (0, 302), 5: (0, 373), 22: (0, 269), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 82: {40: (1, {'@': 21}), 41: (1, {'@': 21})}, 83: {40: (1, {'@': 48}), 41: (1, {'@': 48})}, 84: {41: (0, 491)}, 85: {40: (1, {'@': 79}), 41: (1, {'@': 79})}, 86: {24: (1, {'@': 304}), 33: (1, {'@': 304}), 32: (1, {'@': 304}), 2: (1, {'@': 304}), 26: (1, {'@': 304}), 25: (1, {'@': 304}), 38: (1, {'@': 304}), 31: (1, {'@': 304}), 5: (1, {'@': 304})}, 87: {42: (0, 582)}, 88: {40: (1, {'@': 56}), 41: (1, {'@': 56})}, 89: {53: (0, 577), 52: (0, 243), 56: (0, 463), 57: (0, 251)}, 90: {5: (0, 420), 39: (0, 555), 177: (0, 435), 2: (0, 515)}, 91: {40: (1, {'@': 150}), 41: (1, {'@': 150})}, 92: {40: (1, {'@': 27}), 41: (1, {'@': 27})}, 93: {176: (0, 219)}, 94: {40: (1, {'@': 211}), 41: (1, {'@': 211})}, 95: {40: (1, {'@': 22}), 41: (1, {'@': 22})}, 96: {178: (0, 231)}, 97: {40: (1, {'@': 201}), 41: (1, {'@': 201})}, 98: {42: (0, 557)}, 99: {41: (0, 487), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 100: {40: (1, {'@': 82}), 41: (1, {'@': 82})}, 101: {40: (1, {'@': 28}), 41: (1, {'@': 28})}, 102: {176: (0, 234)}, 103: {1: (0, 111), 3: (0, 587), 179: (0, 500), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 61: (0, 497), 10: (0, 525), 27: (0, 417), 28: (0, 302), 5: (0, 373), 22: (0, 574), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 104: {1: (0, 111), 9: (0, 167), 37: (0, 402), 3: (0, 587), 2: (0, 537), 32: (0, 170), 6: (0, 581), 5: (0, 263), 7: (0, 317), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 19: (0, 365), 21: (0, 186), 38: (0, 592), 23: (0, 352), 10: (0, 173), 24: (0, 564), 25: (0, 485), 0: (0, 178), 26: (0, 518), 16: (0, 180), 27: (0, 417), 28: (0, 302), 29: (0, 311), 22: (0, 182), 31: (0, 565), 34: (0, 511), 36: (0, 183), 33: (0, 602), 20: (0, 414), 35: (0, 347)}, 105: {176: (0, 337)}, 106: {40: (1, {'@': 55}), 41: (1, {'@': 55})}, 107: {32: (0, 307), 52: (0, 260)}, 108: {1: (0, 111), 3: (0, 587), 32: (0, 254), 6: (0, 581), 14: (0, 431), 8: (0, 529), 13: (0, 535), 12: (0, 248), 15: (0, 226), 17: (0, 211), 22: (0, 258), 18: (0, 192), 21: (0, 186), 10: (0, 262), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 109: {178: (0, 367)}, 110: {40: (1, {'@': 86}), 41: (1, {'@': 86})}, 111: {171: (0, 112), 168: (0, 568), 172: (0, 571), 169: (0, 162), 170: (0, 501)}, 112: {3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 18: (0, 192), 21: (0, 186), 20: (0, 150), 10: (0, 121), 27: (0, 417), 28: (0, 302), 17: (0, 116), 5: (0, 373), 34: (0, 511)}, 113: {40: (1, {'@': 54}), 41: (1, {'@': 54})}, 114: {40: (1, {'@': 282}), 41: (1, {'@': 282}), 42: (1, {'@': 282})}, 115: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 22: (0, 547), 10: (0, 593), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 116: {168: (1, {'@': 343}), 178: (1, {'@': 343}), 176: (1, {'@': 343}), 40: (1, {'@': 343}), 14: (1, {'@': 343}), 170: (1, {'@': 343}), 172: (1, {'@': 343}), 42: (1, {'@': 343}), 180: (1, {'@': 343}), 169: (1, {'@': 343}), 171: (1, {'@': 343}), 41: (1, {'@': 343}), 13: (1, {'@': 343})}, 117: {40: (1, {'@': 283}), 41: (1, {'@': 283}), 42: (1, {'@': 283})}, 118: {40: (1, {'@': 129}), 41: (1, {'@': 129})}, 119: {174: (1, {'@': 349}), 168: (1, {'@': 349}), 178: (1, {'@': 349}), 176: (1, {'@': 349}), 40: (1, {'@': 349}), 14: (1, {'@': 349}), 170: (1, {'@': 349}), 65: (1, {'@': 349}), 172: (1, {'@': 349}), 42: (1, {'@': 349}), 180: (1, {'@': 349}), 169: (1, {'@': 349}), 171: (1, {'@': 349}), 41: (1, {'@': 349}), 13: (1, {'@': 349}), 175: (1, {'@': 349})}, 120: {40: (1, {'@': 207}), 41: (1, {'@': 207})}, 121: {173: (1, {'@': 348}), 168: (1, {'@': 344}), 178: (1, {'@': 344}), 176: (1, {'@': 344}), 40: (1, {'@': 344}), 14: (1, {'@': 344}), 170: (1, {'@': 344}), 172: (1, {'@': 344}), 42: (1, {'@': 344}), 180: (1, {'@': 344}), 169: (1, {'@': 344}), 171: (1, {'@': 344}), 41: (1, {'@': 344}), 13: (1, {'@': 344}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346})}, 122: {40: (1, {'@': 285}), 41: (1, {'@': 285}), 42: (1, {'@': 285})}, 123: {42: (1, {'@': 337}), 40: (1, {'@': 337}), 41: (1, {'@': 337})}, 124: {40: (1, {'@': 57}), 41: (1, {'@': 57})}, 125: {40: (1, {'@': 38}), 41: (1, {'@': 38})}, 126: {91: (1, {'@': 6}), 41: (1, {'@': 6})}, 127: {40: (1, {'@': 36}), 41: (1, {'@': 36})}, 128: {40: (1, {'@': 284}), 41: (1, {'@': 284}), 42: (1, {'@': 284})}, 129: {42: (1, {'@': 334}), 40: (1, {'@': 334}), 41: (1, {'@': 334})}, 130: {52: (0, 476)}, 131: {40: (1, {'@': 85}), 41: (1, {'@': 85})}, 132: {42: (1, {'@': 332}), 40: (1, {'@': 332}), 41: (1, {'@': 332})}, 133: {40: (1, {'@': 39}), 41: (1, {'@': 39})}, 134: {40: (1, {'@': 44}), 41: (1, {'@': 44})}, 135: {26: (0, 513)}, 136: {40: (1, {'@': 81}), 41: (1, {'@': 81})}, 137: {39: (0, 215), 52: (0, 201), 58: (0, 217), 59: (0, 160), 53: (0, 577), 57: (0, 241), 56: (0, 463)}, 138: {40: (1, {'@': 146}), 41: (1, {'@': 146}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 139: {40: (1, {'@': 41}), 41: (1, {'@': 41})}, 140: {42: (0, 438)}, 141: {26: (0, 509)}, 142: {40: (1, {'@': 149}), 41: (1, {'@': 149})}, 143: {40: (1, {'@': 198}), 41: (1, {'@': 198})}, 144: {40: (1, {'@': 25}), 41: (1, {'@': 25})}, 145: {174: (1, {'@': 365}), 168: (1, {'@': 365}), 40: (1, {'@': 365}), 14: (1, {'@': 365}), 169: (1, {'@': 365}), 170: (1, {'@': 365}), 171: (1, {'@': 365}), 65: (1, {'@': 365}), 172: (1, {'@': 365}), 41: (1, {'@': 365}), 13: (1, {'@': 365}), 175: (1, {'@': 365}), 42: (1, {'@': 365}), 176: (1, {'@': 365}), 178: (1, {'@': 365}), 180: (1, {'@': 365})}, 146: {40: (1, {'@': 43}), 41: (1, {'@': 43})}, 147: {40: (1, {'@': 175}), 41: (1, {'@': 175}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 148: {1: (0, 111), 22: (0, 84), 3: (0, 587), 6: (0, 581), 10: (0, 99), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 149: {173: (1, {'@': 348}), 174: (1, {'@': 346}), 168: (1, {'@': 346}), 178: (1, {'@': 346}), 176: (1, {'@': 346}), 40: (1, {'@': 346}), 14: (1, {'@': 346}), 170: (1, {'@': 346}), 65: (1, {'@': 346}), 172: (1, {'@': 346}), 42: (1, {'@': 346}), 180: (1, {'@': 346}), 169: (1, {'@': 346}), 171: (1, {'@': 346}), 41: (1, {'@': 346}), 13: (1, {'@': 346}), 175: (1, {'@': 346})}, 150: {14: (0, 594), 13: (0, 389), 168: (1, {'@': 356}), 41: (1, {'@': 356}), 40: (1, {'@': 356}), 169: (1, {'@': 356}), 170: (1, {'@': 356}), 171: (1, {'@': 356}), 172: (1, {'@': 356}), 42: (1, {'@': 356}), 176: (1, {'@': 356}), 180: (1, {'@': 356}), 178: (1, {'@': 356})}, 151: {40: (1, {'@': 40}), 41: (1, {'@': 40})}, 152: {41: (0, 357), 91: (1, {'@': 1})}, 153: {174: (1, {'@': 372}), 168: (1, {'@': 372}), 40: (1, {'@': 372}), 14: (1, {'@': 372}), 173: (1, {'@': 372}), 169: (1, {'@': 372}), 170: (1, {'@': 372}), 171: (1, {'@': 372}), 65: (1, {'@': 372}), 172: (1, {'@': 372}), 41: (1, {'@': 372}), 13: (1, {'@': 372}), 175: (1, {'@': 372}), 42: (1, {'@': 372}), 178: (1, {'@': 372}), 176: (1, {'@': 372}), 180: (1, {'@': 372})}, 154: {174: (1, {'@': 363}), 168: (1, {'@': 363}), 40: (1, {'@': 363}), 14: (1, {'@': 363}), 169: (1, {'@': 363}), 170: (1, {'@': 363}), 171: (1, {'@': 363}), 65: (1, {'@': 363}), 172: (1, {'@': 363}), 41: (1, {'@': 363}), 13: (1, {'@': 363}), 175: (1, {'@': 363}), 42: (1, {'@': 363}), 176: (1, {'@': 363}), 178: (1, {'@': 363}), 180: (1, {'@': 363})}, 155: {40: (1, {'@': 221}), 41: (1, {'@': 221})}, 156: {174: (1, {'@': 376}), 168: (1, {'@': 376}), 40: (1, {'@': 376}), 14: (1, {'@': 376}), 173: (1, {'@': 376}), 169: (1, {'@': 376}), 170: (1, {'@': 376}), 171: (1, {'@': 376}), 65: (1, {'@': 376}), 172: (1, {'@': 376}), 41: (1, {'@': 376}), 13: (1, {'@': 376}), 175: (1, {'@': 376}), 42: (1, {'@': 376}), 176: (1, {'@': 376}), 178: (1, {'@': 376}), 180: (1, {'@': 376})}, 157: {40: (1, {'@': 42}), 41: (1, {'@': 42})}, 158: {42: (0, 141)}, 159: {40: (1, {'@': 95}), 41: (1, {'@': 95})}, 160: {40: (1, {'@': 117}), 41: (1, {'@': 117})}, 161: {40: (1, {'@': 214}), 41: (1, {'@': 214})}, 162: {3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 20: (0, 195), 18: (0, 192), 21: (0, 186), 10: (0, 121), 27: (0, 417), 28: (0, 302), 17: (0, 116), 5: (0, 373), 34: (0, 511)}, 163: {173: (1, {'@': 348}), 174: (1, {'@': 350}), 168: (1, {'@': 350}), 178: (1, {'@': 350}), 176: (1, {'@': 350}), 40: (1, {'@': 350}), 14: (1, {'@': 350}), 170: (1, {'@': 350}), 65: (1, {'@': 350}), 172: (1, {'@': 350}), 42: (1, {'@': 350}), 180: (1, {'@': 350}), 169: (1, {'@': 350}), 171: (1, {'@': 350}), 41: (1, {'@': 350}), 13: (1, {'@': 350}), 175: (1, {'@': 350})}, 164: {40: (1, {'@': 291}), 41: (1, {'@': 291}), 42: (1, {'@': 291})}, 165: {178: (0, 480)}, 166: {40: (1, {'@': 205}), 41: (1, {'@': 205})}, 167: {40: (1, {'@': 91}), 41: (1, {'@': 91})}, 168: {40: (1, {'@': 290}), 41: (1, {'@': 290}), 42: (1, {'@': 290})}, 169: {27: (0, 417), 3: (0, 587), 21: (0, 186), 12: (0, 486), 28: (0, 302), 15: (0, 226), 10: (0, 540), 5: (0, 373), 13: (0, 535), 14: (0, 431)}, 170: {40: (1, {'@': 92}), 41: (1, {'@': 92})}, 171: {42: (0, 233)}, 172: {40: (1, {'@': 143}), 41: (1, {'@': 143})}, 173: {40: (1, {'@': 120}), 41: (1, {'@': 120}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 174: {40: (1, {'@': 289}), 41: (1, {'@': 289}), 42: (1, {'@': 289})}, 175: {3: (1, {'@': 297}), 27: (1, {'@': 297}), 14: (1, {'@': 297}), 21: (1, {'@': 297}), 28: (1, {'@': 297}), 13: (1, {'@': 297}), 5: (1, {'@': 297})}, 176: {42: (0, 223)}, 177: {40: (1, {'@': 139}), 41: (1, {'@': 139})}, 178: {40: (1, {'@': 125}), 41: (1, {'@': 125})}, 179: {40: (1, {'@': 80}), 41: (1, {'@': 80}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 13: (1, {'@': 344}), 14: (1, {'@': 344}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346})}, 180: {40: (1, {'@': 90}), 41: (1, {'@': 90})}, 181: {40: (1, {'@': 288}), 41: (1, {'@': 288}), 42: (1, {'@': 288})}, 182: {40: (1, {'@': 119}), 41: (1, {'@': 119})}, 183: {40: (1, {'@': 93}), 41: (1, {'@': 93})}, 184: {40: (1, {'@': 45}), 41: (1, {'@': 45})}, 185: {24: (1, {'@': 302}), 33: (1, {'@': 302}), 32: (1, {'@': 302}), 2: (1, {'@': 302}), 26: (1, {'@': 302}), 25: (1, {'@': 302}), 38: (1, {'@': 302}), 31: (1, {'@': 302}), 5: (1, {'@': 302})}, 186: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 253), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347), 22: (0, 418)}, 187: {174: (1, {'@': 373}), 168: (1, {'@': 373}), 40: (1, {'@': 373}), 14: (1, {'@': 373}), 173: (1, {'@': 373}), 169: (1, {'@': 373}), 170: (1, {'@': 373}), 171: (1, {'@': 373}), 65: (1, {'@': 373}), 172: (1, {'@': 373}), 41: (1, {'@': 373}), 13: (1, {'@': 373}), 175: (1, {'@': 373}), 42: (1, {'@': 373}), 178: (1, {'@': 373}), 176: (1, {'@': 373}), 180: (1, {'@': 373})}, 188: {14: (0, 594), 13: (0, 389), 168: (1, {'@': 354}), 41: (1, {'@': 354}), 40: (1, {'@': 354}), 169: (1, {'@': 354}), 170: (1, {'@': 354}), 171: (1, {'@': 354}), 172: (1, {'@': 354}), 42: (1, {'@': 354}), 176: (1, {'@': 354}), 180: (1, {'@': 354}), 178: (1, {'@': 354})}, 189: {91: (1, {'@': 7}), 41: (1, {'@': 7})}, 190: {14: (0, 594), 13: (0, 389), 168: (1, {'@': 353}), 41: (1, {'@': 353}), 40: (1, {'@': 353}), 169: (1, {'@': 353}), 170: (1, {'@': 353}), 171: (1, {'@': 353}), 172: (1, {'@': 353}), 42: (1, {'@': 353}), 176: (1, {'@': 353}), 180: (1, {'@': 353}), 178: (1, {'@': 353})}, 191: {40: (1, {'@': 47}), 41: (1, {'@': 47})}, 192: {174: (1, {'@': 364}), 168: (1, {'@': 364}), 40: (1, {'@': 364}), 14: (1, {'@': 364}), 169: (1, {'@': 364}), 170: (1, {'@': 364}), 171: (1, {'@': 364}), 65: (1, {'@': 364}), 172: (1, {'@': 364}), 41: (1, {'@': 364}), 13: (1, {'@': 364}), 175: (1, {'@': 364}), 42: (1, {'@': 364}), 176: (1, {'@': 364}), 178: (1, {'@': 364}), 180: (1, {'@': 364})}, 193: {40: (1, {'@': 178}), 41: (1, {'@': 178})}, 194: {27: (0, 404)}, 195: {14: (0, 594), 13: (0, 389), 168: (1, {'@': 352}), 41: (1, {'@': 352}), 40: (1, {'@': 352}), 169: (1, {'@': 352}), 170: (1, {'@': 352}), 171: (1, {'@': 352}), 172: (1, {'@': 352}), 42: (1, {'@': 352}), 176: (1, {'@': 352}), 180: (1, {'@': 352}), 178: (1, {'@': 352})}, 196: {14: (0, 594), 13: (0, 389), 168: (1, {'@': 355}), 41: (1, {'@': 355}), 40: (1, {'@': 355}), 169: (1, {'@': 355}), 170: (1, {'@': 355}), 171: (1, {'@': 355}), 172: (1, {'@': 355}), 42: (1, {'@': 355}), 176: (1, {'@': 355}), 180: (1, {'@': 355}), 178: (1, {'@': 355})}, 197: {40: (1, {'@': 187}), 41: (1, {'@': 187})}, 198: {40: (1, {'@': 179}), 41: (1, {'@': 179})}, 199: {174: (0, 355), 65: (0, 426), 175: (0, 412), 168: (1, {'@': 358}), 40: (1, {'@': 358}), 14: (1, {'@': 358}), 169: (1, {'@': 358}), 170: (1, {'@': 358}), 171: (1, {'@': 358}), 172: (1, {'@': 358}), 41: (1, {'@': 358}), 13: (1, {'@': 358}), 42: (1, {'@': 358}), 176: (1, {'@': 358}), 178: (1, {'@': 358}), 180: (1, {'@': 358})}, 200: {40: (1, {'@': 340}), 41: (1, {'@': 340}), 42: (1, {'@': 340})}, 201: {40: (1, {'@': 116}), 41: (1, {'@': 116})}, 202: {40: (1, {'@': 185}), 41: (1, {'@': 185})}, 203: {40: (1, {'@': 131}), 41: (1, {'@': 131})}, 204: {42: (0, 288), 40: (1, {'@': 52}), 41: (1, {'@': 52})}, 205: {174: (1, {'@': 361}), 168: (1, {'@': 361}), 40: (1, {'@': 361}), 14: (1, {'@': 361}), 169: (1, {'@': 361}), 170: (1, {'@': 361}), 171: (1, {'@': 361}), 65: (1, {'@': 361}), 172: (1, {'@': 361}), 41: (1, {'@': 361}), 13: (1, {'@': 361}), 175: (1, {'@': 361}), 42: (1, {'@': 361}), 176: (1, {'@': 361}), 178: (1, {'@': 361}), 180: (1, {'@': 361})}, 206: {40: (1, {'@': 58}), 41: (1, {'@': 58})}, 207: {176: (0, 550)}, 208: {40: (1, {'@': 161}), 41: (1, {'@': 161})}, 209: {40: (1, {'@': 184}), 41: (1, {'@': 184})}, 210: {40: (1, {'@': 200}), 41: (1, {'@': 200})}, 211: {13: (1, {'@': 343}), 14: (1, {'@': 343}), 168: (1, {'@': 357}), 41: (1, {'@': 357}), 40: (1, {'@': 357}), 169: (1, {'@': 357}), 170: (1, {'@': 357}), 171: (1, {'@': 357}), 172: (1, {'@': 357}), 42: (1, {'@': 357}), 176: (1, {'@': 357}), 180: (1, {'@': 357}), 178: (1, {'@': 357})}, 212: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 22: (0, 531), 18: (0, 192), 21: (0, 186), 10: (0, 253), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 213: {174: (1, {'@': 362}), 168: (1, {'@': 362}), 40: (1, {'@': 362}), 14: (1, {'@': 362}), 169: (1, {'@': 362}), 170: (1, {'@': 362}), 171: (1, {'@': 362}), 65: (1, {'@': 362}), 172: (1, {'@': 362}), 41: (1, {'@': 362}), 13: (1, {'@': 362}), 175: (1, {'@': 362}), 42: (1, {'@': 362}), 176: (1, {'@': 362}), 178: (1, {'@': 362}), 180: (1, {'@': 362})}, 214: {27: (0, 417), 3: (0, 587), 10: (0, 528), 21: (0, 186), 28: (0, 302), 15: (0, 226), 12: (0, 456), 5: (0, 373), 13: (0, 535), 14: (0, 431)}, 215: {40: (1, {'@': 115}), 41: (1, {'@': 115})}, 216: {40: (1, {'@': 186}), 41: (1, {'@': 186})}, 217: {40: (1, {'@': 114}), 41: (1, {'@': 114})}, 218: {40: (1, {'@': 168}), 41: (1, {'@': 168})}, 219: {40: (1, {'@': 181}), 41: (1, {'@': 181})}, 220: {42: (0, 562), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 221: {40: (1, {'@': 169}), 41: (1, {'@': 169}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 222: {42: (0, 595)}, 223: {2: (0, 382), 30: (0, 391), 5: (0, 392), 10: (0, 401)}, 224: {176: (0, 453)}, 225: {42: (0, 560)}, 226: {174: (1, {'@': 371}), 168: (1, {'@': 371}), 40: (1, {'@': 371}), 14: (1, {'@': 371}), 173: (1, {'@': 371}), 169: (1, {'@': 371}), 170: (1, {'@': 371}), 171: (1, {'@': 371}), 65: (1, {'@': 371}), 172: (1, {'@': 371}), 41: (1, {'@': 371}), 13: (1, {'@': 371}), 175: (1, {'@': 371}), 42: (1, {'@': 371}), 176: (1, {'@': 371}), 178: (1, {'@': 371}), 180: (1, {'@': 371})}, 227: {42: (0, 249)}, 228: {57: (0, 558), 58: (0, 524), 54: (0, 172), 52: (0, 541), 32: (0, 381), 53: (0, 577), 59: (0, 297), 56: (0, 463), 39: (0, 470)}, 229: {174: (1, {'@': 345}), 168: (1, {'@': 345}), 178: (1, {'@': 345}), 176: (1, {'@': 345}), 40: (1, {'@': 345}), 14: (1, {'@': 345}), 170: (1, {'@': 345}), 65: (1, {'@': 345}), 172: (1, {'@': 345}), 42: (1, {'@': 345}), 180: (1, {'@': 345}), 169: (1, {'@': 345}), 171: (1, {'@': 345}), 41: (1, {'@': 345}), 13: (1, {'@': 345}), 175: (1, {'@': 345})}, 230: {65: (0, 426), 174: (0, 355), 175: (0, 412), 168: (1, {'@': 359}), 40: (1, {'@': 359}), 14: (1, {'@': 359}), 169: (1, {'@': 359}), 170: (1, {'@': 359}), 171: (1, {'@': 359}), 172: (1, {'@': 359}), 41: (1, {'@': 359}), 13: (1, {'@': 359}), 42: (1, {'@': 359}), 176: (1, {'@': 359}), 178: (1, {'@': 359}), 180: (1, {'@': 359})}, 231: {40: (1, {'@': 180}), 41: (1, {'@': 180})}, 232: {40: (1, {'@': 182}), 41: (1, {'@': 182})}, 233: {10: (0, 459), 1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 22: (0, 462), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 234: {40: (1, {'@': 183}), 41: (1, {'@': 183})}, 235: {39: (0, 362)}, 236: {1: (0, 111), 2: (0, 81), 3: (0, 587), 6: (0, 581), 22: (0, 136), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 138), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 30: (0, 142), 20: (0, 414), 35: (0, 347)}, 237: {40: (1, {'@': 19}), 41: (1, {'@': 19})}, 238: {40: (1, {'@': 312}), 41: (1, {'@': 312})}, 239: {40: (1, {'@': 66}), 41: (1, {'@': 66})}, 240: {3: (1, {'@': 13}), 62: (1, {'@': 13}), 63: (1, {'@': 13}), 114: (1, {'@': 13}), 64: (1, {'@': 13}), 65: (1, {'@': 13}), 115: (1, {'@': 13}), 66: (1, {'@': 13}), 67: (1, {'@': 13}), 68: (1, {'@': 13}), 69: (1, {'@': 13}), 70: (1, {'@': 13}), 71: (1, {'@': 13}), 72: (1, {'@': 13}), 116: (1, {'@': 13}), 117: (1, {'@': 13}), 73: (1, {'@': 13}), 118: (1, {'@': 13}), 119: (1, {'@': 13}), 120: (1, {'@': 13}), 74: (1, {'@': 13}), 75: (1, {'@': 13}), 121: (1, {'@': 13}), 122: (1, {'@': 13}), 123: (1, {'@': 13}), 76: (1, {'@': 13}), 124: (1, {'@': 13}), 125: (1, {'@': 13}), 126: (1, {'@': 13}), 127: (1, {'@': 13}), 27: (1, {'@': 13}), 128: (1, {'@': 13}), 77: (1, {'@': 13}), 129: (1, {'@': 13}), 78: (1, {'@': 13}), 130: (1, {'@': 13}), 131: (1, {'@': 13}), 79: (1, {'@': 13}), 80: (1, {'@': 13}), 81: (1, {'@': 13}), 82: (1, {'@': 13}), 83: (1, {'@': 13}), 132: (1, {'@': 13}), 133: (1, {'@': 13}), 134: (1, {'@': 13}), 84: (1, {'@': 13}), 85: (1, {'@': 13}), 161: (1, {'@': 13}), 135: (1, {'@': 13}), 136: (1, {'@': 13}), 86: (1, {'@': 13}), 87: (1, {'@': 13}), 137: (1, {'@': 13}), 88: (1, {'@': 13}), 138: (1, {'@': 13}), 89: (1, {'@': 13}), 90: (1, {'@': 13}), 91: (1, {'@': 13}), 92: (1, {'@': 13}), 93: (1, {'@': 13}), 139: (1, {'@': 13}), 140: (1, {'@': 13}), 94: (1, {'@': 13}), 141: (1, {'@': 13}), 95: (1, {'@': 13}), 142: (1, {'@': 13}), 96: (1, {'@': 13}), 143: (1, {'@': 13}), 144: (1, {'@': 13}), 145: (1, {'@': 13}), 146: (1, {'@': 13}), 147: (1, {'@': 13}), 97: (1, {'@': 13}), 98: (1, {'@': 13}), 148: (1, {'@': 13}), 99: (1, {'@': 13}), 149: (1, {'@': 13}), 41: (1, {'@': 13}), 150: (1, {'@': 13}), 151: (1, {'@': 13}), 152: (1, {'@': 13}), 100: (1, {'@': 13}), 101: (1, {'@': 13}), 102: (1, {'@': 13}), 40: (1, {'@': 13}), 153: (1, {'@': 13}), 103: (1, {'@': 13}), 154: (1, {'@': 13}), 104: (1, {'@': 13}), 105: (1, {'@': 13}), 155: (1, {'@': 13}), 106: (1, {'@': 13}), 107: (1, {'@': 13}), 156: (1, {'@': 13}), 157: (1, {'@': 13}), 158: (1, {'@': 13}), 108: (1, {'@': 13}), 109: (1, {'@': 13}), 110: (1, {'@': 13}), 111: (1, {'@': 13}), 159: (1, {'@': 13}), 160: (1, {'@': 13}), 112: (1, {'@': 13}), 162: (1, {'@': 13}), 113: (1, {'@': 13})}, 241: {40: (1, {'@': 118}), 41: (1, {'@': 118})}, 242: {40: (1, {'@': 261}), 41: (1, {'@': 261}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 243: {40: (1, {'@': 73}), 41: (1, {'@': 73})}, 244: {40: (1, {'@': 222}), 41: (1, {'@': 222})}, 245: {40: (1, {'@': 153}), 41: (1, {'@': 153})}, 246: {40: (1, {'@': 97}), 41: (1, {'@': 97})}, 247: {40: (1, {'@': 263}), 41: (1, {'@': 263})}, 248: {173: (1, {'@': 347}), 174: (1, {'@': 366}), 168: (1, {'@': 366}), 40: (1, {'@': 366}), 14: (1, {'@': 366}), 169: (1, {'@': 366}), 170: (1, {'@': 366}), 171: (1, {'@': 366}), 65: (1, {'@': 366}), 172: (1, {'@': 366}), 41: (1, {'@': 366}), 13: (1, {'@': 366}), 175: (1, {'@': 366}), 42: (1, {'@': 366}), 176: (1, {'@': 366}), 178: (1, {'@': 366}), 180: (1, {'@': 366})}, 249: {5: (0, 323), 2: (0, 604)}, 250: {40: (1, {'@': 260}), 41: (1, {'@': 260})}, 251: {40: (1, {'@': 71}), 41: (1, {'@': 71})}, 252: {40: (1, {'@': 195}), 41: (1, {'@': 195})}, 253: {168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 254: {40: (1, {'@': 262}), 41: (1, {'@': 262})}, 255: {40: (1, {'@': 72}), 41: (1, {'@': 72})}, 256: {40: (1, {'@': 132}), 41: (1, {'@': 132})}, 257: {42: (0, 492)}, 258: {40: (1, {'@': 258}), 41: (1, {'@': 258})}, 259: {49: (0, 171), 1: (0, 111), 3: (0, 587), 6: (0, 581), 14: (0, 431), 43: (0, 318), 8: (0, 529), 13: (0, 535), 12: (0, 248), 15: (0, 226), 18: (0, 192), 45: (0, 387), 20: (0, 414), 21: (0, 186), 44: (0, 129), 10: (0, 282), 46: (0, 132), 38: (0, 123), 27: (0, 417), 47: (0, 79), 28: (0, 302), 17: (0, 211), 5: (0, 373), 34: (0, 511), 48: (0, 353), 35: (0, 347), 50: (0, 542), 51: (0, 341), 22: (0, 567)}, 260: {42: (0, 436)}, 261: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 22: (0, 495), 18: (0, 192), 21: (0, 186), 10: (0, 498), 61: (0, 504), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 262: {40: (1, {'@': 259}), 41: (1, {'@': 259}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 263: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 52: (0, 447), 17: (0, 211), 18: (0, 192), 21: (0, 186), 22: (0, 403), 10: (0, 432), 56: (0, 405), 27: (0, 417), 53: (0, 394), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 264: {40: (1, {'@': 253}), 41: (1, {'@': 253}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 265: {40: (1, {'@': 74}), 41: (1, {'@': 74})}, 266: {40: (1, {'@': 135}), 41: (1, {'@': 135})}, 267: {1: (0, 111), 3: (0, 587), 53: (0, 336), 6: (0, 581), 58: (0, 572), 8: (0, 529), 56: (0, 354), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 52: (0, 313), 10: (0, 253), 27: (0, 417), 28: (0, 302), 5: (0, 373), 22: (0, 269), 34: (0, 511), 39: (0, 576), 20: (0, 414), 35: (0, 347)}, 268: {40: (1, {'@': 250}), 41: (1, {'@': 250})}, 269: {176: (0, 200)}, 270: {176: (0, 445)}, 271: {40: (1, {'@': 108}), 41: (1, {'@': 108})}, 272: {27: (0, 51), 40: (1, {'@': 63}), 41: (1, {'@': 63})}, 273: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 32: (0, 106), 18: (0, 192), 38: (0, 592), 20: (0, 414), 21: (0, 186), 22: (0, 110), 16: (0, 113), 10: (0, 253), 24: (0, 564), 25: (0, 485), 26: (0, 518), 27: (0, 417), 28: (0, 302), 17: (0, 211), 5: (0, 373), 31: (0, 565), 34: (0, 511), 33: (0, 602), 35: (0, 347), 37: (0, 402)}, 274: {40: (1, {'@': 141}), 41: (1, {'@': 141})}, 275: {40: (1, {'@': 107}), 41: (1, {'@': 107})}, 276: {1: (0, 111), 10: (0, 395), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 18: (0, 192), 20: (0, 414), 21: (0, 186), 27: (0, 417), 28: (0, 302), 17: (0, 211), 22: (0, 327), 5: (0, 373), 34: (0, 511), 35: (0, 347)}, 277: {176: (0, 585)}, 278: {27: (0, 417), 3: (0, 587), 21: (0, 186), 12: (0, 486), 28: (0, 302), 15: (0, 226), 10: (0, 536), 5: (0, 373), 13: (0, 535), 14: (0, 431)}, 279: {40: (1, {'@': 106}), 41: (1, {'@': 106})}, 280: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 10: (0, 343), 12: (0, 248), 14: (0, 431), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 27: (0, 417), 32: (0, 346), 28: (0, 302), 5: (0, 373), 34: (0, 511), 22: (0, 351), 20: (0, 414), 35: (0, 347)}, 281: {40: (1, {'@': 109}), 41: (1, {'@': 109})}, 282: {40: (1, {'@': 176}), 41: (1, {'@': 176}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 13: (1, {'@': 344}), 14: (1, {'@': 344}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346})}, 283: {40: (1, {'@': 240}), 41: (1, {'@': 240})}, 284: {1: (0, 111), 3: (0, 587), 6: (0, 581), 22: (0, 304), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 483), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 285: {40: (1, {'@': 226}), 41: (1, {'@': 226})}, 286: {27: (0, 417), 3: (0, 587), 10: (0, 545), 21: (0, 186), 12: (0, 486), 28: (0, 302), 15: (0, 226), 5: (0, 373), 13: (0, 535), 14: (0, 431)}, 287: {40: (1, {'@': 126}), 41: (1, {'@': 126})}, 288: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 520), 27: (0, 417), 22: (0, 521), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 289: {40: (1, {'@': 98}), 41: (1, {'@': 98})}, 290: {176: (0, 477)}, 291: {40: (1, {'@': 121}), 41: (1, {'@': 121})}, 292: {40: (1, {'@': 99}), 41: (1, {'@': 99})}, 293: {40: (1, {'@': 122}), 41: (1, {'@': 122}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 13: (1, {'@': 344}), 14: (1, {'@': 344}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346})}, 294: {42: (0, 137)}, 295: {40: (1, {'@': 70}), 41: (1, {'@': 70})}, 296: {40: (1, {'@': 137}), 41: (1, {'@': 137})}, 297: {40: (1, {'@': 144}), 41: (1, {'@': 144})}, 298: {40: (1, {'@': 101}), 41: (1, {'@': 101})}, 299: {42: (1, {'@': 266}), 41: (1, {'@': 266}), 40: (1, {'@': 266})}, 300: {40: (1, {'@': 311}), 41: (1, {'@': 311})}, 301: {40: (1, {'@': 100}), 41: (1, {'@': 100})}, 302: {174: (1, {'@': 377}), 168: (1, {'@': 377}), 40: (1, {'@': 377}), 14: (1, {'@': 377}), 173: (1, {'@': 377}), 169: (1, {'@': 377}), 170: (1, {'@': 377}), 171: (1, {'@': 377}), 65: (1, {'@': 377}), 172: (1, {'@': 377}), 41: (1, {'@': 377}), 13: (1, {'@': 377}), 175: (1, {'@': 377}), 42: (1, {'@': 377}), 176: (1, {'@': 377}), 178: (1, {'@': 377}), 180: (1, {'@': 377})}, 303: {40: (1, {'@': 96}), 41: (1, {'@': 96})}, 304: {40: (1, {'@': 256}), 41: (1, {'@': 256})}, 305: {1: (0, 111), 3: (0, 587), 2: (0, 537), 6: (0, 581), 5: (0, 263), 7: (0, 317), 0: (0, 287), 8: (0, 529), 13: (0, 535), 14: (0, 431), 16: (0, 289), 12: (0, 248), 15: (0, 226), 19: (0, 365), 18: (0, 192), 22: (0, 291), 20: (0, 414), 21: (0, 186), 38: (0, 592), 23: (0, 352), 9: (0, 292), 10: (0, 293), 24: (0, 564), 25: (0, 485), 26: (0, 518), 27: (0, 417), 29: (0, 311), 28: (0, 302), 17: (0, 211), 36: (0, 298), 31: (0, 565), 32: (0, 301), 34: (0, 511), 33: (0, 602), 35: (0, 347), 37: (0, 402)}, 306: {40: (1, {'@': 254}), 41: (1, {'@': 254}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 307: {42: (0, 575)}, 308: {57: (0, 558), 58: (0, 524), 32: (0, 177), 52: (0, 541), 59: (0, 274), 53: (0, 577), 54: (0, 413), 56: (0, 463), 39: (0, 470)}, 309: {3: (1, {'@': 298}), 27: (1, {'@': 298}), 14: (1, {'@': 298}), 21: (1, {'@': 298}), 28: (1, {'@': 298}), 13: (1, {'@': 298}), 5: (1, {'@': 298})}, 310: {40: (1, {'@': 241}), 41: (1, {'@': 241})}, 311: {40: (1, {'@': 323}), 41: (1, {'@': 323}), 42: (1, {'@': 323})}, 312: {178: (0, 516)}, 313: {176: (0, 415)}, 314: {62: (0, 556), 164: (0, 460), 121: (0, 499), 117: (0, 472), 100: (0, 78), 123: (0, 276), 74: (0, 54), 126: (0, 58), 165: (0, 469), 92: (0, 551), 166: (0, 548), 97: (0, 334), 154: (0, 309), 157: (0, 446), 160: (0, 514), 108: (0, 523), 139: (0, 461), 142: (0, 244), 143: (0, 386), 136: (0, 155), 79: (0, 284), 132: (0, 532), 135: (0, 448), 114: (0, 344), 181: (0, 152), 93: (0, 185), 116: (0, 161), 81: (0, 107), 88: (0, 143), 159: (0, 322), 150: (0, 83), 89: (0, 252), 122: (0, 427), 69: (0, 441), 145: (0, 166), 129: (0, 210), 182: (0, 607), 109: (0, 235), 82: (0, 259), 101: (0, 9), 77: (0, 1), 127: (0, 22), 95: (0, 42), 104: (0, 14), 102: (0, 75), 113: (0, 97), 64: (0, 120), 131: (0, 86), 72: (0, 90), 151: (0, 94), 141: (0, 103), 103: (0, 115), 153: (0, 67), 147: (0, 31), 68: (0, 6), 138: (0, 57), 140: (0, 64), 105: (0, 62), 27: (0, 44), 111: (0, 45), 183: (0, 59), 94: (0, 61), 137: (0, 28), 85: (0, 36), 99: (0, 52), 107: (0, 20), 167: (0, 29), 87: (0, 3), 86: (0, 12), 80: (0, 21), 83: (0, 15), 110: (0, 4), 70: (0, 8), 133: (0, 5), 130: (0, 10), 73: (0, 13), 124: (0, 66), 148: (0, 24), 125: (0, 37), 156: (0, 50), 119: (0, 55), 184: (0, 60), 98: (0, 65), 112: (0, 48), 158: (0, 46), 155: (0, 49), 162: (0, 53), 78: (0, 11), 106: (0, 16), 76: (0, 63), 146: (0, 18), 3: (0, 19), 128: (0, 40), 118: (0, 23), 66: (0, 25), 71: (0, 26), 120: (0, 27), 67: (0, 30), 161: (0, 32), 84: (0, 33), 149: (0, 35), 65: (0, 38), 96: (0, 39), 144: (0, 47), 152: (0, 43), 63: (0, 175), 75: (0, 473), 134: (0, 285), 90: (0, 457), 115: (0, 411), 40: (1, {'@': 16}), 41: (1, {'@': 16}), 91: (1, {'@': 0})}, 315: {27: (0, 417), 3: (0, 587), 21: (0, 186), 28: (0, 302), 15: (0, 226), 10: (0, 543), 12: (0, 456), 5: (0, 373), 13: (0, 535), 14: (0, 431)}, 316: {40: (1, {'@': 251}), 41: (1, {'@': 251})}, 317: {40: (1, {'@': 321}), 41: (1, {'@': 321}), 42: (1, {'@': 321})}, 318: {42: (1, {'@': 335}), 40: (1, {'@': 335}), 41: (1, {'@': 335})}, 319: {177: (0, 467)}, 320: {42: (0, 273)}, 321: {40: (1, {'@': 162}), 41: (1, {'@': 162})}, 322: {1: (0, 111), 3: (0, 587), 6: (0, 581), 52: (0, 541), 121: (0, 272), 8: (0, 529), 53: (0, 577), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 10: (0, 393), 18: (0, 192), 20: (0, 414), 21: (0, 186), 54: (0, 440), 22: (0, 368), 56: (0, 463), 177: (0, 266), 27: (0, 417), 28: (0, 302), 17: (0, 211), 5: (0, 373), 39: (0, 470), 57: (0, 558), 58: (0, 524), 34: (0, 511), 35: (0, 347)}, 323: {38: (0, 358)}, 324: {40: (1, {'@': 78}), 41: (1, {'@': 78})}, 325: {26: (0, 468)}, 326: {40: (1, {'@': 158}), 41: (1, {'@': 158})}, 327: {42: (0, 108)}, 328: {40: (1, {'@': 160}), 41: (1, {'@': 160})}, 329: {42: (0, 544)}, 330: {178: (0, 455)}, 331: {40: (1, {'@': 190}), 41: (1, {'@': 190})}, 332: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 52: (0, 447), 39: (0, 421), 17: (0, 211), 18: (0, 192), 21: (0, 186), 22: (0, 403), 10: (0, 432), 56: (0, 405), 27: (0, 417), 53: (0, 394), 28: (0, 302), 5: (0, 373), 34: (0, 511), 58: (0, 388), 20: (0, 414), 35: (0, 347)}, 333: {178: (0, 450)}, 334: {57: (0, 558), 58: (0, 524), 52: (0, 541), 121: (0, 17), 177: (0, 296), 54: (0, 374), 53: (0, 577), 56: (0, 463), 39: (0, 470)}, 335: {42: (0, 308)}, 336: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 22: (0, 207), 13: (0, 383), 14: (0, 517), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 253), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 337: {42: (0, 596)}, 338: {176: (0, 225)}, 339: {40: (1, {'@': 33}), 41: (1, {'@': 33})}, 340: {40: (1, {'@': 156}), 41: (1, {'@': 156})}, 341: {42: (1, {'@': 331}), 40: (1, {'@': 331}), 41: (1, {'@': 331})}, 342: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 10: (0, 481), 15: (0, 226), 17: (0, 211), 18: (0, 192), 22: (0, 482), 21: (0, 186), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 343: {40: (1, {'@': 255}), 41: (1, {'@': 255}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 344: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 22: (0, 198), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 253), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 345: {40: (1, {'@': 30}), 41: (1, {'@': 30})}, 346: {40: (1, {'@': 242}), 41: (1, {'@': 242})}, 347: {168: (1, {'@': 341}), 169: (1, {'@': 341}), 170: (1, {'@': 341}), 171: (1, {'@': 341}), 172: (1, {'@': 341}), 40: (1, {'@': 351}), 41: (1, {'@': 351}), 42: (1, {'@': 351}), 176: (1, {'@': 351}), 180: (1, {'@': 351}), 178: (1, {'@': 351})}, 348: {2: (0, 537), 5: (0, 590), 0: (0, 208), 16: (0, 356), 24: (0, 564), 25: (0, 485), 26: (0, 518), 36: (0, 360), 32: (0, 363), 31: (0, 565), 33: (0, 602), 37: (0, 402), 38: (0, 592)}, 349: {42: (0, 489)}, 350: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 52: (0, 447), 17: (0, 211), 18: (0, 192), 21: (0, 186), 22: (0, 403), 58: (0, 579), 10: (0, 432), 56: (0, 405), 39: (0, 583), 27: (0, 417), 53: (0, 394), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 351: {40: (1, {'@': 252}), 41: (1, {'@': 252})}, 352: {40: (1, {'@': 322}), 41: (1, {'@': 322}), 42: (1, {'@': 322})}, 353: {42: (1, {'@': 333}), 40: (1, {'@': 333}), 41: (1, {'@': 333})}, 354: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 286), 14: (0, 598), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 253), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 22: (0, 70), 20: (0, 414), 35: (0, 347)}, 355: {27: (0, 417), 3: (0, 587), 12: (0, 248), 6: (0, 581), 21: (0, 186), 28: (0, 302), 14: (0, 431), 15: (0, 226), 18: (0, 119), 5: (0, 373), 13: (0, 535), 185: (0, 154), 10: (0, 163)}, 356: {40: (1, {'@': 157}), 41: (1, {'@': 157})}, 357: {91: (1, {'@': 5}), 41: (1, {'@': 5})}, 358: {178: (0, 232)}, 359: {178: (0, 475)}, 360: {40: (1, {'@': 159}), 41: (1, {'@': 159})}, 361: {40: (1, {'@': 67}), 41: (1, {'@': 67}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 362: {42: (0, 589)}, 363: {40: (1, {'@': 155}), 41: (1, {'@': 155})}, 364: {42: (0, 385)}, 365: {40: (1, {'@': 324}), 41: (1, {'@': 324}), 42: (1, {'@': 324})}, 366: {40: (1, {'@': 83}), 41: (1, {'@': 83})}, 367: {42: (0, 591)}, 368: {40: (1, {'@': 264}), 41: (1, {'@': 264})}, 369: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 38: (0, 592), 10: (0, 253), 24: (0, 564), 25: (0, 485), 16: (0, 95), 26: (0, 518), 27: (0, 417), 28: (0, 302), 22: (0, 100), 5: (0, 373), 31: (0, 565), 34: (0, 511), 33: (0, 602), 32: (0, 101), 20: (0, 414), 35: (0, 347), 37: (0, 402)}, 370: {40: (1, {'@': 133}), 41: (1, {'@': 133})}, 371: {168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 13: (1, {'@': 344}), 14: (1, {'@': 344}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 40: (1, {'@': 145}), 41: (1, {'@': 145})}, 372: {40: (1, {'@': 191}), 41: (1, {'@': 191})}, 373: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 22: (0, 403), 10: (0, 432), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 374: {40: (1, {'@': 138}), 41: (1, {'@': 138})}, 375: {40: (1, {'@': 35}), 41: (1, {'@': 35})}, 376: {40: (1, {'@': 247}), 41: (1, {'@': 247})}, 377: {40: (1, {'@': 31}), 41: (1, {'@': 31})}, 378: {40: (1, {'@': 111}), 41: (1, {'@': 111})}, 379: {40: (1, {'@': 26}), 41: (1, {'@': 26})}, 380: {42: (0, 236)}, 381: {40: (1, {'@': 142}), 41: (1, {'@': 142})}, 382: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 253), 27: (0, 417), 28: (0, 302), 5: (0, 373), 22: (0, 269), 34: (0, 511), 20: (0, 414), 35: (0, 347), 38: (0, 93)}, 383: {27: (0, 417), 3: (0, 587), 21: (0, 186), 12: (0, 486), 10: (0, 566), 28: (0, 302), 15: (0, 226), 5: (0, 373), 13: (0, 535), 14: (0, 431)}, 384: {42: (0, 559)}, 385: {1: (0, 111), 3: (0, 587), 6: (0, 581), 7: (0, 317), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 19: (0, 365), 18: (0, 192), 38: (0, 592), 20: (0, 414), 21: (0, 186), 23: (0, 352), 10: (0, 253), 25: (0, 485), 26: (0, 518), 27: (0, 417), 29: (0, 311), 28: (0, 302), 37: (0, 125), 17: (0, 211), 5: (0, 373), 32: (0, 127), 22: (0, 131), 33: (0, 602), 34: (0, 511), 35: (0, 347), 9: (0, 133)}, 386: {40: (1, {'@': 236}), 41: (1, {'@': 236})}, 387: {42: (1, {'@': 338}), 40: (1, {'@': 338}), 41: (1, {'@': 338})}, 388: {178: (0, 349)}, 389: {3: (0, 587), 6: (0, 581), 13: (0, 535), 14: (0, 431), 15: (0, 226), 12: (0, 248), 34: (0, 230), 18: (0, 192), 21: (0, 186), 10: (0, 149), 27: (0, 417), 28: (0, 302), 5: (0, 373), 8: (0, 229)}, 390: {40: (1, {'@': 148}), 41: (1, {'@': 148})}, 391: {40: (1, {'@': 188}), 41: (1, {'@': 188})}, 392: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 22: (0, 403), 10: (0, 432), 38: (0, 96), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 393: {40: (1, {'@': 265}), 41: (1, {'@': 265}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 13: (1, {'@': 344}), 14: (1, {'@': 344}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346})}, 394: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 169), 14: (0, 315), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 253), 22: (0, 359), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 395: {42: (0, 510), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 13: (1, {'@': 344}), 14: (1, {'@': 344}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346})}, 396: {40: (1, {'@': 34}), 41: (1, {'@': 34})}, 397: {168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 13: (1, {'@': 344}), 14: (1, {'@': 344}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 40: (1, {'@': 174}), 41: (1, {'@': 174})}, 398: {40: (1, {'@': 29}), 41: (1, {'@': 29})}, 399: {40: (1, {'@': 310}), 41: (1, {'@': 310})}, 400: {40: (1, {'@': 77}), 41: (1, {'@': 77})}, 401: {40: (1, {'@': 189}), 41: (1, {'@': 189})}, 402: {40: (1, {'@': 316}), 41: (1, {'@': 316}), 42: (1, {'@': 316})}, 403: {178: (0, 153)}, 404: {42: (1, {'@': 267}), 41: (1, {'@': 267}), 40: (1, {'@': 267})}, 405: {1: (0, 111), 3: (0, 587), 6: (0, 581), 14: (0, 214), 8: (0, 529), 13: (0, 278), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 22: (0, 312), 10: (0, 253), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 406: {40: (1, {'@': 113}), 41: (1, {'@': 113})}, 407: {42: (0, 194), 40: (1, {'@': 50}), 41: (1, {'@': 50})}, 408: {40: (1, {'@': 18}), 41: (1, {'@': 18})}, 409: {40: (1, {'@': 23}), 41: (1, {'@': 23})}, 410: {40: (1, {'@': 112}), 41: (1, {'@': 112})}, 411: {3: (1, {'@': 293}), 23: (1, {'@': 293}), 19: (1, {'@': 293}), 2: (1, {'@': 293}), 25: (1, {'@': 293}), 31: (1, {'@': 293}), 32: (1, {'@': 293}), 38: (1, {'@': 293}), 27: (1, {'@': 293}), 33: (1, {'@': 293}), 14: (1, {'@': 293}), 5: (1, {'@': 293}), 29: (1, {'@': 293}), 24: (1, {'@': 293}), 7: (1, {'@': 293}), 21: (1, {'@': 293}), 26: (1, {'@': 293}), 28: (1, {'@': 293}), 13: (1, {'@': 293})}, 412: {27: (0, 417), 3: (0, 587), 12: (0, 248), 6: (0, 581), 21: (0, 186), 28: (0, 302), 14: (0, 431), 15: (0, 226), 185: (0, 213), 18: (0, 119), 5: (0, 373), 13: (0, 535), 10: (0, 163)}, 413: {40: (1, {'@': 140}), 41: (1, {'@': 140})}, 414: {14: (0, 594), 13: (0, 389)}, 415: {40: (1, {'@': 279}), 41: (1, {'@': 279}), 42: (1, {'@': 279})}, 416: {40: (1, {'@': 127}), 41: (1, {'@': 127})}, 417: {174: (1, {'@': 375}), 168: (1, {'@': 375}), 40: (1, {'@': 375}), 14: (1, {'@': 375}), 173: (1, {'@': 375}), 169: (1, {'@': 375}), 170: (1, {'@': 375}), 171: (1, {'@': 375}), 65: (1, {'@': 375}), 172: (1, {'@': 375}), 41: (1, {'@': 375}), 13: (1, {'@': 375}), 175: (1, {'@': 375}), 42: (1, {'@': 375}), 176: (1, {'@': 375}), 178: (1, {'@': 375}), 180: (1, {'@': 375})}, 418: {180: (0, 156)}, 419: {42: (0, 484)}, 420: {59: (0, 165)}, 421: {178: (0, 257)}, 422: {40: (1, {'@': 24}), 41: (1, {'@': 24})}, 423: {40: (1, {'@': 278}), 41: (1, {'@': 278}), 42: (1, {'@': 278})}, 424: {40: (1, {'@': 102}), 41: (1, {'@': 102})}, 425: {40: (1, {'@': 124}), 41: (1, {'@': 124}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 13: (1, {'@': 344}), 14: (1, {'@': 344}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346})}, 426: {27: (0, 417), 3: (0, 587), 12: (0, 248), 6: (0, 581), 21: (0, 186), 28: (0, 302), 185: (0, 205), 14: (0, 431), 15: (0, 226), 18: (0, 119), 5: (0, 373), 13: (0, 535), 10: (0, 163)}, 427: {24: (1, {'@': 303}), 33: (1, {'@': 303}), 32: (1, {'@': 303}), 2: (1, {'@': 303}), 26: (1, {'@': 303}), 25: (1, {'@': 303}), 38: (1, {'@': 303}), 31: (1, {'@': 303}), 5: (1, {'@': 303})}, 428: {40: (1, {'@': 134}), 41: (1, {'@': 134}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 429: {40: (1, {'@': 103}), 41: (1, {'@': 103})}, 430: {40: (1, {'@': 87}), 41: (1, {'@': 87})}, 431: {27: (0, 417), 3: (0, 587), 21: (0, 186), 28: (0, 302), 15: (0, 226), 12: (0, 456), 10: (0, 466), 5: (0, 373), 13: (0, 535), 14: (0, 431)}, 432: {178: (0, 187), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 433: {40: (1, {'@': 105}), 41: (1, {'@': 105})}, 434: {40: (1, {'@': 123}), 41: (1, {'@': 123})}, 435: {42: (0, 319)}, 436: {58: (0, 586), 59: (0, 406), 39: (0, 378), 52: (0, 410)}, 437: {40: (1, {'@': 309}), 41: (1, {'@': 309})}, 438: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 22: (0, 218), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 221), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 439: {40: (1, {'@': 104}), 41: (1, {'@': 104})}, 440: {40: (1, {'@': 136}), 41: (1, {'@': 136})}, 441: {27: (0, 299), 186: (0, 407)}, 442: {42: (0, 104)}, 443: {52: (0, 313), 57: (0, 224), 53: (0, 577), 38: (0, 270), 56: (0, 463)}, 444: {1: (0, 111), 3: (0, 587), 6: (0, 581), 10: (0, 264), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 22: (0, 268), 17: (0, 211), 52: (0, 271), 18: (0, 192), 21: (0, 186), 39: (0, 275), 58: (0, 279), 27: (0, 417), 28: (0, 302), 59: (0, 281), 32: (0, 283), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 445: {40: (1, {'@': 244}), 41: (1, {'@': 244})}, 446: {2: (0, 68), 5: (0, 546), 30: (0, 222), 10: (0, 384)}, 447: {178: (0, 423)}, 448: {40: (1, {'@': 230}), 41: (1, {'@': 230})}, 449: {40: (1, {'@': 308}), 41: (1, {'@': 308})}, 450: {40: (1, {'@': 88}), 41: (1, {'@': 88})}, 451: {40: (1, {'@': 313}), 41: (1, {'@': 313})}, 452: {42: (0, 212), 40: (1, {'@': 68}), 41: (1, {'@': 68})}, 453: {40: (1, {'@': 89}), 41: (1, {'@': 89})}, 454: {40: (1, {'@': 246}), 41: (1, {'@': 246})}, 455: {40: (1, {'@': 243}), 41: (1, {'@': 243})}, 456: {174: (1, {'@': 368}), 168: (1, {'@': 368}), 40: (1, {'@': 368}), 14: (1, {'@': 368}), 173: (1, {'@': 368}), 169: (1, {'@': 368}), 170: (1, {'@': 368}), 171: (1, {'@': 368}), 65: (1, {'@': 368}), 172: (1, {'@': 368}), 41: (1, {'@': 368}), 13: (1, {'@': 368}), 175: (1, {'@': 368}), 42: (1, {'@': 368}), 176: (1, {'@': 368}), 178: (1, {'@': 368}), 180: (1, {'@': 368})}, 457: {40: (1, {'@': 192}), 41: (1, {'@': 192})}, 458: {40: (1, {'@': 154}), 41: (1, {'@': 154})}, 459: {40: (1, {'@': 166}), 41: (1, {'@': 166}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 460: {2: (0, 537), 5: (0, 590), 32: (0, 245), 0: (0, 458), 24: (0, 564), 25: (0, 485), 36: (0, 533), 26: (0, 518), 31: (0, 565), 16: (0, 578), 33: (0, 602), 37: (0, 402), 38: (0, 592)}, 461: {1: (0, 111), 22: (0, 193), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 253), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 462: {40: (1, {'@': 165}), 41: (1, {'@': 165})}, 463: {40: (1, {'@': 329}), 41: (1, {'@': 329}), 42: (1, {'@': 329}), 176: (1, {'@': 329}), 178: (1, {'@': 329})}, 464: {168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 42: (1, {'@': 275}), 41: (1, {'@': 275}), 40: (1, {'@': 275}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 465: {42: (0, 348)}, 466: {174: (1, {'@': 370}), 168: (1, {'@': 370}), 40: (1, {'@': 370}), 14: (1, {'@': 370}), 173: (1, {'@': 370}), 169: (1, {'@': 370}), 170: (1, {'@': 370}), 171: (1, {'@': 370}), 65: (1, {'@': 370}), 172: (1, {'@': 370}), 41: (1, {'@': 370}), 13: (1, {'@': 370}), 175: (1, {'@': 370}), 42: (1, {'@': 370}), 176: (1, {'@': 370}), 178: (1, {'@': 370}), 180: (1, {'@': 370})}, 467: {187: (0, 206)}, 468: {40: (1, {'@': 245}), 41: (1, {'@': 245})}, 469: {1: (0, 111), 3: (0, 587), 2: (0, 537), 6: (0, 581), 5: (0, 263), 7: (0, 317), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 16: (0, 570), 18: (0, 192), 19: (0, 365), 36: (0, 203), 21: (0, 186), 38: (0, 592), 22: (0, 370), 23: (0, 352), 10: (0, 428), 24: (0, 564), 25: (0, 485), 26: (0, 518), 27: (0, 417), 28: (0, 302), 29: (0, 311), 31: (0, 565), 32: (0, 479), 34: (0, 511), 9: (0, 118), 33: (0, 602), 20: (0, 414), 35: (0, 347), 37: (0, 402), 0: (0, 256)}, 470: {40: (1, {'@': 326}), 41: (1, {'@': 326}), 42: (1, {'@': 326})}, 471: {41: (0, 126)}, 472: {52: (0, 553), 32: (0, 442)}, 473: {40: (1, {'@': 212}), 41: (1, {'@': 212})}, 474: {40: (1, {'@': 170}), 41: (1, {'@': 170})}, 475: {40: (1, {'@': 281}), 41: (1, {'@': 281}), 42: (1, {'@': 281})}, 476: {40: (1, {'@': 59}), 41: (1, {'@': 59})}, 477: {42: (0, 73)}, 478: {42: (0, 584)}, 479: {40: (1, {'@': 130}), 41: (1, {'@': 130})}, 480: {42: (0, 89)}, 481: {40: (1, {'@': 164}), 41: (1, {'@': 164}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 482: {40: (1, {'@': 163}), 41: (1, {'@': 163})}, 483: {40: (1, {'@': 257}), 41: (1, {'@': 257}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 484: {1: (0, 111), 2: (0, 81), 3: (0, 587), 57: (0, 144), 6: (0, 581), 8: (0, 529), 53: (0, 577), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 52: (0, 422), 18: (0, 192), 20: (0, 414), 21: (0, 186), 10: (0, 69), 56: (0, 463), 27: (0, 417), 30: (0, 91), 28: (0, 302), 17: (0, 211), 5: (0, 373), 34: (0, 511), 22: (0, 74), 35: (0, 347)}, 485: {40: (1, {'@': 320}), 41: (1, {'@': 320}), 42: (1, {'@': 320})}, 486: {174: (1, {'@': 367}), 168: (1, {'@': 367}), 40: (1, {'@': 367}), 14: (1, {'@': 367}), 173: (1, {'@': 367}), 169: (1, {'@': 367}), 170: (1, {'@': 367}), 171: (1, {'@': 367}), 65: (1, {'@': 367}), 172: (1, {'@': 367}), 41: (1, {'@': 367}), 13: (1, {'@': 367}), 175: (1, {'@': 367}), 42: (1, {'@': 367}), 176: (1, {'@': 367}), 178: (1, {'@': 367}), 180: (1, {'@': 367})}, 487: {3: (1, {'@': 11}), 62: (1, {'@': 11}), 63: (1, {'@': 11}), 114: (1, {'@': 11}), 64: (1, {'@': 11}), 65: (1, {'@': 11}), 115: (1, {'@': 11}), 66: (1, {'@': 11}), 67: (1, {'@': 11}), 68: (1, {'@': 11}), 69: (1, {'@': 11}), 70: (1, {'@': 11}), 71: (1, {'@': 11}), 72: (1, {'@': 11}), 116: (1, {'@': 11}), 117: (1, {'@': 11}), 73: (1, {'@': 11}), 118: (1, {'@': 11}), 119: (1, {'@': 11}), 120: (1, {'@': 11}), 74: (1, {'@': 11}), 75: (1, {'@': 11}), 121: (1, {'@': 11}), 122: (1, {'@': 11}), 123: (1, {'@': 11}), 76: (1, {'@': 11}), 124: (1, {'@': 11}), 125: (1, {'@': 11}), 126: (1, {'@': 11}), 127: (1, {'@': 11}), 27: (1, {'@': 11}), 128: (1, {'@': 11}), 77: (1, {'@': 11}), 129: (1, {'@': 11}), 78: (1, {'@': 11}), 130: (1, {'@': 11}), 131: (1, {'@': 11}), 79: (1, {'@': 11}), 80: (1, {'@': 11}), 81: (1, {'@': 11}), 82: (1, {'@': 11}), 83: (1, {'@': 11}), 132: (1, {'@': 11}), 133: (1, {'@': 11}), 134: (1, {'@': 11}), 84: (1, {'@': 11}), 85: (1, {'@': 11}), 161: (1, {'@': 11}), 135: (1, {'@': 11}), 136: (1, {'@': 11}), 86: (1, {'@': 11}), 87: (1, {'@': 11}), 137: (1, {'@': 11}), 88: (1, {'@': 11}), 138: (1, {'@': 11}), 89: (1, {'@': 11}), 90: (1, {'@': 11}), 91: (1, {'@': 11}), 92: (1, {'@': 11}), 93: (1, {'@': 11}), 139: (1, {'@': 11}), 140: (1, {'@': 11}), 94: (1, {'@': 11}), 141: (1, {'@': 11}), 95: (1, {'@': 11}), 142: (1, {'@': 11}), 96: (1, {'@': 11}), 143: (1, {'@': 11}), 144: (1, {'@': 11}), 145: (1, {'@': 11}), 146: (1, {'@': 11}), 147: (1, {'@': 11}), 97: (1, {'@': 11}), 98: (1, {'@': 11}), 148: (1, {'@': 11}), 99: (1, {'@': 11}), 149: (1, {'@': 11}), 41: (1, {'@': 11}), 150: (1, {'@': 11}), 151: (1, {'@': 11}), 152: (1, {'@': 11}), 100: (1, {'@': 11}), 101: (1, {'@': 11}), 102: (1, {'@': 11}), 40: (1, {'@': 11}), 153: (1, {'@': 11}), 103: (1, {'@': 11}), 154: (1, {'@': 11}), 104: (1, {'@': 11}), 105: (1, {'@': 11}), 155: (1, {'@': 11}), 106: (1, {'@': 11}), 107: (1, {'@': 11}), 156: (1, {'@': 11}), 157: (1, {'@': 11}), 158: (1, {'@': 11}), 108: (1, {'@': 11}), 109: (1, {'@': 11}), 110: (1, {'@': 11}), 111: (1, {'@': 11}), 159: (1, {'@': 11}), 160: (1, {'@': 11}), 112: (1, {'@': 11}), 162: (1, {'@': 11}), 113: (1, {'@': 11})}, 488: {174: (1, {'@': 369}), 168: (1, {'@': 369}), 40: (1, {'@': 369}), 14: (1, {'@': 369}), 173: (1, {'@': 369}), 169: (1, {'@': 369}), 170: (1, {'@': 369}), 171: (1, {'@': 369}), 65: (1, {'@': 369}), 172: (1, {'@': 369}), 41: (1, {'@': 369}), 13: (1, {'@': 369}), 175: (1, {'@': 369}), 42: (1, {'@': 369}), 176: (1, {'@': 369}), 178: (1, {'@': 369}), 180: (1, {'@': 369})}, 489: {32: (0, 134)}, 490: {42: (0, 288), 40: (1, {'@': 53}), 41: (1, {'@': 53})}, 491: {3: (1, {'@': 10}), 62: (1, {'@': 10}), 63: (1, {'@': 10}), 114: (1, {'@': 10}), 64: (1, {'@': 10}), 65: (1, {'@': 10}), 115: (1, {'@': 10}), 66: (1, {'@': 10}), 67: (1, {'@': 10}), 68: (1, {'@': 10}), 69: (1, {'@': 10}), 70: (1, {'@': 10}), 71: (1, {'@': 10}), 72: (1, {'@': 10}), 116: (1, {'@': 10}), 117: (1, {'@': 10}), 73: (1, {'@': 10}), 118: (1, {'@': 10}), 119: (1, {'@': 10}), 120: (1, {'@': 10}), 74: (1, {'@': 10}), 75: (1, {'@': 10}), 121: (1, {'@': 10}), 122: (1, {'@': 10}), 123: (1, {'@': 10}), 76: (1, {'@': 10}), 124: (1, {'@': 10}), 125: (1, {'@': 10}), 126: (1, {'@': 10}), 127: (1, {'@': 10}), 27: (1, {'@': 10}), 128: (1, {'@': 10}), 77: (1, {'@': 10}), 129: (1, {'@': 10}), 78: (1, {'@': 10}), 130: (1, {'@': 10}), 131: (1, {'@': 10}), 79: (1, {'@': 10}), 80: (1, {'@': 10}), 81: (1, {'@': 10}), 82: (1, {'@': 10}), 83: (1, {'@': 10}), 132: (1, {'@': 10}), 133: (1, {'@': 10}), 134: (1, {'@': 10}), 84: (1, {'@': 10}), 85: (1, {'@': 10}), 161: (1, {'@': 10}), 135: (1, {'@': 10}), 136: (1, {'@': 10}), 86: (1, {'@': 10}), 87: (1, {'@': 10}), 137: (1, {'@': 10}), 88: (1, {'@': 10}), 138: (1, {'@': 10}), 89: (1, {'@': 10}), 90: (1, {'@': 10}), 91: (1, {'@': 10}), 92: (1, {'@': 10}), 93: (1, {'@': 10}), 139: (1, {'@': 10}), 140: (1, {'@': 10}), 94: (1, {'@': 10}), 141: (1, {'@': 10}), 95: (1, {'@': 10}), 142: (1, {'@': 10}), 96: (1, {'@': 10}), 143: (1, {'@': 10}), 144: (1, {'@': 10}), 145: (1, {'@': 10}), 146: (1, {'@': 10}), 147: (1, {'@': 10}), 97: (1, {'@': 10}), 98: (1, {'@': 10}), 148: (1, {'@': 10}), 99: (1, {'@': 10}), 149: (1, {'@': 10}), 41: (1, {'@': 10}), 150: (1, {'@': 10}), 151: (1, {'@': 10}), 152: (1, {'@': 10}), 100: (1, {'@': 10}), 101: (1, {'@': 10}), 102: (1, {'@': 10}), 40: (1, {'@': 10}), 153: (1, {'@': 10}), 103: (1, {'@': 10}), 154: (1, {'@': 10}), 104: (1, {'@': 10}), 105: (1, {'@': 10}), 155: (1, {'@': 10}), 106: (1, {'@': 10}), 107: (1, {'@': 10}), 156: (1, {'@': 10}), 157: (1, {'@': 10}), 158: (1, {'@': 10}), 108: (1, {'@': 10}), 109: (1, {'@': 10}), 110: (1, {'@': 10}), 111: (1, {'@': 10}), 159: (1, {'@': 10}), 160: (1, {'@': 10}), 112: (1, {'@': 10}), 162: (1, {'@': 10}), 113: (1, {'@': 10})}, 492: {32: (0, 605)}, 493: {42: (1, {'@': 274}), 41: (1, {'@': 274}), 40: (1, {'@': 274})}, 494: {41: (0, 357), 91: (1, {'@': 2})}, 495: {42: (1, {'@': 271}), 41: (1, {'@': 271}), 40: (1, {'@': 271})}, 496: {3: (1, {'@': 15}), 62: (1, {'@': 15}), 63: (1, {'@': 15}), 114: (1, {'@': 15}), 64: (1, {'@': 15}), 65: (1, {'@': 15}), 115: (1, {'@': 15}), 66: (1, {'@': 15}), 67: (1, {'@': 15}), 68: (1, {'@': 15}), 69: (1, {'@': 15}), 70: (1, {'@': 15}), 71: (1, {'@': 15}), 72: (1, {'@': 15}), 116: (1, {'@': 15}), 117: (1, {'@': 15}), 73: (1, {'@': 15}), 118: (1, {'@': 15}), 119: (1, {'@': 15}), 120: (1, {'@': 15}), 74: (1, {'@': 15}), 75: (1, {'@': 15}), 121: (1, {'@': 15}), 122: (1, {'@': 15}), 123: (1, {'@': 15}), 76: (1, {'@': 15}), 124: (1, {'@': 15}), 125: (1, {'@': 15}), 126: (1, {'@': 15}), 127: (1, {'@': 15}), 27: (1, {'@': 15}), 128: (1, {'@': 15}), 77: (1, {'@': 15}), 129: (1, {'@': 15}), 78: (1, {'@': 15}), 130: (1, {'@': 15}), 131: (1, {'@': 15}), 79: (1, {'@': 15}), 80: (1, {'@': 15}), 81: (1, {'@': 15}), 82: (1, {'@': 15}), 83: (1, {'@': 15}), 132: (1, {'@': 15}), 133: (1, {'@': 15}), 134: (1, {'@': 15}), 84: (1, {'@': 15}), 85: (1, {'@': 15}), 161: (1, {'@': 15}), 135: (1, {'@': 15}), 136: (1, {'@': 15}), 86: (1, {'@': 15}), 87: (1, {'@': 15}), 137: (1, {'@': 15}), 88: (1, {'@': 15}), 138: (1, {'@': 15}), 89: (1, {'@': 15}), 90: (1, {'@': 15}), 91: (1, {'@': 15}), 92: (1, {'@': 15}), 93: (1, {'@': 15}), 139: (1, {'@': 15}), 140: (1, {'@': 15}), 94: (1, {'@': 15}), 141: (1, {'@': 15}), 95: (1, {'@': 15}), 142: (1, {'@': 15}), 96: (1, {'@': 15}), 143: (1, {'@': 15}), 144: (1, {'@': 15}), 145: (1, {'@': 15}), 146: (1, {'@': 15}), 147: (1, {'@': 15}), 97: (1, {'@': 15}), 98: (1, {'@': 15}), 148: (1, {'@': 15}), 99: (1, {'@': 15}), 149: (1, {'@': 15}), 41: (1, {'@': 15}), 150: (1, {'@': 15}), 151: (1, {'@': 15}), 152: (1, {'@': 15}), 100: (1, {'@': 15}), 101: (1, {'@': 15}), 102: (1, {'@': 15}), 40: (1, {'@': 15}), 153: (1, {'@': 15}), 103: (1, {'@': 15}), 154: (1, {'@': 15}), 104: (1, {'@': 15}), 105: (1, {'@': 15}), 155: (1, {'@': 15}), 106: (1, {'@': 15}), 107: (1, {'@': 15}), 156: (1, {'@': 15}), 157: (1, {'@': 15}), 158: (1, {'@': 15}), 108: (1, {'@': 15}), 109: (1, {'@': 15}), 110: (1, {'@': 15}), 111: (1, {'@': 15}), 159: (1, {'@': 15}), 160: (1, {'@': 15}), 112: (1, {'@': 15}), 162: (1, {'@': 15}), 113: (1, {'@': 15})}, 497: {42: (1, {'@': 268}), 41: (1, {'@': 268}), 40: (1, {'@': 268})}, 498: {42: (1, {'@': 272}), 41: (1, {'@': 272}), 40: (1, {'@': 272}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 499: {27: (0, 2)}, 500: {42: (0, 261), 40: (1, {'@': 51}), 41: (1, {'@': 51})}, 501: {3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 18: (0, 192), 21: (0, 186), 10: (0, 121), 27: (0, 417), 28: (0, 302), 17: (0, 116), 5: (0, 373), 20: (0, 196), 34: (0, 511)}, 502: {42: (0, 369)}, 503: {40: (1, {'@': 32}), 41: (1, {'@': 32})}, 504: {42: (1, {'@': 273}), 41: (1, {'@': 273}), 40: (1, {'@': 273})}, 505: {40: (1, {'@': 75}), 41: (1, {'@': 75})}, 506: {42: (0, 527)}, 507: {41: (0, 189), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 508: {42: (0, 552)}, 509: {40: (1, {'@': 249}), 41: (1, {'@': 249})}, 510: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 10: (0, 242), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 32: (0, 247), 22: (0, 250), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 511: {174: (0, 355), 65: (0, 426), 175: (0, 412)}, 512: {3: (1, {'@': 12}), 62: (1, {'@': 12}), 63: (1, {'@': 12}), 114: (1, {'@': 12}), 64: (1, {'@': 12}), 65: (1, {'@': 12}), 115: (1, {'@': 12}), 66: (1, {'@': 12}), 67: (1, {'@': 12}), 68: (1, {'@': 12}), 69: (1, {'@': 12}), 70: (1, {'@': 12}), 71: (1, {'@': 12}), 72: (1, {'@': 12}), 116: (1, {'@': 12}), 117: (1, {'@': 12}), 73: (1, {'@': 12}), 118: (1, {'@': 12}), 119: (1, {'@': 12}), 120: (1, {'@': 12}), 74: (1, {'@': 12}), 75: (1, {'@': 12}), 121: (1, {'@': 12}), 122: (1, {'@': 12}), 123: (1, {'@': 12}), 76: (1, {'@': 12}), 124: (1, {'@': 12}), 125: (1, {'@': 12}), 126: (1, {'@': 12}), 127: (1, {'@': 12}), 27: (1, {'@': 12}), 128: (1, {'@': 12}), 77: (1, {'@': 12}), 129: (1, {'@': 12}), 78: (1, {'@': 12}), 130: (1, {'@': 12}), 131: (1, {'@': 12}), 79: (1, {'@': 12}), 80: (1, {'@': 12}), 81: (1, {'@': 12}), 82: (1, {'@': 12}), 83: (1, {'@': 12}), 132: (1, {'@': 12}), 133: (1, {'@': 12}), 134: (1, {'@': 12}), 84: (1, {'@': 12}), 85: (1, {'@': 12}), 161: (1, {'@': 12}), 135: (1, {'@': 12}), 136: (1, {'@': 12}), 86: (1, {'@': 12}), 87: (1, {'@': 12}), 137: (1, {'@': 12}), 88: (1, {'@': 12}), 138: (1, {'@': 12}), 89: (1, {'@': 12}), 90: (1, {'@': 12}), 91: (1, {'@': 12}), 92: (1, {'@': 12}), 93: (1, {'@': 12}), 139: (1, {'@': 12}), 140: (1, {'@': 12}), 94: (1, {'@': 12}), 141: (1, {'@': 12}), 95: (1, {'@': 12}), 142: (1, {'@': 12}), 96: (1, {'@': 12}), 143: (1, {'@': 12}), 144: (1, {'@': 12}), 145: (1, {'@': 12}), 146: (1, {'@': 12}), 147: (1, {'@': 12}), 97: (1, {'@': 12}), 98: (1, {'@': 12}), 148: (1, {'@': 12}), 99: (1, {'@': 12}), 149: (1, {'@': 12}), 41: (1, {'@': 12}), 150: (1, {'@': 12}), 151: (1, {'@': 12}), 152: (1, {'@': 12}), 100: (1, {'@': 12}), 101: (1, {'@': 12}), 102: (1, {'@': 12}), 40: (1, {'@': 12}), 153: (1, {'@': 12}), 103: (1, {'@': 12}), 154: (1, {'@': 12}), 104: (1, {'@': 12}), 105: (1, {'@': 12}), 155: (1, {'@': 12}), 106: (1, {'@': 12}), 107: (1, {'@': 12}), 156: (1, {'@': 12}), 157: (1, {'@': 12}), 158: (1, {'@': 12}), 108: (1, {'@': 12}), 109: (1, {'@': 12}), 110: (1, {'@': 12}), 111: (1, {'@': 12}), 159: (1, {'@': 12}), 160: (1, {'@': 12}), 112: (1, {'@': 12}), 162: (1, {'@': 12}), 113: (1, {'@': 12})}, 513: {40: (1, {'@': 248}), 41: (1, {'@': 248})}, 514: {24: (1, {'@': 307}), 33: (1, {'@': 307}), 32: (1, {'@': 307}), 2: (1, {'@': 307}), 26: (1, {'@': 307}), 25: (1, {'@': 307}), 38: (1, {'@': 307}), 31: (1, {'@': 307}), 5: (1, {'@': 307})}, 515: {59: (0, 290)}, 516: {40: (1, {'@': 280}), 41: (1, {'@': 280}), 42: (1, {'@': 280})}, 517: {27: (0, 417), 3: (0, 587), 21: (0, 186), 28: (0, 302), 15: (0, 226), 12: (0, 456), 10: (0, 569), 5: (0, 373), 13: (0, 535), 14: (0, 431)}, 518: {40: (1, {'@': 317}), 41: (1, {'@': 317}), 42: (1, {'@': 317})}, 519: {168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344}), 40: (1, {'@': 61}), 41: (1, {'@': 61})}, 520: {42: (1, {'@': 277}), 41: (1, {'@': 277}), 40: (1, {'@': 277}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 521: {42: (1, {'@': 276}), 41: (1, {'@': 276}), 40: (1, {'@': 276})}, 522: {62: (0, 556), 164: (0, 460), 121: (0, 499), 117: (0, 472), 100: (0, 78), 123: (0, 276), 74: (0, 54), 126: (0, 58), 165: (0, 469), 92: (0, 551), 166: (0, 548), 97: (0, 334), 154: (0, 309), 157: (0, 446), 160: (0, 514), 108: (0, 523), 139: (0, 461), 142: (0, 244), 143: (0, 386), 136: (0, 155), 79: (0, 284), 181: (0, 494), 132: (0, 532), 135: (0, 448), 114: (0, 344), 93: (0, 185), 116: (0, 161), 81: (0, 107), 88: (0, 143), 159: (0, 322), 188: (0, 314), 150: (0, 83), 89: (0, 252), 122: (0, 427), 69: (0, 441), 145: (0, 166), 129: (0, 210), 109: (0, 235), 82: (0, 259), 101: (0, 9), 77: (0, 1), 127: (0, 22), 95: (0, 42), 104: (0, 14), 102: (0, 75), 113: (0, 97), 64: (0, 120), 131: (0, 86), 72: (0, 90), 151: (0, 94), 141: (0, 103), 103: (0, 115), 153: (0, 67), 147: (0, 31), 68: (0, 6), 138: (0, 57), 140: (0, 64), 105: (0, 62), 27: (0, 44), 183: (0, 59), 94: (0, 61), 111: (0, 45), 137: (0, 28), 85: (0, 36), 99: (0, 52), 107: (0, 20), 167: (0, 29), 87: (0, 3), 86: (0, 12), 80: (0, 21), 83: (0, 15), 110: (0, 4), 70: (0, 8), 133: (0, 5), 130: (0, 10), 73: (0, 13), 189: (0, 41), 124: (0, 66), 148: (0, 24), 125: (0, 37), 156: (0, 50), 119: (0, 55), 184: (0, 60), 98: (0, 65), 112: (0, 48), 158: (0, 46), 155: (0, 49), 162: (0, 53), 78: (0, 11), 106: (0, 16), 76: (0, 63), 146: (0, 18), 3: (0, 19), 128: (0, 40), 118: (0, 23), 66: (0, 25), 71: (0, 26), 120: (0, 27), 67: (0, 30), 161: (0, 32), 84: (0, 33), 182: (0, 34), 149: (0, 35), 65: (0, 38), 96: (0, 39), 144: (0, 47), 152: (0, 43), 63: (0, 175), 75: (0, 473), 134: (0, 285), 90: (0, 457), 115: (0, 411), 40: (1, {'@': 16}), 41: (1, {'@': 16}), 91: (1, {'@': 3})}, 523: {40: (1, {'@': 213}), 41: (1, {'@': 213})}, 524: {40: (1, {'@': 325}), 41: (1, {'@': 325}), 42: (1, {'@': 325})}, 525: {168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 42: (1, {'@': 270}), 41: (1, {'@': 270}), 40: (1, {'@': 270}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 526: {40: (1, {'@': 227}), 41: (1, {'@': 227})}, 527: {32: (0, 396)}, 528: {178: (0, 114), 174: (1, {'@': 370}), 168: (1, {'@': 370}), 14: (1, {'@': 370}), 173: (1, {'@': 370}), 169: (1, {'@': 370}), 170: (1, {'@': 370}), 171: (1, {'@': 370}), 65: (1, {'@': 370}), 172: (1, {'@': 370}), 13: (1, {'@': 370}), 175: (1, {'@': 370})}, 529: {168: (1, {'@': 360}), 40: (1, {'@': 360}), 14: (1, {'@': 360}), 169: (1, {'@': 360}), 170: (1, {'@': 360}), 171: (1, {'@': 360}), 172: (1, {'@': 360}), 41: (1, {'@': 360}), 13: (1, {'@': 360}), 174: (1, {'@': 345}), 65: (1, {'@': 345}), 175: (1, {'@': 345}), 42: (1, {'@': 360}), 176: (1, {'@': 360}), 178: (1, {'@': 360}), 180: (1, {'@': 360})}, 530: {42: (0, 280)}, 531: {42: (0, 600), 40: (1, {'@': 69}), 41: (1, {'@': 69})}, 532: {33: (0, 602), 32: (0, 176), 16: (0, 227), 31: (0, 565), 24: (0, 564), 37: (0, 402), 25: (0, 485), 38: (0, 592), 26: (0, 518)}, 533: {40: (1, {'@': 152}), 41: (1, {'@': 152})}, 534: {42: (0, 342)}, 535: {27: (0, 417), 3: (0, 587), 21: (0, 186), 12: (0, 486), 28: (0, 302), 15: (0, 226), 10: (0, 488), 5: (0, 373), 13: (0, 535), 14: (0, 431)}, 536: {178: (0, 117), 174: (1, {'@': 369}), 168: (1, {'@': 369}), 14: (1, {'@': 369}), 173: (1, {'@': 369}), 169: (1, {'@': 369}), 170: (1, {'@': 369}), 171: (1, {'@': 369}), 65: (1, {'@': 369}), 172: (1, {'@': 369}), 13: (1, {'@': 369}), 175: (1, {'@': 369})}, 537: {53: (0, 336), 56: (0, 354), 52: (0, 313)}, 538: {1: (0, 111), 38: (0, 330), 3: (0, 587), 6: (0, 581), 8: (0, 529), 57: (0, 333), 52: (0, 447), 53: (0, 577), 12: (0, 248), 13: (0, 535), 14: (0, 431), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 22: (0, 403), 10: (0, 432), 56: (0, 463), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 539: {40: (1, {'@': 60}), 41: (1, {'@': 60})}, 540: {178: (0, 122), 174: (1, {'@': 369}), 168: (1, {'@': 369}), 14: (1, {'@': 369}), 173: (1, {'@': 369}), 169: (1, {'@': 369}), 170: (1, {'@': 369}), 171: (1, {'@': 369}), 65: (1, {'@': 369}), 172: (1, {'@': 369}), 13: (1, {'@': 369}), 175: (1, {'@': 369})}, 541: {40: (1, {'@': 327}), 41: (1, {'@': 327}), 42: (1, {'@': 327})}, 542: {42: (1, {'@': 339}), 40: (1, {'@': 339}), 41: (1, {'@': 339})}, 543: {178: (0, 128), 174: (1, {'@': 370}), 168: (1, {'@': 370}), 14: (1, {'@': 370}), 173: (1, {'@': 370}), 169: (1, {'@': 370}), 170: (1, {'@': 370}), 171: (1, {'@': 370}), 65: (1, {'@': 370}), 172: (1, {'@': 370}), 13: (1, {'@': 370}), 175: (1, {'@': 370})}, 544: {26: (0, 454)}, 545: {176: (0, 174), 174: (1, {'@': 369}), 168: (1, {'@': 369}), 14: (1, {'@': 369}), 173: (1, {'@': 369}), 169: (1, {'@': 369}), 170: (1, {'@': 369}), 171: (1, {'@': 369}), 65: (1, {'@': 369}), 172: (1, {'@': 369}), 13: (1, {'@': 369}), 175: (1, {'@': 369})}, 546: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 38: (0, 109), 18: (0, 192), 20: (0, 414), 21: (0, 186), 22: (0, 403), 10: (0, 432), 27: (0, 417), 28: (0, 302), 17: (0, 211), 5: (0, 373), 34: (0, 511), 35: (0, 347)}, 547: {40: (1, {'@': 173}), 41: (1, {'@': 173})}, 548: {1: (0, 111), 3: (0, 587), 10: (0, 220), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 22: (0, 465), 17: (0, 211), 18: (0, 192), 21: (0, 186), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 549: {27: (0, 417), 3: (0, 587), 12: (0, 248), 6: (0, 581), 21: (0, 186), 28: (0, 302), 14: (0, 431), 15: (0, 226), 18: (0, 119), 185: (0, 145), 5: (0, 373), 13: (0, 535), 10: (0, 163)}, 550: {40: (1, {'@': 287}), 41: (1, {'@': 287}), 42: (1, {'@': 287})}, 551: {40: (1, {'@': 232}), 41: (1, {'@': 232})}, 552: {25: (0, 526)}, 553: {42: (0, 603)}, 554: {40: (1, {'@': 171}), 41: (1, {'@': 171})}, 555: {42: (0, 130)}, 556: {52: (0, 541), 59: (0, 419), 5: (0, 332), 11: (0, 87), 32: (0, 71), 2: (0, 601), 16: (0, 478), 24: (0, 564), 36: (0, 502), 29: (0, 311), 0: (0, 320), 7: (0, 317), 19: (0, 365), 10: (0, 335), 9: (0, 364), 23: (0, 352), 54: (0, 380), 56: (0, 463), 58: (0, 524), 26: (0, 518), 31: (0, 565), 30: (0, 573), 33: (0, 602), 57: (0, 558), 38: (0, 592), 53: (0, 577), 4: (0, 506), 25: (0, 485), 39: (0, 470), 37: (0, 402)}, 557: {1: (0, 111), 10: (0, 306), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 32: (0, 310), 18: (0, 192), 21: (0, 186), 22: (0, 316), 27: (0, 417), 28: (0, 302), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 558: {40: (1, {'@': 328}), 41: (1, {'@': 328}), 42: (1, {'@': 328})}, 559: {32: (0, 372)}, 560: {32: (0, 184)}, 561: {176: (0, 181), 174: (1, {'@': 370}), 168: (1, {'@': 370}), 14: (1, {'@': 370}), 173: (1, {'@': 370}), 169: (1, {'@': 370}), 170: (1, {'@': 370}), 171: (1, {'@': 370}), 65: (1, {'@': 370}), 172: (1, {'@': 370}), 13: (1, {'@': 370}), 175: (1, {'@': 370})}, 562: {2: (0, 537), 16: (0, 326), 5: (0, 590), 24: (0, 564), 0: (0, 321), 25: (0, 485), 26: (0, 518), 36: (0, 328), 31: (0, 565), 32: (0, 340), 33: (0, 602), 37: (0, 402), 38: (0, 592)}, 563: {40: (1, {'@': 286}), 41: (1, {'@': 286}), 42: (1, {'@': 286})}, 564: {40: (1, {'@': 315}), 41: (1, {'@': 315}), 42: (1, {'@': 315})}, 565: {40: (1, {'@': 314}), 41: (1, {'@': 314}), 42: (1, {'@': 314})}, 566: {176: (0, 164), 174: (1, {'@': 369}), 168: (1, {'@': 369}), 14: (1, {'@': 369}), 173: (1, {'@': 369}), 169: (1, {'@': 369}), 170: (1, {'@': 369}), 171: (1, {'@': 369}), 65: (1, {'@': 369}), 172: (1, {'@': 369}), 13: (1, {'@': 369}), 175: (1, {'@': 369})}, 567: {40: (1, {'@': 172}), 41: (1, {'@': 172})}, 568: {3: (0, 587), 6: (0, 581), 20: (0, 188), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 18: (0, 192), 21: (0, 186), 10: (0, 121), 27: (0, 417), 28: (0, 302), 17: (0, 116), 5: (0, 373), 34: (0, 511)}, 569: {176: (0, 168), 174: (1, {'@': 370}), 168: (1, {'@': 370}), 14: (1, {'@': 370}), 173: (1, {'@': 370}), 169: (1, {'@': 370}), 170: (1, {'@': 370}), 171: (1, {'@': 370}), 65: (1, {'@': 370}), 172: (1, {'@': 370}), 13: (1, {'@': 370}), 175: (1, {'@': 370})}, 570: {40: (1, {'@': 128}), 41: (1, {'@': 128})}, 571: {3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 18: (0, 192), 21: (0, 186), 10: (0, 121), 20: (0, 190), 27: (0, 417), 28: (0, 302), 17: (0, 116), 5: (0, 373), 34: (0, 511)}, 572: {176: (0, 139)}, 573: {42: (0, 228)}, 574: {42: (1, {'@': 269}), 41: (1, {'@': 269}), 40: (1, {'@': 269})}, 575: {1: (0, 111), 0: (0, 416), 2: (0, 537), 3: (0, 587), 16: (0, 424), 5: (0, 263), 6: (0, 581), 7: (0, 317), 14: (0, 431), 8: (0, 529), 13: (0, 535), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 19: (0, 365), 20: (0, 414), 21: (0, 186), 10: (0, 425), 38: (0, 592), 23: (0, 352), 9: (0, 429), 24: (0, 564), 25: (0, 485), 26: (0, 518), 27: (0, 417), 28: (0, 302), 29: (0, 311), 31: (0, 565), 34: (0, 511), 36: (0, 433), 33: (0, 602), 35: (0, 347), 22: (0, 434), 37: (0, 402), 32: (0, 439)}, 576: {176: (0, 146)}, 577: {40: (1, {'@': 330}), 41: (1, {'@': 330}), 42: (1, {'@': 330}), 176: (1, {'@': 330}), 178: (1, {'@': 330})}, 578: {40: (1, {'@': 151}), 41: (1, {'@': 151})}, 579: {178: (0, 151)}, 580: {42: (0, 305)}, 581: {173: (0, 549)}, 582: {32: (0, 503)}, 583: {178: (0, 157)}, 584: {1: (0, 111), 10: (0, 179), 2: (0, 537), 3: (0, 587), 5: (0, 263), 6: (0, 581), 7: (0, 317), 16: (0, 409), 9: (0, 77), 36: (0, 82), 8: (0, 529), 13: (0, 535), 12: (0, 248), 14: (0, 431), 15: (0, 226), 17: (0, 211), 19: (0, 365), 18: (0, 192), 38: (0, 592), 20: (0, 414), 21: (0, 186), 23: (0, 352), 24: (0, 564), 25: (0, 485), 22: (0, 85), 26: (0, 518), 27: (0, 417), 29: (0, 311), 28: (0, 302), 0: (0, 88), 32: (0, 92), 31: (0, 565), 34: (0, 511), 33: (0, 602), 35: (0, 347), 37: (0, 402)}, 585: {42: (0, 588)}, 586: {40: (1, {'@': 110}), 41: (1, {'@': 110})}, 587: {174: (1, {'@': 374}), 168: (1, {'@': 374}), 40: (1, {'@': 374}), 14: (1, {'@': 374}), 173: (1, {'@': 374}), 169: (1, {'@': 374}), 170: (1, {'@': 374}), 171: (1, {'@': 374}), 65: (1, {'@': 374}), 172: (1, {'@': 374}), 41: (1, {'@': 374}), 13: (1, {'@': 374}), 175: (1, {'@': 374}), 42: (1, {'@': 374}), 176: (1, {'@': 374}), 178: (1, {'@': 374}), 180: (1, {'@': 374})}, 588: {32: (0, 191)}, 589: {26: (0, 376)}, 590: {56: (0, 405), 53: (0, 394), 52: (0, 447)}, 591: {32: (0, 209), 33: (0, 602), 31: (0, 565), 24: (0, 564), 37: (0, 402), 25: (0, 485), 38: (0, 592), 26: (0, 518), 16: (0, 216)}, 592: {40: (1, {'@': 318}), 41: (1, {'@': 318}), 42: (1, {'@': 318})}, 593: {40: (1, {'@': 177}), 41: (1, {'@': 177}), 168: (1, {'@': 342}), 169: (1, {'@': 342}), 170: (1, {'@': 342}), 171: (1, {'@': 342}), 172: (1, {'@': 342}), 173: (1, {'@': 348}), 174: (1, {'@': 346}), 65: (1, {'@': 346}), 175: (1, {'@': 346}), 13: (1, {'@': 344}), 14: (1, {'@': 344})}, 594: {3: (0, 587), 6: (0, 581), 13: (0, 535), 14: (0, 431), 15: (0, 226), 12: (0, 248), 18: (0, 192), 34: (0, 199), 21: (0, 186), 10: (0, 149), 27: (0, 417), 28: (0, 302), 5: (0, 373), 8: (0, 229)}, 595: {32: (0, 331)}, 596: {33: (0, 602), 16: (0, 197), 31: (0, 565), 24: (0, 564), 37: (0, 402), 32: (0, 202), 25: (0, 485), 38: (0, 592), 26: (0, 518)}, 597: {91: (1, {'@': 4}), 41: (1, {'@': 4})}, 598: {10: (0, 561), 27: (0, 417), 3: (0, 587), 21: (0, 186), 28: (0, 302), 15: (0, 226), 12: (0, 456), 5: (0, 373), 13: (0, 535), 14: (0, 431)}, 599: {40: (1, {'@': 94}), 41: (1, {'@': 94})}, 600: {1: (0, 111), 3: (0, 587), 6: (0, 581), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 17: (0, 211), 18: (0, 192), 21: (0, 186), 10: (0, 253), 27: (0, 417), 28: (0, 302), 22: (0, 295), 5: (0, 373), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 601: {1: (0, 111), 3: (0, 587), 53: (0, 336), 6: (0, 581), 56: (0, 354), 8: (0, 529), 13: (0, 535), 14: (0, 431), 12: (0, 248), 15: (0, 226), 58: (0, 338), 17: (0, 211), 18: (0, 192), 21: (0, 186), 52: (0, 313), 10: (0, 253), 27: (0, 417), 28: (0, 302), 5: (0, 373), 22: (0, 269), 39: (0, 277), 34: (0, 511), 20: (0, 414), 35: (0, 347)}, 602: {40: (1, {'@': 319}), 41: (1, {'@': 319}), 42: (1, {'@': 319})}, 603: {52: (0, 246), 39: (0, 303), 58: (0, 159), 59: (0, 599)}, 604: {38: (0, 102)}, 605: {40: (1, {'@': 46}), 41: (1, {'@': 46})}, 606: {42: (0, 135)}, 607: {62: (1, {'@': 9}), 63: (1, {'@': 9}), 64: (1, {'@': 9}), 65: (1, {'@': 9}), 66: (1, {'@': 9}), 67: (1, {'@': 9}), 68: (1, {'@': 9}), 69: (1, {'@': 9}), 70: (1, {'@': 9}), 71: (1, {'@': 9}), 72: (1, {'@': 9}), 73: (1, {'@': 9}), 74: (1, {'@': 9}), 75: (1, {'@': 9}), 76: (1, {'@': 9}), 27: (1, {'@': 9}), 77: (1, {'@': 9}), 78: (1, {'@': 9}), 79: (1, {'@': 9}), 80: (1, {'@': 9}), 81: (1, {'@': 9}), 82: (1, {'@': 9}), 83: (1, {'@': 9}), 84: (1, {'@': 9}), 85: (1, {'@': 9}), 86: (1, {'@': 9}), 87: (1, {'@': 9}), 88: (1, {'@': 9}), 89: (1, {'@': 9}), 90: (1, {'@': 9}), 91: (1, {'@': 9}), 92: (1, {'@': 9}), 93: (1, {'@': 9}), 94: (1, {'@': 9}), 95: (1, {'@': 9}), 96: (1, {'@': 9}), 97: (1, {'@': 9}), 98: (1, {'@': 9}), 99: (1, {'@': 9}), 100: (1, {'@': 9}), 101: (1, {'@': 9}), 102: (1, {'@': 9}), 40: (1, {'@': 9}), 103: (1, {'@': 9}), 104: (1, {'@': 9}), 105: (1, {'@': 9}), 106: (1, {'@': 9}), 107: (1, {'@': 9}), 108: (1, {'@': 9}), 109: (1, {'@': 9}), 110: (1, {'@': 9}), 111: (1, {'@': 9}), 112: (1, {'@': 9}), 113: (1, {'@': 9}), 3: (1, {'@': 9}), 114: (1, {'@': 9}), 115: (1, {'@': 9}), 116: (1, {'@': 9}), 117: (1, {'@': 9}), 118: (1, {'@': 9}), 119: (1, {'@': 9}), 120: (1, {'@': 9}), 121: (1, {'@': 9}), 122: (1, {'@': 9}), 123: (1, {'@': 9}), 124: (1, {'@': 9}), 125: (1, {'@': 9}), 126: (1, {'@': 9}), 127: (1, {'@': 9}), 128: (1, {'@': 9}), 129: (1, {'@': 9}), 130: (1, {'@': 9}), 131: (1, {'@': 9}), 132: (1, {'@': 9}), 133: (1, {'@': 9}), 134: (1, {'@': 9}), 135: (1, {'@': 9}), 136: (1, {'@': 9}), 137: (1, {'@': 9}), 138: (1, {'@': 9}), 139: (1, {'@': 9}), 140: (1, {'@': 9}), 141: (1, {'@': 9}), 142: (1, {'@': 9}), 143: (1, {'@': 9}), 144: (1, {'@': 9}), 145: (1, {'@': 9}), 146: (1, {'@': 9}), 147: (1, {'@': 9}), 148: (1, {'@': 9}), 149: (1, {'@': 9}), 41: (1, {'@': 9}), 150: (1, {'@': 9}), 151: (1, {'@': 9}), 152: (1, {'@': 9}), 153: (1, {'@': 9}), 154: (1, {'@': 9}), 155: (1, {'@': 9}), 156: (1, {'@': 9}), 157: (1, {'@': 9}), 158: (1, {'@': 9}), 159: (1, {'@': 9}), 160: (1, {'@': 9}), 161: (1, {'@': 9}), 162: (1, {'@': 9})}}, 'start_states': {'start': 522}, 'end_states': {'start': 41}}, '__type__': 'ParsingFrontend'}, 'rules': [{'@': 0}, {'@': 1}, {'@': 2}, {'@': 3}, {'@': 4}, {'@': 5}, {'@': 6}, {'@': 7}, {'@': 8}, {'@': 9}, {'@': 10}, {'@': 11}, {'@': 12}, {'@': 13}, {'@': 14}, {'@': 15}, {'@': 16}, {'@': 17}, {'@': 18}, {'@': 19}, {'@': 20}, {'@': 21}, {'@': 22}, {'@': 23}, {'@': 24}, {'@': 25}, {'@': 26}, {'@': 27}, {'@': 28}, {'@': 29}, {'@': 30}, {'@': 31}, {'@': 32}, {'@': 33}, {'@': 34}, {'@': 35}, {'@': 36}, {'@': 37}, {'@': 38}, {'@': 39}, {'@': 40}, {'@': 41}, {'@': 42}, {'@': 43}, {'@': 44}, {'@': 45}, {'@': 46}, {'@': 47}, {'@': 48}, {'@': 49}, {'@': 50}, {'@': 51}, {'@': 52}, {'@': 53}, {'@': 54}, {'@': 55}, {'@': 56}, {'@': 57}, {'@': 58}, {'@': 59}, {'@': 60}, {'@': 61}, {'@': 62}, {'@': 63}, {'@': 64}, {'@': 65}, {'@': 66}, {'@': 67}, {'@': 68}, {'@': 69}, {'@': 70}, {'@': 71}, {'@': 72}, {'@': 73}, {'@': 74}, {'@': 75}, {'@': 76}, {'@': 77}, {'@': 78}, {'@': 79}, {'@': 80}, {'@': 81}, {'@': 82}, {'@': 83}, {'@': 84}, {'@': 85}, {'@': 86}, {'@': 87}, {'@': 88}, {'@': 89}, {'@': 90}, {'@': 91}, {'@': 92}, {'@': 93}, {'@': 94}, {'@': 95}, {'@': 96}, {'@': 97}, {'@': 98}, {'@': 99}, {'@': 100}, {'@': 101}, {'@': 102}, {'@': 103}, {'@': 104}, {'@': 105}, {'@': 106}, {'@': 107}, {'@': 108}, {'@': 109}, {'@': 110}, {'@': 111}, {'@': 112}, {'@': 113}, {'@': 114}, {'@': 115}, {'@': 116}, {'@': 117}, {'@': 118}, {'@': 119}, {'@': 120}, {'@': 121}, {'@': 122}, {'@': 123}, {'@': 124}, {'@': 125}, {'@': 126}, {'@': 127}, {'@': 128}, {'@': 129}, {'@': 130}, {'@': 131}, {'@': 132}, {'@': 133}, {'@': 134}, {'@': 135}, {'@': 136}, {'@': 137}, {'@': 138}, {'@': 139}, {'@': 140}, {'@': 141}, {'@': 142}, {'@': 143}, {'@': 144}, {'@': 145}, {'@': 146}, {'@': 147}, {'@': 148}, {'@': 149}, {'@': 150}, {'@': 151}, {'@': 152}, {'@': 153}, {'@': 154}, {'@': 155}, {'@': 156}, {'@': 157}, {'@': 158}, {'@': 159}, {'@': 160}, {'@': 161}, {'@': 162}, {'@': 163}, {'@': 164}, {'@': 165}, {'@': 166}, {'@': 167}, {'@': 168}, {'@': 169}, {'@': 170}, {'@': 171}, {'@': 172}, {'@': 173}, {'@': 174}, {'@': 175}, {'@': 176}, {'@': 177}, {'@': 178}, {'@': 179}, {'@': 180}, {'@': 181}, {'@': 182}, {'@': 183}, {'@': 184}, {'@': 185}, {'@': 186}, {'@': 187}, {'@': 188}, {'@': 189}, {'@': 190}, {'@': 191}, {'@': 192}, {'@': 193}, {'@': 194}, {'@': 195}, {'@': 196}, {'@': 197}, {'@': 198}, {'@': 199}, {'@': 200}, {'@': 201}, {'@': 202}, {'@': 203}, {'@': 204}, {'@': 205}, {'@': 206}, {'@': 207}, {'@': 208}, {'@': 209}, {'@': 210}, {'@': 211}, {'@': 212}, {'@': 213}, {'@': 214}, {'@': 215}, {'@': 216}, {'@': 217}, {'@': 218}, {'@': 219}, {'@': 220}, {'@': 221}, {'@': 222}, {'@': 223}, {'@': 224}, {'@': 225}, {'@': 226}, {'@': 227}, {'@': 228}, {'@': 229}, {'@': 230}, {'@': 231}, {'@': 232}, {'@': 233}, {'@': 234}, {'@': 235}, {'@': 236}, {'@': 237}, {'@': 238}, {'@': 239}, {'@': 240}, {'@': 241}, {'@': 242}, {'@': 243}, {'@': 244}, {'@': 245}, {'@': 246}, {'@': 247}, {'@': 248}, {'@': 249}, {'@': 250}, {'@': 251}, {'@': 252}, {'@': 253}, {'@': 254}, {'@': 255}, {'@': 256}, {'@': 257}, {'@': 258}, {'@': 259}, {'@': 260}, {'@': 261}, {'@': 262}, {'@': 263}, {'@': 264}, {'@': 265}, {'@': 266}, {'@': 267}, {'@': 268}, {'@': 269}, {'@': 270}, {'@': 271}, {'@': 272}, {'@': 273}, {'@': 274}, {'@': 275}, {'@': 276}, {'@': 277}, {'@': 278}, {'@': 279}, {'@': 280}, {'@': 281}, {'@': 282}, {'@': 283}, {'@': 284}, {'@': 285}, {'@': 286}, {'@': 287}, {'@': 288}, {'@': 289}, {'@': 290}, {'@': 291}, {'@': 292}, {'@': 293}, {'@': 294}, {'@': 295}, {'@': 296}, {'@': 297}, {'@': 298}, {'@': 299}, {'@': 300}, {'@': 301}, {'@': 302}, {'@': 303}, {'@': 304}, {'@': 305}, {'@': 306}, {'@': 307}, {'@': 308}, {'@': 309}, {'@': 310}, {'@': 311}, {'@': 312}, {'@': 313}, {'@': 314}, {'@': 315}, {'@': 316}, {'@': 317}, {'@': 318}, {'@': 319}, {'@': 320}, {'@': 321}, {'@': 322}, {'@': 323}, {'@': 324}, {'@': 325}, {'@': 326}, {'@': 327}, {'@': 328}, {'@': 329}, {'@': 330}, {'@': 331}, {'@': 332}, {'@': 333}, {'@': 334}, {'@': 335}, {'@': 336}, {'@': 337}, {'@': 338}, {'@': 339}, {'@': 340}, {'@': 341}, {'@': 342}, {'@': 343}, {'@': 344}, {'@': 345}, {'@': 346}, {'@': 347}, {'@': 348}, {'@': 349}, {'@': 350}, {'@': 351}, {'@': 352}, {'@': 353}, {'@': 354}, {'@': 355}, {'@': 356}, {'@': 357}, {'@': 358}, {'@': 359}, {'@': 360}, {'@': 361}, {'@': 362}, {'@': 363}, {'@': 364}, {'@': 365}, {'@': 366}, {'@': 367}, {'@': 368}, {'@': 369}, {'@': 370}, {'@': 371}, {'@': 372}, {'@': 373}, {'@': 374}, {'@': 375}, {'@': 376}, {'@': 377}], 'options': {'debug': False, 'strict': False, 'keep_all_tokens': False, 'tree_class': None, 'cache': False, 'cache_grammar': False, 'postlex': None, 'parser': 'lalr', 'lexer': 'contextual', 'transformer': None, 'start': ['start'], 'priority': 'normal', 'ambiguity': 'auto', 'regex': False, 'propagate_positions': False, 'lexer_callbacks': {}, 'maybe_placeholders': False, 'edit_terminals': None, 'g_regex_flags': 0, 'use_bytes': False, 'ordered_sets': True, 'import_paths': [], 'source_path': None, '_plugins': {}}, '__type__': 'Lark'} +) +MEMO = ( +{0: {'origin': {'name': 'start', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 1: {'origin': {'name': 'start', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'endline', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 2: {'origin': {'name': 'start', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'endline', '__type__': 'NonTerminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 3: {'origin': {'name': 'start', '__type__': 'NonTerminal'}, 'expansion': [], 'order': 3, 'alias': 'empty_program', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 4: {'origin': {'name': 'endline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'END', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 5: {'origin': {'name': 'endline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'endline', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 6: {'origin': {'name': 'endline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'END', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'program_endline2', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 7: {'origin': {'name': 'endline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'END', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'program_endline2', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 8: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'line', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 9: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'line', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 10: {'origin': {'name': 'line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQU', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'def_label', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 11: {'origin': {'name': 'line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQU', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'def_label', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 12: {'origin': {'name': 'line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'asms', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'line_asm', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 13: {'origin': {'name': 'line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'asms', '__type__': 'NonTerminal'}, {'name': 'CO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'line_asm', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 14: {'origin': {'name': 'line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'preproc_line', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'preprocessor_line', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 15: {'origin': {'name': 'preproc_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': '_INIT', 'filter_out': True, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'preproc_line_init', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 16: {'origin': {'name': 'asms', '__type__': 'NonTerminal'}, 'expansion': [], 'order': 0, 'alias': 'asms_empty', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 17: {'origin': {'name': 'asms', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'asm', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'asms_asm', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 18: {'origin': {'name': 'asms', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'asms', '__type__': 'NonTerminal'}, {'name': 'CO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'asm', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'asms_asms_asm', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 19: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'asm_label', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 20: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INTEGER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'asm_label', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 21: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 22: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 23: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 24: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 25: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}], 'order': 6, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 26: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 7, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 27: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 8, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 28: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 9, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 29: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 10, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 30: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 11, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 31: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'I', 'filter_out': False, '__type__': 'Terminal'}], 'order': 12, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 32: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'I', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 13, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 33: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'R', 'filter_out': False, '__type__': 'Terminal'}], 'order': 14, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 34: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'R', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 15, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 35: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 16, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 36: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 17, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 37: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 18, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 38: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg_bcde', '__type__': 'NonTerminal'}], 'order': 19, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 39: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 20, 'alias': 'asm_ld8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 40: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 21, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 41: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 22, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 42: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 23, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 43: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 24, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 44: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 25, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 45: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 26, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 46: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 27, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 47: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 28, 'alias': 'ld_a_instr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 48: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PROC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 29, 'alias': 'proc_scope', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 49: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ENDP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 30, 'alias': 'endp_scope', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 50: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LOCAL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'id_list', '__type__': 'NonTerminal'}], 'order': 31, 'alias': 'local_labels', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 51: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DEFB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_list', '__type__': 'NonTerminal'}], 'order': 32, 'alias': 'defb_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 52: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DEFS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'number_list', '__type__': 'NonTerminal'}], 'order': 33, 'alias': 'defs_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 53: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DEFW', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'number_list', '__type__': 'NonTerminal'}], 'order': 34, 'alias': 'defw_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 54: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 35, 'alias': 'asm_ldind_r8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 55: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 36, 'alias': 'asm_ldind_r8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 56: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 37, 'alias': 'asm_ldr8_ind', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 57: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 38, 'alias': 'asm_ldr8_ind', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 58: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'AF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'AF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'APO', 'filter_out': False, '__type__': 'Terminal'}], 'order': 39, 'alias': 'ex_af_af', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 59: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 40, 'alias': 'ex_de_hl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 60: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ORG', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 41, 'alias': 'org', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 61: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ORG', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 42, 'alias': 'org', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 62: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NAMESPACE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 43, 'alias': 'namespace', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 63: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PUSH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NAMESPACE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 44, 'alias': 'push_namespace', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 64: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PUSH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NAMESPACE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 45, 'alias': 'push_namespace', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 65: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'POP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NAMESPACE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 46, 'alias': 'pop_namespace', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 66: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ALIGN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 47, 'alias': 'align', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 67: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ALIGN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 48, 'alias': 'align', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 68: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INCBIN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}], 'order': 49, 'alias': 'incbin', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 69: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INCBIN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 50, 'alias': 'incbin', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 70: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INCBIN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 51, 'alias': 'incbin', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 71: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}], 'order': 52, 'alias': 'ex_sp_reg8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 72: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}], 'order': 53, 'alias': 'ex_sp_reg8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 73: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 54, 'alias': 'ex_sp_reg8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 74: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 55, 'alias': 'ex_sp_reg8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 75: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inc_reg', '__type__': 'NonTerminal'}], 'order': 56, 'alias': 'incdec', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 76: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DEC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'inc_reg', '__type__': 'NonTerminal'}], 'order': 57, 'alias': 'incdec', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 77: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 58, 'alias': 'incdeci', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 78: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DEC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 59, 'alias': 'incdeci', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 79: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 60, 'alias': 'ld_reg_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 80: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 61, 'alias': 'ld_reg_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 81: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 62, 'alias': 'ld_reg_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 82: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 63, 'alias': 'ld_reg_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 83: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 64, 'alias': 'ld_reg_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 84: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 65, 'alias': 'ld_reg_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 85: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 66, 'alias': 'ld_reg_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 86: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 67, 'alias': 'ld_reg_val_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 87: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 68, 'alias': 'jp_hl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 88: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 69, 'alias': 'jp_hl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 89: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 70, 'alias': 'jp_hl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 90: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 71, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 91: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 72, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 92: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 73, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 93: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 74, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 94: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 75, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 95: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 76, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 96: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 77, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 97: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 78, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 98: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 79, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 99: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 80, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 100: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 81, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 101: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 82, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 102: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 83, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 103: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 84, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 104: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 85, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 105: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 86, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 106: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 87, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 107: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 88, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 108: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 89, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 109: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 90, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 110: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 91, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 111: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 92, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 112: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 93, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 113: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 94, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 114: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 95, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 115: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 96, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 116: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 97, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 117: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 98, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 118: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16i', '__type__': 'NonTerminal'}], 'order': 99, 'alias': 'sbcadd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 119: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 100, 'alias': 'arith_a_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 120: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 101, 'alias': 'arith_a_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 121: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 102, 'alias': 'arith_a_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 122: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 103, 'alias': 'arith_a_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 123: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 104, 'alias': 'arith_a_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 124: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 105, 'alias': 'arith_a_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 125: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SBC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 106, 'alias': 'arith_a_reg_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 126: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 107, 'alias': 'arith_a_reg_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 127: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 108, 'alias': 'arith_a_reg_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 128: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitwiseop', '__type__': 'NonTerminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 109, 'alias': 'bitwiseop_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 129: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitwiseop', '__type__': 'NonTerminal'}, {'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 110, 'alias': 'bitwiseop_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 130: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitwiseop', '__type__': 'NonTerminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 111, 'alias': 'bitwiseop_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 131: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitwiseop', '__type__': 'NonTerminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 112, 'alias': 'bitwiseop_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 132: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitwiseop', '__type__': 'NonTerminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 113, 'alias': 'bitwiseop_reg_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 133: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitwiseop', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 114, 'alias': 'bitwise_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 134: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitwiseop', '__type__': 'NonTerminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 115, 'alias': 'bitwise_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 135: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PUSH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'AF', 'filter_out': False, '__type__': 'Terminal'}], 'order': 116, 'alias': 'push_pop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 136: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PUSH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16', '__type__': 'NonTerminal'}], 'order': 117, 'alias': 'push_pop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 137: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'POP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'AF', 'filter_out': False, '__type__': 'Terminal'}], 'order': 118, 'alias': 'push_pop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 138: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'POP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16', '__type__': 'NonTerminal'}], 'order': 119, 'alias': 'push_pop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 139: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 120, 'alias': 'ld_addr_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 140: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16', '__type__': 'NonTerminal'}], 'order': 121, 'alias': 'ld_addr_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 141: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 122, 'alias': 'ld_addr_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 142: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 123, 'alias': 'ld_addr_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 143: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16', '__type__': 'NonTerminal'}], 'order': 124, 'alias': 'ld_addr_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 144: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 125, 'alias': 'ld_addr_reg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 145: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 126, 'alias': 'ld_reg_addr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 146: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 127, 'alias': 'ld_reg_addr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 147: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 128, 'alias': 'ld_reg_addr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 148: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}], 'order': 129, 'alias': 'ld_reg_addr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 149: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg16', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}], 'order': 130, 'alias': 'ld_reg_addr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 150: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}], 'order': 131, 'alias': 'ld_reg_addr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 151: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'rotation', '__type__': 'NonTerminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 132, 'alias': 'rotate', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 152: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'rotation', '__type__': 'NonTerminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 133, 'alias': 'rotate', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 153: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'rotation', '__type__': 'NonTerminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 134, 'alias': 'rotate', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 154: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'rotation', '__type__': 'NonTerminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 135, 'alias': 'rotate_ix', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 155: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 136, 'alias': 'bit', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 156: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 137, 'alias': 'bit', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 157: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 138, 'alias': 'bit', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 158: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 139, 'alias': 'bit', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 159: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 140, 'alias': 'bit', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 160: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 141, 'alias': 'bit', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 161: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 142, 'alias': 'bit_ix', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 162: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bitop', '__type__': 'NonTerminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8_i', '__type__': 'NonTerminal'}], 'order': 143, 'alias': 'bit_ix', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 163: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'jp_flags', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 144, 'alias': 'jp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 164: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'jp_flags', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 145, 'alias': 'jp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 165: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CALL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'jp_flags', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 146, 'alias': 'jp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 166: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CALL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'jp_flags', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 147, 'alias': 'jp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 167: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'jp_flags', '__type__': 'NonTerminal'}], 'order': 148, 'alias': 'ret', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 168: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'jr_flags', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 149, 'alias': 'jr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 169: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'jr_flags', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 150, 'alias': 'jr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 170: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 151, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 171: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 152, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 172: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CALL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 153, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 173: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DJNZ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 154, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 174: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 155, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 175: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 156, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 176: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CALL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 157, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 177: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DJNZ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 158, 'alias': 'jrjp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 178: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RST', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 159, 'alias': 'rst', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 179: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IM', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 160, 'alias': 'im', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 180: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 161, 'alias': 'in_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 181: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 162, 'alias': 'in_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 182: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 163, 'alias': 'in_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 183: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 164, 'alias': 'in_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 184: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 165, 'alias': 'out_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 185: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 166, 'alias': 'out_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 186: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 167, 'alias': 'out_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 187: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 168, 'alias': 'out_op', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 188: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}], 'order': 169, 'alias': 'in_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 189: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 170, 'alias': 'in_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 190: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'mem_indir', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 171, 'alias': 'out_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 191: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 172, 'alias': 'out_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 192: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NOP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 173, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 193: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EXX', 'filter_out': False, '__type__': 'Terminal'}], 'order': 174, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 194: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CCF', 'filter_out': False, '__type__': 'Terminal'}], 'order': 175, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 195: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SCF', 'filter_out': False, '__type__': 'Terminal'}], 'order': 176, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 196: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LDIR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 177, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 197: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LDI', 'filter_out': False, '__type__': 'Terminal'}], 'order': 178, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 198: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LDDR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 179, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 199: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LDD', 'filter_out': False, '__type__': 'Terminal'}], 'order': 180, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 200: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CPIR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 181, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 201: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CPI', 'filter_out': False, '__type__': 'Terminal'}], 'order': 182, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 202: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CPDR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 183, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 203: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CPD', 'filter_out': False, '__type__': 'Terminal'}], 'order': 184, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 204: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DAA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 185, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 205: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NEG', 'filter_out': False, '__type__': 'Terminal'}], 'order': 186, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 206: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CPL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 187, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 207: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'HALT', 'filter_out': False, '__type__': 'Terminal'}], 'order': 188, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 208: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EI', 'filter_out': False, '__type__': 'Terminal'}], 'order': 189, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 209: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DI', 'filter_out': False, '__type__': 'Terminal'}], 'order': 190, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 210: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUTD', 'filter_out': False, '__type__': 'Terminal'}], 'order': 191, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 211: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUTI', 'filter_out': False, '__type__': 'Terminal'}], 'order': 192, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 212: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OTDR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 193, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 213: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OTIR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 194, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 214: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IND', 'filter_out': False, '__type__': 'Terminal'}], 'order': 195, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 215: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INI', 'filter_out': False, '__type__': 'Terminal'}], 'order': 196, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 216: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INDR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 197, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 217: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INIR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 198, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 218: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RET', 'filter_out': False, '__type__': 'Terminal'}], 'order': 199, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 219: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RETI', 'filter_out': False, '__type__': 'Terminal'}], 'order': 200, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 220: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RETN', 'filter_out': False, '__type__': 'Terminal'}], 'order': 201, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 221: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RLA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 202, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 222: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RLCA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 203, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 223: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RRA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 204, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 224: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RRCA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 205, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 225: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RLD', 'filter_out': False, '__type__': 'Terminal'}], 'order': 206, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 226: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RRD', 'filter_out': False, '__type__': 'Terminal'}], 'order': 207, 'alias': 'single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 227: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'MUL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'D', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'E', 'filter_out': False, '__type__': 'Terminal'}], 'order': 208, 'alias': 'mul_d_e', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 228: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LDIX', 'filter_out': False, '__type__': 'Terminal'}], 'order': 209, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 229: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LDWS', 'filter_out': False, '__type__': 'Terminal'}], 'order': 210, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 230: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LDIRX', 'filter_out': False, '__type__': 'Terminal'}], 'order': 211, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 231: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LDDX', 'filter_out': False, '__type__': 'Terminal'}], 'order': 212, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 232: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LDDRX', 'filter_out': False, '__type__': 'Terminal'}], 'order': 213, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 233: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LDPIRX', 'filter_out': False, '__type__': 'Terminal'}], 'order': 214, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 234: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUTINB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 215, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 235: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SWAPNIB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 216, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 236: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'MIRROR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 217, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 237: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PIXELDN', 'filter_out': False, '__type__': 'Terminal'}], 'order': 218, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 238: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PIXELAD', 'filter_out': False, '__type__': 'Terminal'}], 'order': 219, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 239: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SETAE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 220, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 240: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 221, 'alias': 'add_reg16_a', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 241: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 222, 'alias': 'add_reg16_a', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 242: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 223, 'alias': 'add_reg16_a', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 243: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 224, 'alias': 'jp_c', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 244: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'JP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'C', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 225, 'alias': 'jp_c', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 245: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BSLA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'B', 'filter_out': False, '__type__': 'Terminal'}], 'order': 226, 'alias': 'bxxxx_de_b', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 246: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BSRA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'B', 'filter_out': False, '__type__': 'Terminal'}], 'order': 227, 'alias': 'bxxxx_de_b', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 247: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BSRL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'B', 'filter_out': False, '__type__': 'Terminal'}], 'order': 228, 'alias': 'bxxxx_de_b', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 248: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BSRF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'B', 'filter_out': False, '__type__': 'Terminal'}], 'order': 229, 'alias': 'bxxxx_de_b', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 249: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BRLC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'B', 'filter_out': False, '__type__': 'Terminal'}], 'order': 230, 'alias': 'bxxxx_de_b', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 250: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 231, 'alias': 'add_reg_nn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 251: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 232, 'alias': 'add_reg_nn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 252: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 233, 'alias': 'add_reg_nn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 253: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 234, 'alias': 'add_reg_nn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 254: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 235, 'alias': 'add_reg_nn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 255: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 236, 'alias': 'add_reg_nn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 256: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'TEST', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 237, 'alias': 'test_nn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 257: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'TEST', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 238, 'alias': 'test_nn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 258: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NEXTREG', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 239, 'alias': 'nextreg_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 259: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NEXTREG', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 240, 'alias': 'nextreg_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 260: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NEXTREG', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 241, 'alias': 'nextreg_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 261: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NEXTREG', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 242, 'alias': 'nextreg_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 262: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NEXTREG', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 243, 'alias': 'nextreg_a', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 263: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NEXTREG', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 244, 'alias': 'nextreg_a', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 264: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PUSH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 245, 'alias': 'push_imm', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 265: {'origin': {'name': 'asm', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PUSH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 246, 'alias': 'push_imm', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 266: {'origin': {'name': 'id_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'idlist', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 267: {'origin': {'name': 'id_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'id_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'idlist_id', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 268: {'origin': {'name': 'expr_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'expr_list_from_string', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 269: {'origin': {'name': 'expr_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'expr_list_from_num', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 270: {'origin': {'name': 'expr_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'expr_list_from_num', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 271: {'origin': {'name': 'expr_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'expr_list_plus_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 272: {'origin': {'name': 'expr_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'expr_list_plus_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 273: {'origin': {'name': 'expr_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'expr_list_plus_string', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 274: {'origin': {'name': 'number_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'number_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 275: {'origin': {'name': 'number_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'number_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 276: {'origin': {'name': 'number_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'number_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'number_list_number', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 277: {'origin': {'name': 'number_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'number_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'number_list_number', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 278: {'origin': {'name': 'reg8_hl', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'reg8_hl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 279: {'origin': {'name': 'reg8_hl', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'reg8_hl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 280: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 281: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IY', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 282: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 283: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 284: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IY', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 285: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IY', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 286: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 6, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 287: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IY', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 7, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 288: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 8, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 289: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IX', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 9, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 290: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IY', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 10, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 291: {'origin': {'name': 'reg8_i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'IY', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 11, 'alias': 'ind8_i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 292: {'origin': {'name': 'bitwiseop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'bitwise', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 293: {'origin': {'name': 'bitwiseop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'AND', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'bitwise', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 294: {'origin': {'name': 'bitwiseop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'XOR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'bitwise', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 295: {'origin': {'name': 'bitwiseop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SUB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'bitwise', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 296: {'origin': {'name': 'bitwiseop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'bitwise', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 297: {'origin': {'name': 'bitop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BIT', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'bitop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 298: {'origin': {'name': 'bitop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RES', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'bitop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 299: {'origin': {'name': 'bitop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SET', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'bitop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 300: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 301: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 302: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RRC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 303: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RLC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 304: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 305: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SLL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 306: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SRA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 6, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 307: {'origin': {'name': 'rotation', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SRL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 7, 'alias': 'rotation', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 308: {'origin': {'name': 'inc_reg', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'reg_inc', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 309: {'origin': {'name': 'inc_reg', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'reg8', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'reg_inc', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 310: {'origin': {'name': 'inc_reg', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'reg16', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'reg_inc', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 311: {'origin': {'name': 'inc_reg', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'reg8_hl', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'reg_inc', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 312: {'origin': {'name': 'inc_reg', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'A', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'reg_inc', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 313: {'origin': {'name': 'inc_reg', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'reg8i', '__type__': 'NonTerminal'}], 'order': 5, 'alias': 'reg_inc', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 314: {'origin': {'name': 'reg8', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'H', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'reg8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 315: {'origin': {'name': 'reg8', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'L', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'reg8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 316: {'origin': {'name': 'reg8', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'reg_bcde', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'reg8', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 317: {'origin': {'name': 'reg_bcde', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'B', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'reg_bcde', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 318: {'origin': {'name': 'reg_bcde', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'C', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'reg_bcde', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 319: {'origin': {'name': 'reg_bcde', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'D', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'reg_bcde', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 320: {'origin': {'name': 'reg_bcde', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'E', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'reg_bcde', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 321: {'origin': {'name': 'reg8i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IXH', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'reg8i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 322: {'origin': {'name': 'reg8i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IXL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'reg8i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 323: {'origin': {'name': 'reg8i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IYH', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'reg8i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 324: {'origin': {'name': 'reg8i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IYL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'reg8i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 325: {'origin': {'name': 'reg16', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'reg16', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 326: {'origin': {'name': 'reg16', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'reg16', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 327: {'origin': {'name': 'reg16', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'HL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'reg16', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 328: {'origin': {'name': 'reg16', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'reg16i', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'reg16', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 329: {'origin': {'name': 'reg16i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IX', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'reg16i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 330: {'origin': {'name': 'reg16i', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IY', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'reg16i', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 331: {'origin': {'name': 'jp_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'P', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'jpflags_other', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 332: {'origin': {'name': 'jp_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'M', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'jpflags_other', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 333: {'origin': {'name': 'jp_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PO', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'jpflags_other', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 334: {'origin': {'name': 'jp_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'jpflags_other', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 335: {'origin': {'name': 'jp_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'jr_flags', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'jpflags_other', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 336: {'origin': {'name': 'jr_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'Z', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'jr_flags', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 337: {'origin': {'name': 'jr_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'C', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'jr_flags', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 338: {'origin': {'name': 'jr_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NZ', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'jr_flags', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 339: {'origin': {'name': 'jr_flags', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'jr_flags', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 340: {'origin': {'name': 'mem_indir', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'mem_indir', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 341: {'origin': {'name': 'expr_bitwise_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bitwise', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 342: {'origin': {'name': 'expr_bitwise_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 343: {'origin': {'name': 'expr_add_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_add', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 344: {'origin': {'name': 'expr_add_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 345: {'origin': {'name': 'expr_mul_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_mul', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 346: {'origin': {'name': 'expr_mul_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 347: {'origin': {'name': 'expr_unary_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_unary', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 348: {'origin': {'name': 'expr_unary_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 349: {'origin': {'name': 'expr_pow_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_pow', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 350: {'origin': {'name': 'expr_pow_operand', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 351: {'origin': {'name': 'expr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bitwise', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 352: {'origin': {'name': 'expr_bitwise', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bitwise_operand', '__type__': 'NonTerminal'}, {'name': 'RSHIFT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_add_operand', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 353: {'origin': {'name': 'expr_bitwise', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bitwise_operand', '__type__': 'NonTerminal'}, {'name': 'LSHIFT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_add_operand', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 354: {'origin': {'name': 'expr_bitwise', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bitwise_operand', '__type__': 'NonTerminal'}, {'name': 'BAND', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_add_operand', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 355: {'origin': {'name': 'expr_bitwise', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bitwise_operand', '__type__': 'NonTerminal'}, {'name': 'BOR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_add_operand', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 356: {'origin': {'name': 'expr_bitwise', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bitwise_operand', '__type__': 'NonTerminal'}, {'name': 'BXOR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_add_operand', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 357: {'origin': {'name': 'expr_bitwise', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_add', '__type__': 'NonTerminal'}], 'order': 5, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 358: {'origin': {'name': 'expr_add', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_add_operand', '__type__': 'NonTerminal'}, {'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_mul_operand', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 359: {'origin': {'name': 'expr_add', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_add_operand', '__type__': 'NonTerminal'}, {'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_mul_operand', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 360: {'origin': {'name': 'expr_add', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_mul', '__type__': 'NonTerminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 361: {'origin': {'name': 'expr_mul', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_mul_operand', '__type__': 'NonTerminal'}, {'name': 'MUL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_pow_operand', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 362: {'origin': {'name': 'expr_mul', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_mul_operand', '__type__': 'NonTerminal'}, {'name': 'DIV', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_pow_operand', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 363: {'origin': {'name': 'expr_mul', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_mul_operand', '__type__': 'NonTerminal'}, {'name': 'MOD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_pow_operand', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 364: {'origin': {'name': 'expr_mul', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_pow', '__type__': 'NonTerminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 365: {'origin': {'name': 'expr_pow', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_unary_operand', '__type__': 'NonTerminal'}, {'name': 'POW', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_pow_operand', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 366: {'origin': {'name': 'expr_pow', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_unary', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 367: {'origin': {'name': 'expr_unary', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_unary', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_uminus', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 368: {'origin': {'name': 'expr_unary', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_unary', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'expr_uplus', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 369: {'origin': {'name': 'expr_unary', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'expr_uminus', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 370: {'origin': {'name': 'expr_unary', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'expr_uplus', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 371: {'origin': {'name': 'expr_unary', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_atom', '__type__': 'NonTerminal'}], 'order': 4, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 372: {'origin': {'name': 'pexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'expr_lprp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 373: {'origin': {'name': 'pexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'pexpr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'expr_lprp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 374: {'origin': {'name': 'expr_atom', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INTEGER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'expr_int', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 375: {'origin': {'name': 'expr_atom', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'expr_label', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 376: {'origin': {'name': 'expr_atom', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LPP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RPP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'expr_paren', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 377: {'origin': {'name': 'expr_atom', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADDR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'expr_addr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}} +) +Shift = 0 +Reduce = 1 +def Lark_StandAlone(**kwargs): + return Lark._load_from_dict(DATA, MEMO, **kwargs) diff --git a/src/zxbasm/basic.py b/src/zxbasm/basic.py index 6bb698854..5fd8bea2e 100755 --- a/src/zxbasm/basic.py +++ b/src/zxbasm/basic.py @@ -1,13 +1,11 @@ #!/usr/bin/python3 -# ------------------------------------------------------------------------------- -# Copyleft (K) 2008 by Jose M. Rodriguez de la Rosa -# -# Simple ASCII to BASIC tokenizer -# -# Implements a simple (really simple) ZX Spectrum BASIC tokenizer -# This will convert a simple ASCII text to a ZX spectrum BASIC bytes program -# ------------------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src import outfmt as tzx from src.api import fp diff --git a/src/zxbasm/expr.py b/src/zxbasm/expr.py index 2086c11db..536dc2b4d 100644 --- a/src/zxbasm/expr.py +++ b/src/zxbasm/expr.py @@ -1,6 +1,13 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api.errmsg import error -from src.ast import Ast -from src.ast.tree import NotAnAstError +from src.ast_ import Ast +from src.ast_.exceptions import NotAnAstError from src.zxbasm.label import Label diff --git a/src/zxbasm/global_.py b/src/zxbasm/global_.py index 80e2c42f4..0fe9fa3ed 100644 --- a/src/zxbasm/global_.py +++ b/src/zxbasm/global_.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api import global_ as gl DOT = gl.NAMESPACE_SEPARATOR # NAMESPACE separator diff --git a/src/zxbasm/label.py b/src/zxbasm/label.py index 9ead507de..e4e09dbba 100644 --- a/src/zxbasm/label.py +++ b/src/zxbasm/label.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.api.errmsg import error from src.zxbasm import global_ as asm_gl diff --git a/src/zxbasm/memory.py b/src/zxbasm/memory.py index 4a1e0732a..603418896 100644 --- a/src/zxbasm/memory.py +++ b/src/zxbasm/memory.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from bisect import bisect_left, bisect_right from collections import defaultdict @@ -153,10 +160,12 @@ def add_instruction(self, instr: Asm): for byte in instr.bytes(): self.__set_byte(byte, instr.lineno) - def dump(self): - """Returns a tuple containing code ORG (origin address), and a list of bytes (OUTPUT)""" + def dump(self) -> tuple[int, list[int]]: + """Returns a tuple containing code ORG (origin address), and a list of bytes (OUTPUT) + self.orgs[ ] contains ADDRESS -> Mnemonic, where Mnemonic.arg[x].eval() is the value of the argument x. + """ org = min(self.memory_bytes.keys()) # Org is the lowest one - OUTPUT = [] + OUTPUT: list[int] = [] align = [] for filename in self._tmp_pending_labels: @@ -202,7 +211,12 @@ def dump(self): return org, OUTPUT def declare_label( - self, label: str, lineno: int, value: int = None, local: bool = False, namespace: str | None = None + self, + label: str, + lineno: int, + value: int | None = None, + local: bool = False, + namespace: str | None = None, ) -> None: """Sets a label with the given value or with the current address (org) if no value is passed. @@ -269,7 +283,7 @@ def set_label(self, label: str, lineno: int, local: bool = False) -> Label: The resulting label is returned. """ - ex_label, namespace = Memory.id_name(label) + ex_label, _ = Memory.id_name(label) if ex_label in self.local_labels[-1].keys(): result = self.local_labels[-1][ex_label] diff --git a/src/zxbasm/version.py b/src/zxbasm/version.py index cca3a688b..3323e8fdd 100644 --- a/src/zxbasm/version.py +++ b/src/zxbasm/version.py @@ -1 +1,8 @@ -VERSION = "1.18.2" +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + +VERSION = "1.18.7" diff --git a/src/zxbasm/z80.py b/src/zxbasm/z80.py index 67704d5a8..3a9aaa37f 100644 --- a/src/zxbasm/z80.py +++ b/src/zxbasm/z80.py @@ -1,4 +1,9 @@ -# vim:ts=4:et: +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from typing import Final, NamedTuple diff --git a/src/zxbasm/zxbasm.py b/src/zxbasm/zxbasm.py index c4162c78f..8ee1451c6 100755 --- a/src/zxbasm/zxbasm.py +++ b/src/zxbasm/zxbasm.py @@ -1,25 +1,33 @@ #!/usr/bin/env python3 -# vim: ts=4:et:sw=4 -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# -# This is the Parser for the ZXBASM (ZXBasic Assembler) -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import argparse import os import sys +from enum import StrEnum import src.api.config from src.api import errmsg, global_ from src.api.config import OPTIONS +from src.api.errmsg import warning_command_line_flag_deprecation from src.zxbasm import asmparse, expr from src.zxbasm.version import VERSION from src.zxbpp import zxbpp +from src.zxbpp.zxbpp import PreprocMode + + +class FileType(StrEnum): + BIN = "bin" + SNA = "sna" + TAP = "tap" + TZX = "tzx" + Z80 = "z80" def main(args=None): @@ -53,7 +61,8 @@ def main(args=None): default=None, ) - o_parser.add_argument( + output_file_type_group = o_parser.add_mutually_exclusive_group() + output_file_type_group.add_argument( "-T", "--tzx", action="store_true", @@ -62,7 +71,7 @@ def main(args=None): help="Sets output format to tzx (default is .bin)", ) - o_parser.add_argument( + output_file_type_group.add_argument( "-t", "--tap", action="store_true", @@ -71,6 +80,16 @@ def main(args=None): help="Sets output format to tzx (default is .bin)", ) + output_file_type_group.add_argument( + "-f", + "--output-format", + type=str, + choices=[str(x) for x in FileType], + required=False, + help="Output format", + default=OPTIONS.output_file_type, + ) + o_parser.add_argument( "-B", "--BASIC", @@ -117,7 +136,6 @@ def main(args=None): if not os.path.exists(options.PROGRAM): o_parser.error("No such file or directory: '%s'" % options.PROGRAM) - sys.exit(2) OPTIONS.debug_level = int(options.debug) OPTIONS.input_filename = options.PROGRAM @@ -130,10 +148,18 @@ def main(args=None): OPTIONS.force_asm_brackets = options.bracket OPTIONS.zxnext = options.zxnext - if options.tzx: - OPTIONS.output_file_type = "tzx" + if options.output_format: + OPTIONS.output_file_type = options.output_format + elif options.tzx: + OPTIONS.output_file_type = FileType.TZX + warning_command_line_flag_deprecation( + f"--tzx (use -f {FileType.TZX} or --output-format={FileType.TZX} instead)" + ) elif options.tap: - OPTIONS.output_file_type = "tap" + OPTIONS.output_file_type = FileType.TAP + warning_command_line_flag_deprecation( + f"--tap (use -f {FileType.TAP} or --output-format={FileType.TAP} instead)" + ) if not OPTIONS.output_filename: OPTIONS.output_filename = ( @@ -143,16 +169,12 @@ def main(args=None): if OPTIONS.stderr_filename: OPTIONS.stderr = open(OPTIONS.stderr_filename, "wt") - if int(options.tzx) + int(options.tap) > 1: - o_parser.error("Options --tap, --tzx and --asm are mutually exclusive") - return 3 - - if OPTIONS.use_basic_loader and not options.tzx and not options.tap: - o_parser.error("Option --BASIC and --autorun requires --tzx or tap format") + if OPTIONS.use_basic_loader and OPTIONS.output_file_type not in (FileType.TZX, FileType.TAP): + o_parser.error("Option --BASIC and --autorun requires tzx or tap format") return 4 # Configure the preprocessor to use the asm-preprocessor-lexer - zxbpp.setMode("asm") + zxbpp.setMode(PreprocMode.ASM) # Now filter them against the preprocessor zxbpp.main([OPTIONS.input_filename]) diff --git a/src/zxbasm/zxnext.py b/src/zxbasm/zxnext.py index a637ed262..145abbb6a 100644 --- a/src/zxbasm/zxnext.py +++ b/src/zxbasm/zxnext.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + __all__ = [ "p_JP_c", "p_add_reg16_a", diff --git a/src/zxbc/__init__.py b/src/zxbc/__init__.py index 375ff7c6c..8d3e3909d 100644 --- a/src/zxbc/__init__.py +++ b/src/zxbc/__init__.py @@ -1,11 +1,9 @@ -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.outfmt import CodeEmitter # noqa diff --git a/src/zxbc/args_config.py b/src/zxbc/args_config.py index eb4116175..5e8f5d19a 100644 --- a/src/zxbc/args_config.py +++ b/src/zxbc/args_config.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from __future__ import annotations import os @@ -177,3 +184,5 @@ def set_option_defines() -> None: if OPTIONS.enable_break: OPTIONS.__DEFINES["__ENABLE_BREAK__"] = "" + + OPTIONS.__DEFINES["__OPT_STRATEGY__"] = OPTIONS.opt_strategy diff --git a/src/zxbc/args_parser.py b/src/zxbc/args_parser.py index fa6d4e52c..1cbd91564 100644 --- a/src/zxbc/args_parser.py +++ b/src/zxbc/args_parser.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from __future__ import annotations import argparse @@ -44,7 +51,7 @@ def parser() -> argparse.ArgumentParser: "-O", "--optimize", type=int, - help=f"Sets optimization level. 0 = None (default level is {OPTIONS.optimization_level}", + help=f"Sets optimization level. 0 = None (default level is {OPTIONS.optimization_level})", ) parser_.add_argument( "-o", @@ -59,13 +66,13 @@ def parser() -> argparse.ArgumentParser: "-T", "--tzx", action="store_true", - help="Sets output format to .tzx (default is .bin).", + help="Sets output format to .tzx (default is .bin). DEPRECATED. Use -f", ) output_file_type_group.add_argument( "-t", "--tap", action="store_true", - help="Sets output format to .tap (default is .bin).", + help="Sets output format to .tap (default is .bin). DEPRECATED. Use -f", ) output_file_type_group.add_argument( "-A", @@ -77,7 +84,7 @@ def parser() -> argparse.ArgumentParser: "-E", "--emit-backend", action="store_true", - help="Emits backend code (IR) instead of ASM or binary.", + help="Emits backend code (IR) instead of ASM or binary. DEPRECATED. Use -f", ) output_file_type_group.add_argument( "--parse-only", action="store_true", help="Only parses to check for syntax and semantic errors" @@ -97,7 +104,7 @@ def parser() -> argparse.ArgumentParser: action="store_true", dest="basic", default=None, - help="Creates a BASIC loader which loads the rest of the CODE. Requires -T ot -t", + help="Creates a BASIC loader which loads the rest of the CODE. Requires one of sna, tzx, tap or z80 output", ) parser_.add_argument( "-a", "--autorun", action="store_true", default=None, help="Sets the program to be run once loaded" @@ -166,13 +173,13 @@ def parser() -> argparse.ArgumentParser: ) parser_.add_argument("--version", action="version", version=f"%(prog)s {VERSION}") parser_.add_argument( - "--append-binary", default=[], action="append", help="Appends binary to tape file (only works with -t or -T)" + "--append-binary", default=[], action="append", help="Appends binary to tape file (only works with tzx and tap)" ) parser_.add_argument( "--append-headless-binary", default=[], action="append", - help="Appends binary to tape file (only works with -t or -T)", + help="Appends binary to tape file (only works with output formats tzx and tap)", ) parser_.add_argument( "-N", "--zxnext", action="store_true", default=None, help="Enables ZX Next asm extended opcodes" diff --git a/src/zxbc/keywords.py b/src/zxbc/keywords.py index e73bd1490..5dec3d46d 100644 --- a/src/zxbc/keywords.py +++ b/src/zxbc/keywords.py @@ -1,14 +1,9 @@ -# vim:ts=4:et:sw=4 - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- - -# Traditional BASIC used even reserved words for math functions like -# SIN, COS, TAN. These are what ZX BASIC uses +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- KEYWORDS = { # Keywords diff --git a/src/zxbc/py.typed b/src/zxbc/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/zxbc/version.py b/src/zxbc/version.py index 2c0175d7b..8ef2f3536 100755 --- a/src/zxbc/version.py +++ b/src/zxbc/version.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from typing import Final -VERSION: Final[str] = "1.18.2" +VERSION: Final[str] = "1.18.7" diff --git a/src/zxbc/zxbc.py b/src/zxbc/zxbc.py index 766197dcb..97b07aec8 100755 --- a/src/zxbc/zxbc.py +++ b/src/zxbc/zxbc.py @@ -1,5 +1,11 @@ #!/usr/bin/env python3 -# vim: ts=4:sw=4:et: + +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import re import sys diff --git a/src/zxbc/zxblex.py b/src/zxbc/zxblex.py index f7e1fb00e..b92f960b4 100755 --- a/src/zxbc/zxblex.py +++ b/src/zxbc/zxblex.py @@ -1,22 +1,19 @@ #!/usr/bin/env python3 -# vim:ts=4:et:sw=4 -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import re import sys from src import api -from src.api import global_ +from src.api import global_, lex from src.api.errmsg import error -from src.ply import lex - -from .keywords import KEYWORDS as reserved +from src.zxbc.keywords import KEYWORDS as reserved ASM = "" # Set to asm block when commenting ASMLINENO = 0 # Line of ASM INLINE beginning diff --git a/src/zxbc/zxbparser.py b/src/zxbc/zxbparser.py index af649f479..a9cd319f7 100755 --- a/src/zxbc/zxbparser.py +++ b/src/zxbc/zxbparser.py @@ -1,19 +1,18 @@ #!/usr/bin/env python3 -# vim: ts=4:et:sw=4: - -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import math import sys +from collections.abc import Callable from math import pi as PI # typings -from typing import NamedTuple +from typing import Any, NamedTuple, cast import src.api.config import src.api.dataref @@ -41,7 +40,7 @@ is_unsigned, ) from src.api.config import OPTIONS -from src.api.constants import CLASS, CONVENTION, SCOPE, LoopType +from src.api.constants import CLASS, CONVENTION, SCOPE, TYPE, LoopType from src.api.debug import __DEBUG__ from src.api.errmsg import error, warning from src.api.global_ import LoopInfo @@ -50,14 +49,15 @@ from src.api.symboltable.symboltable import SymbolTable # Lexers and parsers, etc -from src.ply import yacc from src.symbols import sym from src.symbols.id_ import SymbolID from src.symbols.symbol_ import Symbol -from src.symbols.type_ import Type as TYPE +from src.symbols.type_ import Type from src.zxbc import zxblex from src.zxbc.zxblex import tokens # noqa +from .zxbparser_standalone import Lark_StandAlone, Lexer, Token, Transformer, UnexpectedInput, v_args + # ---------------------------------------------------------------------- # Function level entry ID in which scope we are into. If the list # is empty, we are at global scope @@ -137,6 +137,11 @@ def init(): optemps = opcodestemps.OpcodesTemps() gl.INITS.clear() + gl.syntax_error_occurred = False + gl.tokens_yielded = 0 + gl.tokens_rejected = 0 + gl.shifted_at_last_error = 0 + gl.expr_linenos = {} del gl.FUNCTION_CALLS[:] del gl.FUNCTION_LEVEL[:] del gl.FUNCTIONS[:] @@ -155,13 +160,17 @@ def init(): # ---------------------------------------------------------------------- # "Macro" functions. Just return more complex expressions # ---------------------------------------------------------------------- -def _TYPE(type_): - """returns an internal type converted to a SYMBOL_TABLE - type. - """ +def _TYPE(type_: TYPE) -> sym.TYPE | None: + """returns an internal type converted to a SYMBOL_TABLE type.""" + assert isinstance(type_, TYPE) return SYMBOL_TABLE.basic_types[type_] +def _TYPEREF(type_: TYPE, *, implicit: bool = True) -> sym.TYPEREF: + """Returns a typing annotation""" + return sym.TYPEREF(_TYPE(type_), 0, implicit=implicit) + + # ---------------------------------------------------------------------- # Utils # ---------------------------------------------------------------------- @@ -195,17 +204,33 @@ def make_nop(): def make_number(value, lineno: int, type_=None): - """Wrapper: creates a constant number node.""" + if hasattr(value, "original_value"): + value = value.original_value + elif isinstance(value, str): + try: + if "." in value or "e" in value.lower(): + value = float(value) + else: + value = int(value, 0) + except ValueError: + value = float(value) return sym.NUMBER(value, type_=type_, lineno=lineno) -def make_typecast(type_: sym.TYPE, node: sym.SYMBOL | None, lineno: int) -> sym.TYPECAST | None: +def make_typecast(type_: sym.TYPING, node: sym.EXPR | None, lineno: int) -> sym.TYPECAST | sym.EXPR | None: """Wrapper: returns a Typecast node""" if node is None or node.type_ is None: return None # syntax / semantic error + if isinstance(type_, sym.TYPEREF): + type_ = type_.type_ + assert isinstance(type_, sym.TYPE) - return sym.TYPECAST.make_node(type_, node, lineno) + + result = sym.TYPECAST.make_node(type_, node, lineno) + assert isinstance(result, None | sym.TYPECAST | sym.EXPR), f"{result.__class__.__name__} != TYPECAST | EXPR" + + return result def make_binary(lineno: int, operator, left, right, func=None, type_=None): @@ -213,7 +238,13 @@ def make_binary(lineno: int, operator, left, right, func=None, type_=None): return sym.BINARY.make_node(operator, left, right, lineno, func, type_) -def make_unary(lineno: int, operator, operand, func=None, type_=None): +def make_unary( + lineno: int, + operator: str, + operand: sym.EXPR | None, + func=Callable, + type_: sym.TYPE | None = None, +) -> sym.UNARY | sym.NUMBER | sym.STRING | None: """Wrapper: returns a Unary node""" if operand is None: # syntax / semantic error return None @@ -221,17 +252,26 @@ def make_unary(lineno: int, operator, operand, func=None, type_=None): return sym.UNARY.make_node(lineno, operator, operand, func, type_) -def make_builtin(lineno: int, fname: str, operands: Symbol | tuple | list | None, func=None, type_=None): +def make_builtin( + lineno: int, + fname: str, + operands: Symbol | tuple | list | None, + func: Callable | None = None, + type_: sym.TYPE | None = None, +) -> sym.BUILTIN | sym.NUMBER: """Wrapper: returns a Builtin function node. - Can be a Symbol, tuple or list of Symbols + Can be a Symbol, tuple, or list of Symbols If operand is an iterable, they will be expanded. """ if operands is None: operands = [] + assert isinstance(operands, Symbol | tuple | list) + assert isinstance(type_, sym.TYPE | None) + # TODO: In the future, builtin functions will be implemented in an external stdlib, like POINT or ATTR __DEBUG__(f'Creating BUILTIN "{fname}"', 1) - if not isinstance(operands, (list, tuple)): + if not isinstance(operands, list | tuple): operands = [operands] return sym.BUILTIN.make_node(lineno, fname, func, type_, *operands) @@ -268,12 +308,17 @@ def make_var_declaration(entry): return sym.VARDECL(entry) -def make_array_declaration(entry): +def make_array_declaration(entry: sym.ID) -> sym.ARRAYDECL: """This will return a node with the symbol as an array.""" return sym.ARRAYDECL(entry) -def make_func_declaration(func_name: str, lineno: int, class_: CLASS, type_=None): +def make_func_declaration( + func_name: str, + lineno: int, + class_: CLASS, + type_: sym.TYPEREF | None = None, +) -> sym.FUNCDECL | None: """This will return a node with the symbol as a function or sub.""" return sym.FUNCDECL.make_node(func_name, lineno, class_, type_=type_) @@ -315,7 +360,7 @@ def make_array_access(id_, lineno, arglist): """ for i, arg in enumerate(arglist): value = make_typecast( - TYPE.by_name(src.api.constants.TYPE.to_string(gl.BOUND_TYPE)), + Type.by_name(src.api.constants.TYPE.to_string(gl.BOUND_TYPE)), arg.value, arg.lineno, ) @@ -334,7 +379,7 @@ def make_array_substr_assign(lineno: int, id_: str, arg_list, substr, expr_) -> if entry is None: return None # There were errors - if entry.type_ != TYPE.string: + if entry.type_ != Type.string: error(lineno, "Array '%s' is not of type String" % id_) return None # There were errors @@ -372,7 +417,7 @@ def make_call(id_: str, lineno: int, args: sym.ARGLIST): - a(4) can be a string slice if 'a' is a string variable: a$(4) - a(4) can be an access to an array if 'a' is an array - This function will inspect the id_. If it is undeclared then + This function will inspect the id_. If it is undeclared, then id_ will be taken as a forwarded function. """ if args is None: @@ -384,7 +429,7 @@ def make_call(id_: str, lineno: int, args: sym.ARGLIST): if entry is None: return None - if entry.class_ is CLASS.unknown and entry.type_ == TYPE.string and len(args) == 1 and is_numeric(args[0]): + if entry.class_ is CLASS.unknown and entry.type_ == Type.string and len(args) == 1 and is_numeric(args[0]): entry = entry.to_var() # A scalar variable. e.g a$(expr) if entry.class_ == CLASS.array: # An already declared array @@ -393,7 +438,7 @@ def make_call(id_: str, lineno: int, args: sym.ARGLIST): return None if arr.offset is not None: - offset = make_typecast(TYPE.uinteger, make_number(arr.offset, lineno=lineno), lineno) + offset = make_typecast(Type.uinteger, make_number(arr.offset, lineno=lineno), lineno) arr.append_child(offset) return arr @@ -419,9 +464,16 @@ def make_call(id_: str, lineno: int, args: sym.ARGLIST): return make_func_call(id_, lineno, args) -def make_param_decl(id_: str, lineno: int, typedef, is_array: bool, default_value: sym.SYMBOL | None = None): +def make_param_decl( + id_: str, + lineno: int, + typedef: sym.TYPEREF, + *, + is_array: bool, + default_value: sym.SYMBOL | None = None, +): """Wrapper that creates a param declaration""" - return SYMBOL_TABLE.declare_param(id_, lineno, typedef, is_array, default_value) + return SYMBOL_TABLE.declare_param(id_, lineno, typedef, default_value, is_array=is_array) def make_type(typename, lineno, implicit=False): @@ -436,7 +488,7 @@ def make_type(typename, lineno, implicit=False): if not SYMBOL_TABLE.check_is_declared(typename, lineno, "type"): return None - type_ = sym.TYPEREF(SYMBOL_TABLE.get_entry(typename), lineno, implicit) + type_ = sym.TYPEREF(SYMBOL_TABLE.get_entry(typename), lineno, implicit=implicit) return type_ @@ -469,7 +521,7 @@ def make_break(lineno: int, p): return None last_brk_linenum = lineno - return make_sentence(lineno, "CHKBREAK", make_number(lineno, lineno, TYPE.uinteger)) + return make_sentence(lineno, "CHKBREAK", make_number(lineno, lineno, Type.uinteger)) # ---------------------------------------------------------------------- @@ -498,3096 +550,2686 @@ def make_break(lineno: int, p): ) -# ---------------------------------------------------------------------- -# Grammar rules # ---------------------------------------------------------------------- -def p_start(p): - """start : program""" - global ast, data_ast +def get_lineno(item): + if hasattr(gl, "expr_linenos") and id(item) in gl.expr_linenos: + return gl.expr_linenos[id(item)] + if hasattr(item, "line"): + return item.line + if hasattr(item, "lineno"): + return item.lineno + if hasattr(item, "children") and item.children: + for child in item.children: + l = get_lineno(child) + if l: + return l + return zxblex.lexer.lineno if hasattr(zxblex, "lexer") else 0 - make_label(gl.ZXBASIC_USER_DATA, 0) - make_label(gl.ZXBASIC_USER_DATA_LEN, 0) - if PRINT_IS_USED: - OPTIONS.__DEFINES["___PRINT_IS_USED___"] = 1 +def meta_line(meta): + return getattr(meta, "line", None) or (zxblex.lexer.lineno if hasattr(zxblex, "lexer") else 0) or 0 - if zxblex.IN_STATE: - p.type = "NEWLINE" - p_error(p) - sys.exit(1) - ast = p[0] = p[1] - __end = make_sentence(p.lexer.lineno, "END", make_number(0, lineno=p.lexer.lineno), sentinel=True) +class ZXBasicToken(Token): + def __eq__(self, other): + return str.__eq__(self, other) - if not is_null(ast): - if isinstance(ast, sym.BLOCK) and not is_ender(ast[-1]): - ast.append_child(__end) - else: - ast = __end + def __hash__(self): + return str.__hash__(self) - SYMBOL_TABLE.check_labels() - SYMBOL_TABLE.check_classes() - if gl.has_errors: - return - - __DEBUG__("Checking pending labels", 1) - if not src.api.check.check_pending_labels(ast): - return - - __DEBUG__("Checking pending calls", 1) - if not src.api.check.check_pending_calls(): - return - - data_ast = make_sentence(p.lexer.lineno, "BLOCK") - - # Appends variable declarations at the end. - for var in SYMBOL_TABLE.vars_: - data_ast.append_child(make_var_declaration(var)) +class ZXBasicLarkLexerAdapter(Lexer): + def __init__(self, lexer_conf: Any) -> None: + pass - # Appends arrays declarations at the end. - for var in SYMBOL_TABLE.arrays: - data_ast.append_child(make_array_declaration(var)) - - -def p_program_program_line(p): - """program : program_line""" - p[0] = make_block(p[1], make_break(p.lineno(1), p[1])) + def lex(self, data: Any, parser_state: Any = None) -> Any: + lexer = data + lookahead = [] + def get_next(): + tok = lexer.token() + if tok is None: + return None + tok.lexer = lexer + col = zxblex.find_column(tok) if hasattr(zxblex, "find_column") else 1 + val = tok.value + if isinstance(val, float) and val.is_integer(): + val = str(int(val)) + elif isinstance(val, (int, float)): + val = str(val) + t = ZXBasicToken(tok.type, val, line=tok.lineno, column=col) + t.original_value = tok.value + return t + + while True: + if lookahead: + t = lookahead.pop(0) + else: + t = get_next() + if t is None: + break + + if hasattr(gl, "tokens_yielded"): + gl.tokens_yielded += 1 + + if t.type == "END": + next_t = get_next() + if next_t is not None: + if next_t.type in ("SUB", "FUNCTION", "IF", "WHILE"): + merged_type = "END_" + next_t.type + merged_value = t.value + " " + next_t.value + merged_t = ZXBasicToken(merged_type, merged_value, line=t.line, column=t.column) + merged_t.original_value = merged_value + yield merged_t + continue + else: + lookahead.append(next_t) + + yield t + + +@v_args(meta=True) +class ZXBasicTransformer(Transformer): + def start(self, meta, items): + p0 = None + global ast, data_ast + make_label(gl.ZXBASIC_USER_DATA, 0) + make_label(gl.ZXBASIC_USER_DATA_LEN, 0) + if PRINT_IS_USED: + getattr(OPTIONS, "__DEFINES")["___PRINT_IS_USED___"] = 1 + if zxblex.IN_STATE: + error(meta.line, "Unexpected end of line") + sys.exit(1) + ast = p0 = items[0] + __end = make_sentence(meta_line(meta), "END", make_number(0, lineno=meta_line(meta)), sentinel=True) + if not is_null(ast): + if isinstance(ast, sym.BLOCK) and (not is_ender(ast[-1])): + ast.append_child(__end) + else: + ast = __end + SYMBOL_TABLE.check_labels() + SYMBOL_TABLE.check_classes() + if gl.has_errors: + return p0 + __DEBUG__("Checking pending labels", 1) + if not src.api.check.check_pending_labels(ast): + return p0 + __DEBUG__("Checking pending calls", 1) + if not src.api.check.check_pending_calls(): + return p0 + data_ast = make_sentence(meta_line(meta), "BLOCK") + for var in SYMBOL_TABLE.vars_: + data_ast.append_child(make_var_declaration(var)) + for var in SYMBOL_TABLE.arrays: + data_ast.append_child(make_array_declaration(var)) + return p0 + + def program_program_line(self, meta, items): + p0 = None + p0 = make_block(items[0], make_break(get_lineno(items[0]), items[0])) + return p0 + + def program(self, meta, items): + p0 = None + p0 = make_block(items[0], items[1], make_break(get_lineno(items[1]), items[1])) + return p0 + + def program_line(self, meta, items): + p0 = None + p0 = make_nop() if len(items) + 1 == 2 else items[0] + return p0 + + def co_statements_co(self, meta, items): + p0 = None + p0 = items[0] if len(items) + 1 == 3 else make_nop() + return p0 + + def co_statements(self, meta, items): + p0 = None + p0 = make_block(items[0], items[1]) + return p0 + + def statements_co(self, meta, items): + p0 = None + p0 = items[0] + return p0 + + def statements_statement(self, meta, items): + p0 = None + if len(items) + 1 == 2: + p0 = make_block(items[0]) + else: + p0 = make_block(items[0], items[1]) + return p0 + + def var_decls(self, meta, items): + p0 = None + p0 = items[0] + return p0 + + def label(self, meta, items): + p0 = None + p0 = make_label(items[0], get_lineno(items[0])) + return p0 + + def program_line_label(self, meta, items): + p0 = None + lbl = items[0] + p0 = make_block(lbl, items[1]) if len(items) + 1 == 3 else lbl + return p0 + + def label_line_label_line_co(self, meta, items): + p0 = None + p0 = items[0] + return p0 + + def label_line_co(self, meta, items): + p0 = None + lbl = items[0] + p0 = make_block(lbl, items[1]) if len(items) + 1 == 3 else lbl + return p0 + + def program_line_co(self, meta, items): + p0 = None + p0 = items[0] if len(items) + 1 == 2 else make_block(items[0], items[1]) + return p0 + + def var_decl(self, meta, items): + p0 = None + for vardata in items[1]: + SYMBOL_TABLE.declare_variable(vardata[0], vardata[1], items[2]) + p0 = None + return p0 + + def var_decl_at(self, meta, items): + p0 = None + p0 = None + if items[1] is None or items[2] is None or items[4] is None: + return p0 + if len(items[1]) != 1: + error(get_lineno(items[0]), "Only one variable at a time can be declared this way") + return p0 + idlist = items[1][0] + entry = SYMBOL_TABLE.declare_variable(idlist[0], idlist[1], items[2]) + if entry is None: + return p0 + if items[4].token == "CONSTEXPR": + tmp = items[4].expr + entry.addr = tmp + elif not is_static(items[4]): + errmsg.syntax_error_address_must_be_constant(get_lineno(items[3])) + return p0 + else: + entry.addr = make_typecast(_TYPE(gl.PTR_TYPE), items[4], get_lineno(items[3])) + mark_entry_as_accessed(entry) + if entry.scope == SCOPE.local: + SYMBOL_TABLE.make_static(entry.name) + return p0 + + def var_decl_ini(self, meta, items): + p0 = None + keyword, idlist, typedef, expr = (items[0], items[1], items[2], items[4]) + if len(idlist) != 1: + error(get_lineno(items[0]), "Initialized variables must be declared one by one.") + return p0 + + if expr is None: + return p0 + + if is_static(expr) and isinstance(expr, sym.UNARY): + expr = make_constexpr(get_lineno(items[3]), expr) + + if typedef.implicit: + typedef = sym.TYPEREF(expr.type_, meta_line(meta), implicit=True) + value = make_typecast(typedef.type_, expr, get_lineno(items[3])) + defval = value if is_static(expr) and value.type_ != Type.string else None + + if keyword == "DIM": + SYMBOL_TABLE.declare_variable(idlist[0].name, idlist[0].lineno, typedef, default_value=defval) + else: + if defval is None: + if not is_static_str(value): + errmsg.syntax_error_not_constant(get_lineno(items[3])) + return p0 + defval = value + SYMBOL_TABLE.declare_const(idlist[0].name, idlist[0].lineno, typedef, default_value=defval) -def p_program(p): - """program : program program_line""" - p[0] = make_block(p[1], p[2], make_break(p.lineno(2), p[2])) + if defval is None: + p0 = make_sentence( + get_lineno(items[0]), "LET", SYMBOL_TABLE.access_var(idlist[0].name, get_lineno(items[0])), value + ) + return p0 + + def singleid(self, meta, items): + p0 = Id(name=items[0], lineno=get_lineno(items[0])) + return p0 + + def idlist_id(self, meta, items): + p0 = [items[0]] + return p0 + + def idlist_idlist_id(self, meta, items): + items[0].append(items[2]) + p0 = items[0] + return p0 + + def arr_decl(self, meta, items): + p0 = None + return p0 + + def arr_decl_attr(self, meta, items): + p0 = None + arr_decl, expr = (items[0], items[2]) + if arr_decl is None or expr is None: + p0 = None + return p0 + if expr.token == "CONSTEXPR": + expr = expr.expr + if expr.token == "UNARY" and expr.operator == "ADDRESS": + if expr.operand.token == "ARRAYACCESS": + if expr.operand.offset is None: + error(get_lineno(items[3]), "Address is not constant. Only constant subscripts are allowed") + return p0 + else: + if expr.operand.token not in ("ID", "VAR", "LABEL"): + error(get_lineno(items[2]), "Only addresses of identifiers are allowed") + return p0 + expr.operand.has_address = True + elif not is_static(expr): + errmsg.syntax_error_address_must_be_constant(get_lineno(items[2])) + return p0 + arr_entry = SYMBOL_TABLE.access_array(arr_decl[0], arr_decl[1]) + arr_entry.addr = make_typecast(_TYPE(gl.PTR_TYPE), expr, get_lineno(items[1])) + if arr_entry.scope == SCOPE.local: + SYMBOL_TABLE.make_static(arr_entry.name) + p0 = items[0] + return p0 + + def decl_arr(self, meta, items): + p0 = None + if len(items[1]) != 1: + error(get_lineno(items[0]), "Array declaration only allows one variable name at a time") + else: + id_, lineno = items[1][0] + SYMBOL_TABLE.declare_array(id_, lineno, items[5], items[3]) + p0 = items[1][0] + return p0 + + def arr_decl_initialized(self, meta, items): + + def check_bound(boundlist, remaining): + lineno = get_lineno(items[6]) + if not boundlist: + if not isinstance(remaining, list): + return True + error(lineno, "Unexpected extra vector dimensions. It should be %i" % len(remaining)) + return False + if not isinstance(remaining, list): + error(lineno, "Mismatched vector size. Missing %i extra dimension(s)" % len(boundlist)) + return False + if len(remaining) != boundlist[0].count: + error( + lineno, + "Mismatched vector size. Expected %i elements, got %i." % (boundlist[0].count, len(remaining)), + ) + return False + for row in remaining: + if not check_bound(boundlist[1:], row): + return False + return True + p0 = None -def p_program_line(p): - """program_line : preproc_line NEWLINE - | label_line NEWLINE - | statements NEWLINE - | statements_co NEWLINE - | co_statements NEWLINE - | co_statements_co NEWLINE - | NEWLINE - """ - p[0] = make_nop() if len(p) == 2 else p[1] + if items[3] is None or items[5] is None or items[7] is None: + return p0 + if not check_bound(items[3].children, items[7]): + return p0 -def p_co_statements_co(p): - """co_statements_co : co_statements CO - | co_statements_co CO - | CO - """ - p[0] = p[1] if len(p) == 3 else make_nop() + id_, lineno = items[1][0] + SYMBOL_TABLE.declare_array(id_, lineno, items[5], items[3], default_value=items[7]) + entry = SYMBOL_TABLE.get_entry(id_) + if entry is None: + return p0 + if items[5] == Type.string or entry.type_ == Type.string: + errmsg.syntax_error_cannot_initialize_array_of_type(get_lineno(items[0]), Type.string) + return p0 -def p_co_statements(p): - """co_statements : co_statements_co statement""" - p[0] = make_block(p[1], p[2]) + return p0 + def bound_list(self, meta, items): + p0 = make_bound_list(items[0]) + return p0 -def p_statements_co(p): - """statements_co : statements CO - | statements_co CO - """ - p[0] = p[1] + def bound_list_bound(self, meta, items): + p0 = make_bound_list(items[0], items[2]) + return p0 + def bound(self, meta, items): + p0 = make_bound(make_number(OPTIONS.array_base, lineno=get_lineno(items[0])), items[0], meta_line(meta)) + return p0 -def p_statements_statement(p): - """statements : statement - | statements_co statement - """ - if len(p) == 2: - p[0] = make_block(p[1]) - else: - p[0] = make_block(p[1], p[2]) + def bound_to_bound(self, meta, items): + p0 = make_bound(items[0], items[2], get_lineno(items[1])) + return p0 + def const_vector(self, meta, items): + p0 = items[1] + return p0 -def p_var_decls(p): - """statement : var_decl""" - p[0] = p[1] + def const_vector_elem_list(self, meta, items): + p0 = None + if items[0] is None: + return p0 + if not is_static(items[0]): + if isinstance(items[0], sym.UNARY): + tmp = make_constexpr(get_lineno(items[0]), items[0]) + else: + errmsg.syntax_error_not_constant(meta_line(meta)) + return p0 + else: + tmp = items[0] + + p0 = [tmp] + return p0 + + def const_vector_elem_list_list(self, meta, items): + p0 = None + if items[0] is None or items[2] is None: + return p0 + if not is_static(items[2]): + if isinstance(items[2], sym.UNARY): + tmp = make_constexpr(get_lineno(items[1]), items[2]) + else: + errmsg.syntax_error_not_constant(get_lineno(items[1])) + p0 = None + return p0 + else: + tmp = items[2] + if items[0] is not None: + items[0].append(tmp) + p0 = items[0] + return p0 + + def const_vector_list(self, meta, items): + p0 = [items[0]] + return p0 + + def const_vector_vector_list(self, meta, items): + if len(items[2]) != len(items[0][0]): + error(get_lineno(items[1]), "All rows must have the same number of elements") + p0 = None + return p0 + p0 = items[0] + [items[2]] + return p0 + + def staement_func_decl(self, meta, items): + p0 = items[0] + return p0 + + def statement_border(self, meta, items): + p0 = make_sentence(get_lineno(items[0]), "BORDER", make_typecast(Type.ubyte, items[1], get_lineno(items[0]))) + return p0 + + def statement_plot(self, meta, items): + p0 = make_sentence( + get_lineno(items[0]), + "PLOT", + make_typecast(Type.ubyte, items[1], get_lineno(items[2])), + make_typecast(Type.ubyte, items[3], get_lineno(items[2])), + ) + return p0 + + def statement_plot_attr(self, meta, items): + p0 = make_sentence( + get_lineno(items[0]), + "PLOT", + make_typecast(Type.ubyte, items[2], get_lineno(items[3])), + make_typecast(Type.ubyte, items[4], get_lineno(items[3])), + items[1], + ) + return p0 + + def statement_draw3(self, meta, items): + p0 = make_sentence( + get_lineno(items[0]), + "DRAW3", + make_typecast(Type.integer, items[1], get_lineno(items[2])), + make_typecast(Type.integer, items[3], get_lineno(items[4])), + make_typecast(Type.float_, items[5], get_lineno(items[4])), + ) + return p0 + + def statement_draw3_attr(self, meta, items): + p0 = make_sentence( + get_lineno(items[0]), + "DRAW3", + make_typecast(Type.integer, items[2], get_lineno(items[3])), + make_typecast(Type.integer, items[4], get_lineno(items[5])), + make_typecast(Type.float_, items[6], get_lineno(items[5])), + items[1], + ) + return p0 + + def statement_draw(self, meta, items): + p0 = make_sentence( + get_lineno(items[0]), + "DRAW", + make_typecast(Type.integer, items[1], get_lineno(items[2])), + make_typecast(Type.integer, items[3], get_lineno(items[2])), + ) + return p0 + + def statement_draw_attr(self, meta, items): + p0 = make_sentence( + get_lineno(items[0]), + "DRAW", + make_typecast(Type.integer, items[2], get_lineno(items[3])), + make_typecast(Type.integer, items[4], get_lineno(items[3])), + items[1], + ) + return p0 + + def statement_circle(self, meta, items): + p0 = make_sentence( + get_lineno(items[0]), + "CIRCLE", + make_typecast(Type.byte_, items[1], get_lineno(items[2])), + make_typecast(Type.byte_, items[3], get_lineno(items[4])), + make_typecast(Type.byte_, items[5], get_lineno(items[4])), + ) + return p0 + + def statement_circle_attr(self, meta, items): + p0 = make_sentence( + get_lineno(items[0]), + "CIRCLE", + make_typecast(Type.byte_, items[2], get_lineno(items[3])), + make_typecast(Type.byte_, items[4], get_lineno(items[5])), + make_typecast(Type.byte_, items[6], get_lineno(items[5])), + items[1], + ) + return p0 -def p_label(p): - """label : LABEL""" - p[0] = make_label(p[1], p.lineno(1)) + def statement_cls(self, meta, items): + p0 = make_sentence(get_lineno(items[0]), "CLS") + return p0 + def statement_asm(self, meta, items): + p0 = make_asm_sentence(items[0], get_lineno(items[0])) + return p0 -def p_program_line_label(p): - """label_line : label statements - | label co_statements - """ - lbl = p[1] - p[0] = make_block(lbl, p[2]) if len(p) == 3 else lbl + def statement_randomize(self, meta, items): + p0 = make_sentence( + get_lineno(items[0]), "RANDOMIZE", make_number(0, lineno=get_lineno(items[0]), type_=Type.ulong) + ) + return p0 + + def statement_randomize_expr(self, meta, items): + p0 = make_sentence(get_lineno(items[0]), "RANDOMIZE", make_typecast(Type.ulong, items[1], get_lineno(items[0]))) + return p0 + + def statement_beep(self, meta, items): + p0 = make_sentence( + get_lineno(items[0]), + "BEEP", + make_typecast(Type.float_, items[1], get_lineno(items[0])), + make_typecast(Type.float_, items[3], get_lineno(items[2])), + ) + return p0 + + def statement_call(self, meta, items): + if len(items) + 1 > 2 and items[1] is None: + p0 = None + elif len(items) + 1 == 2: + entry = SYMBOL_TABLE.get_entry(items[0]) + if entry is not None and entry.class_ in (CLASS.label, CLASS.unknown): + p0 = make_label(items[0], get_lineno(items[0])) + else: + p0 = make_sub_call(items[0], get_lineno(items[0]), make_arg_list(None)) + else: + p0 = make_sub_call(items[0], get_lineno(items[0]), items[1]) + return p0 -def p_label_line_label_line_co(p): - """label_line : label_line_co""" - p[0] = p[1] + def assignment(self, meta, items): + global LET_ASSIGNMENT + LET_ASSIGNMENT = False + p0 = None + q = items[0:] + i = 1 + if q[1] is None: + return p0 + if q[1].token == "VAR" and q[1].class_ == CLASS.unknown: + q[1] = SYMBOL_TABLE.access_var(q[1].name, get_lineno(items[i - 1])) -def p_label_line_co(p): - """label_line_co : label statements_co %prec CO - | label co_statements_co %prec CO - | label %prec CO - """ - lbl = p[1] - p[0] = make_block(lbl, p[2]) if len(p) == 3 else lbl + q1class_ = q[1].class_ if q[1].token == "VAR" else CLASS.unknown + variable = SYMBOL_TABLE.access_id( + q[0], get_lineno(items[i - 1]), default_type=q[1].type_, default_class=q1class_ + ) + if variable is None: + return p0 -def p_program_line_co(p): - """program_co : program %prec CO - | program label_line_co - | program co_statements_co %prec CO - | program statements_co %prec CO - """ - p[0] = p[1] if len(p) == 2 else make_block(p[1], p[2]) + if variable.class_ == CLASS.unknown: + variable = variable.to_var() + if variable.class_ not in (CLASS.var, CLASS.array): + errmsg.syntax_error_cannot_assign_not_a_var(get_lineno(items[i - 1]), variable.name) + return p0 -def p_var_decl(p): - """var_decl : DIM idlist typedef""" - for vardata in p[2]: - SYMBOL_TABLE.declare_variable(vardata[0], vardata[1], p[3]) + if variable.class_ == CLASS.var and q1class_ == CLASS.array: + error(get_lineno(items[i - 1]), "Cannot assign an array to an scalar variable") + return p0 - p[0] = None # Variable declarations are made at the end of parsing + expr = make_typecast(variable.type_, q[1], get_lineno(items[i - 1])) + p0 = make_sentence(get_lineno(items[0]), "LET", variable, expr) + return p0 + def lexpr(self, meta, items): + global LET_ASSIGNMENT + LET_ASSIGNMENT = True + if items[0] == "LET": + p0 = items[1] + i = 2 + else: + p0 = items[0] + i = 1 -def p_var_decl_at(p): - """var_decl : DIM idlist typedef AT expr""" - p[0] = None + SYMBOL_TABLE.access_id(items[i - 1], get_lineno(items[i - 1])) + return p0 - if p[2] is None or p[3] is None or p[5] is None: - return + def array_copy(self, meta, items): + p0 = None + if items[0] == "LET": + array_id1, array_id2 = (items[1], items[3]) + l1, l2 = (get_lineno(items[1]), get_lineno(items[3])) + else: + array_id1, array_id2 = (items[0], items[2]) + l1, l2 = (get_lineno(items[0]), get_lineno(items[2])) - if len(p[2]) != 1: - error(p.lineno(1), "Only one variable at a time can be declared this way") - return + larray = SYMBOL_TABLE.access_id(array_id1, l1) + rarray = SYMBOL_TABLE.access_id(array_id2, l2) - idlist = p[2][0] + if larray is None or rarray is None: + p0 = None + return p0 - entry = SYMBOL_TABLE.declare_variable(idlist[0], idlist[1], p[3]) - if entry is None: - return + if larray.type_ != rarray.type_: + error(l1, "Arrays must have the same element type") + return p0 - if p[5].token == "CONSTEXPR": - tmp = p[5].expr - entry.addr = tmp - elif not is_static(p[5]): - errmsg.syntax_error_address_must_be_constant(p.lineno(4)) - return - else: - entry.addr = make_typecast(_TYPE(gl.PTR_TYPE), p[5], p.lineno(4)) - mark_entry_as_accessed(entry) - if entry.scope == SCOPE.local: - SYMBOL_TABLE.make_static(entry.name) + if larray.ref.memsize != rarray.ref.memsize: + error(l1, "Arrays '%s' and '%s' must have the same size" % (array_id1, array_id2)) + return p0 + if larray.ref.count != rarray.ref.count: + warning(l1, "Arrays '%s' and '%s' don't have the same number of dimensions" % (larray.name, rarray.name)) + else: + for b1, b2 in zip(larray.ref.bounds, rarray.ref.bounds): + if b1.count != b2.count: + warning(l1, "Arrays '%s' and '%s' don't have the same dimensions" % (array_id1, array_id2)) + break + + mark_entry_as_accessed(larray) + mark_entry_as_accessed(rarray) + p0 = make_sentence(get_lineno(items[0]), "ARRAYCOPY", larray, rarray) + return p0 + + def arr_assignment(self, meta, items): + i = 2 if items[0].upper() == "LET" else 1 + id_ = items[i - 1] + arg_list = items[i + 1 - 1] + expr = items[i + 3 - 1] + p0 = None + if arg_list is None or expr is None: + return p0 + + entry = SYMBOL_TABLE.access_call(id_, get_lineno(items[i - 1])) + if entry is None: + return p0 -def p_var_decl_ini(p): - """var_decl : DIM idlist typedef EQ expr - | CONST idlist typedef EQ expr - """ - p[0] = None + if entry.type_ == Type.string: + variable = gl.SYMBOL_TABLE.access_array(id_, get_lineno(items[i - 1])) + if len(variable.ref.bounds) and len(variable.ref.bounds) + 1 == len(arg_list): + ss = arg_list.children.pop().value + p0 = make_array_substr_assign(get_lineno(items[i - 1]), id_, arg_list, (ss, ss), expr) + return p0 - keyword, idlist, typedef, expr = p[1], p[2], p[3], p[5] + arr = make_array_access(id_, get_lineno(items[i - 1]), arg_list) + if arr is None: + return p0 - if len(idlist) != 1: - error(p.lineno(1), "Initialized variables must be declared one by one.") - return + expr = make_typecast(arr.type_, expr, get_lineno(items[i - 1])) + if entry is None: + return p0 - if expr is None: - return + if entry.addr is not None: + mark_entry_as_accessed(entry) + p0 = make_sentence(get_lineno(items[0]), "LETARRAY", arr, expr) + return p0 - if not is_static(expr): - if isinstance(expr, sym.UNARY): - expr = make_constexpr(p.lineno(4), expr) # Delayed constant evaluation + def substr_assignment_no_let(self, meta, items): + p0 = None + entry = SYMBOL_TABLE.access_call(items[0], get_lineno(items[0])) + if entry is None: + return p0 - if typedef.implicit: - typedef = sym.TYPEREF(expr.type_, p.lexer.lineno, implicit=True) + if entry.class_ == CLASS.unknown: + errmsg.warning_uninitalized_string_var(get_lineno(items[0]), entry.name) + entry.to_var() - value = make_typecast(typedef, expr, p.lineno(4)) - defval = value if is_static(expr) and value.type_ != TYPE.string else None + if items[5].type_ != Type.string: + errmsg.syntax_error_expected_string(get_lineno(items[4]), items[5].type_) - if keyword == "DIM": - SYMBOL_TABLE.declare_variable(idlist[0].name, idlist[0].lineno, typedef, default_value=defval) - else: - # keyword == "CONST" - if defval is None: - if not is_static_str(value): - errmsg.syntax_error_not_constant(p.lineno(4)) - return - defval = value - - SYMBOL_TABLE.declare_const(idlist[0].name, idlist[0].lineno, typedef, default_value=defval) - - if defval is None: # Okay do a delayed initialization - p[0] = make_sentence( - p.lineno(1), - "LET", - SYMBOL_TABLE.access_var(idlist[0].name, p.lineno(1)), - value, + lineno = get_lineno(items[1]) + base = make_number(OPTIONS.string_base, lineno, _TYPE(gl.STR_INDEX_TYPE)) + substr = make_typecast(_TYPE(gl.STR_INDEX_TYPE), items[2], lineno) + p0 = make_sentence( + get_lineno(items[0]), + "LETSUBSTR", + entry, + make_binary(lineno, "MINUS", substr, base, func=lambda x, y: x - y), + make_binary(lineno, "MINUS", substr, base, func=lambda x, y: x - y), + items[5], ) + return p0 + def substr_assignment(self, meta, items): + p0 = None + if items[2] is None or items[4] is None: + return p0 -def p_singleid(p): - """singleid : ID - | ARRAY_ID - """ - p[0] = Id(name=p[1], lineno=p.lineno(1)) + entry = SYMBOL_TABLE.access_call(items[1], get_lineno(items[1])) + if entry is None: + return p0 + if entry.class_ == CLASS.unknown: + entry = entry.to_var() -def p_idlist_id(p): - """idlist : singleid""" - p[0] = [p[1]] + if entry.class_ != CLASS.var: + errmsg.syntax_error_cannot_assign_not_a_var(get_lineno(items[1]), items[1]) + return p0 + if entry.type_ != Type.string: + errmsg.syntax_error_expected_string(get_lineno(items[1]), entry.type_) + return p0 -def p_idlist_idlist_id(p): - """idlist : idlist COMMA singleid""" - p[1].append(p[3]) - p[0] = p[1] + if items[4].type_ != Type.string: + errmsg.syntax_error_expected_string(get_lineno(items[3]), items[4].type_) + return p0 + if len(items[2]) > 1: + error(get_lineno(items[1]), "Accessing string with too many indexes. Expected only one.") + return p0 -def p_arr_decl(p): - """var_decl : var_arr_decl - | var_arr_decl_addr - """ - p[0] = None + if len(items[2]) == 1: + substr = ( + make_typecast(_TYPE(gl.STR_INDEX_TYPE), items[2][0].value, get_lineno(items[1])), + make_typecast(_TYPE(gl.STR_INDEX_TYPE), items[2][0].value, get_lineno(items[1])), + ) + else: + substr = ( + make_typecast( + _TYPE(gl.STR_INDEX_TYPE), + make_number(gl.MIN_STRSLICE_IDX, lineno=get_lineno(items[1])), + get_lineno(items[1]), + ), + make_typecast( + _TYPE(gl.STR_INDEX_TYPE), + make_number(gl.MAX_STRSLICE_IDX, lineno=get_lineno(items[1])), + get_lineno(items[1]), + ), + ) + lineno = get_lineno(items[1]) + base = make_number(OPTIONS.string_base, lineno, _TYPE(gl.STR_INDEX_TYPE)) + p0 = make_sentence( + get_lineno(items[0]), + "LETSUBSTR", + entry, + make_binary(lineno, "MINUS", substr[0], base, func=lambda x, y: x - y), + make_binary(lineno, "MINUS", substr[1], base, func=lambda x, y: x - y), + items[4], + ) + return p0 + + def str_assign(self, meta, items): + p0 = None + if items[0].upper() != "LET": + q = items[0] + r = items[3] + s = items[1] + lineno = get_lineno(items[2]) + else: + q = items[1] + r = items[4] + s = items[2] + lineno = get_lineno(items[3]) + if q is None or s is None: + return p0 -def p_arr_decl_attr(p): - """var_arr_decl_addr : var_arr_decl AT expr""" - arr_decl, expr = p[1], p[3] - if arr_decl is None or expr is None: - p[0] = None - return + if r.type_ != Type.string: + errmsg.syntax_error_expected_string(lineno, r.type_) - if expr.token == "CONSTEXPR": - expr = expr.expr - if expr.token == "UNARY" and expr.operator == "ADDRESS": # Must be an ID - if expr.operand.token == "ARRAYACCESS": - if expr.operand.offset is None: - error( - p.lineno(4), - "Address is not constant. Only constant subscripts are allowed", - ) - return + entry = SYMBOL_TABLE.access_var(q, lineno, default_type=Type.string) + if entry is None: + return p0 + + p0 = make_sentence(get_lineno(items[0]), "LETSUBSTR", entry, s[0], s[1], r) + return p0 + + def goto(self, meta, items): + p0 = None + entry = check_and_make_label(items[1], get_lineno(items[1])) + if entry is not None: + p0 = make_sentence(get_lineno(items[0]), items[0].upper(), entry) + + return p0 + + def go(self, meta, items): + p0 = None + p0 = items[0] + if p0 == "GO": + p0 += items[1] + if p0 == "GOSUB" and FUNCTION_LEVEL: + error(get_lineno(items[0]), "GOSUB not allowed within SUB or FUNCTION") + return p0 + + def if_sentence(self, meta, items): + p0 = None + cond_ = items[0] + if len(items) + 1 == 6: + lbl = items[2] + stat_ = make_block(lbl, items[3]) + endif_ = items[4] + elif len(items) + 1 == 5: + stat_ = items[2] + endif_ = items[3] + else: + stat_ = make_nop() + endif_ = items[2] + p0 = make_sentence(get_lineno(items[1]), "IF", cond_, make_block(stat_, endif_)) + return p0 + + def endif(self, meta, items): + p0 = None + p0 = ( + make_nop() + if getattr(items[0], "type", None) in ("END", "ENDIF", "END_IF") or items[0] in ("END", "ENDIF", "END_IF") + else items[0] + ) + return p0 + + def statement_if(self, meta, items): + p0 = None + p0 = items[0] + return p0 + + def statement_if_then_endif(self, meta, items): + p0 = None + cond_ = items[0] + stat_ = items[1] + endif_ = items[2] + p0 = make_sentence(get_lineno(items[0]), "IF", cond_, make_block(stat_, endif_)) + return p0 + + def single_line_if(self, meta, items): + p0 = None + cond_ = items[0] + stat_ = items[1] + p0 = make_sentence(get_lineno(items[0]), "IF", cond_, stat_) + return p0 + + def if_elseif(self, meta, items): + p0 = None + cond_ = items[0] + stats_ = items[2] if len(items) + 1 == 5 else make_nop() + eliflist = items[3] if len(items) + 1 == 5 else items[2] + p0 = make_sentence(get_lineno(items[1]), "IF", cond_, stats_, eliflist) + return p0 + + def elseif_part(self, meta, items): + p0 = None + if items[0] == "ELSEIF": + label_ = make_nop() + cond_ = items[1] + else: + label_ = items[0] + cond_ = items[2] + p0 = (label_, cond_) + return p0 + + def elseif_list(self, meta, items): + p0 = None + label_, cond_ = items[0] + then_ = items[1] + else_ = items[2] + if isinstance(else_, list): + else_ = make_block(*else_) + else: + then_ = make_block(then_, else_) + else_ = None + p0 = make_block(label_, make_sentence(get_lineno(items[0]), "IF", cond_, then_, else_)) + return p0 + + def elseif_elseiflist(self, meta, items): + p0 = None + label_, cond_ = items[0] + then_ = items[1] + else_ = items[2] + p0 = make_block(label_, make_sentence(get_lineno(items[0]), "IF", cond_, then_, else_)) + return p0 + + def else_part_endif(self, meta, items): + p0 = None + if items[1] == "\n": + if len(items) + 1 == 4: + p0 = [make_nop(), items[2]] + elif len(items) + 1 == 6: + p0 = [items[2], items[3], items[4]] else: - if expr.operand.token not in ("ID", "VAR", "LABEL"): - error(p.lineno(3), "Only addresses of identifiers are allowed") - return - expr.operand.has_address = True - - elif not is_static(expr): - errmsg.syntax_error_address_must_be_constant(p.lineno(3)) - return - - arr_entry = SYMBOL_TABLE.access_array(arr_decl[0], arr_decl[1]) - arr_entry.addr = make_typecast(_TYPE(gl.PTR_TYPE), expr, p.lineno(2)) - if arr_entry.scope == SCOPE.local: - SYMBOL_TABLE.make_static(arr_entry.name) - - p[0] = p[1] - - -def p_decl_arr(p): - """var_arr_decl : DIM idlist LP bound_list RP typedef""" - if len(p[2]) != 1: - error(p.lineno(1), "Array declaration only allows one variable name at a time") - else: - id_, lineno = p[2][0] - SYMBOL_TABLE.declare_array(id_, lineno, p[6], p[4]) - - p[0] = p[2][0] - - -def p_arr_decl_initialized(p): - """var_decl : DIM idlist LP bound_list RP typedef RIGHTARROW const_vector - | DIM idlist LP bound_list RP typedef EQ const_vector - """ + p0 = [items[2], items[3]] + else: + p0 = [items[1], items[2]] + return p0 + + def else_part(self, meta, items): + p0 = None + p0 = [items[1], make_nop()] + return p0 + + def else_part_is_inline(self, meta, items): + p0 = None + p0 = items[0] + return p0 + + def else_part_label(self, meta, items): + p0 = None + lbl = items[0] + p0 = [make_block(lbl, items[2]), items[3]] + return p0 + + def if_then_part(self, meta, items): + p0 = None + expr = items[1] + if expr is None: + p0 = None + return p0 + if is_number(expr): + errmsg.warning_condition_is_always(get_lineno(items[0]), bool(expr.value)) + p0 = expr + return p0 + + def if_inline(self, meta, items): + p0 = None + items[0].append_child(make_block(items[1][0], items[1][1])) + p0 = items[0] + return p0 + + def if_else(self, meta, items): + p0 = None + cond_ = items[0] + then_ = items[2] + else_ = items[3][0] + endif = items[3][1] + p0 = make_sentence(get_lineno(items[1]), "IF", cond_, then_, make_block(else_, endif)) + return p0 + + def then(self, meta, items): + p0 = None + return p0 + + def for_sentence(self, meta, items): + p0 = None + p0 = items[0] + if is_null(p0): + return p0 + items[0].append_child(make_block(items[1], items[2])) + gl.LOOPS.pop() + return p0 + + def next(self, meta, items): + p0 = None + p0 = make_nop() if items[0] == "NEXT" else items[0] + return p0 + + def next1(self, meta, items): + p0 = None + if items[0] == "NEXT": + p1 = make_nop() + p3 = items[1] + else: + p1 = items[0] + p3 = items[2] + if p3 != gl.LOOPS[-1].var: + errmsg.syntax_error_wrong_for_var(get_lineno(items[1]), gl.LOOPS[-1].var, p3) + p0 = make_nop() + return p0 + p0 = p1 + return p0 + + def for_sentence_start(self, meta, items): + p0 = None + gl.LOOPS.append(LoopInfo(type=LoopType.FOR, lineno=get_lineno(items[0]), var=items[1])) + p0 = None + if items[3] is None or items[5] is None or items[6] is None: + return p0 + if is_number(items[3], items[5], items[6]): + if items[3].value != items[5].value and items[6].value == 0: + warning(get_lineno(items[4]), "STEP value is 0 and FOR might loop forever") + if items[3].value > items[5].value and items[6].value > 0: + warning(get_lineno(items[4]), "FOR start value is greater than end. This FOR loop is useless") + if items[3].value < items[5].value and items[6].value < 0: + warning(get_lineno(items[1]), "FOR start value is lower than end. This FOR loop is useless") + id_type = common_type(common_type(items[3].type_, items[5].type_), items[6].type_) + variable = SYMBOL_TABLE.access_var(items[1], get_lineno(items[1]), default_type=id_type) + if variable is None: + return p0 + mark_entry_as_accessed(variable) + expr1 = make_typecast(variable.type_, items[3], get_lineno(items[2])) + expr2 = make_typecast(variable.type_, items[5], get_lineno(items[4])) + expr3 = make_typecast(variable.type_, items[6], meta_line(meta)) + p0 = make_sentence(get_lineno(items[0]), "FOR", variable, expr1, expr2, expr3) + return p0 + + def step(self, meta, items): + p0 = None + p0 = make_number(1, lineno=meta_line(meta)) + return p0 + + def step_expr(self, meta, items): + p0 = None + p0 = items[1] + return p0 + + def end(self, meta, items): + p0 = None + q = make_number(0, lineno=get_lineno(items[0])) if len(items) + 1 == 2 else items[1] + p0 = make_sentence(get_lineno(items[0]), "END", q) + return p0 + + def error_raise(self, meta, items): + p0 = None + q = make_number(1, lineno=get_lineno(items[1])) + r = make_binary( + get_lineno(items[0]), + "MINUS", + make_typecast(Type.ubyte, items[1], get_lineno(items[0])), + q, + lambda x, y: x - y, + ) + p0 = make_sentence(get_lineno(items[0]), "ERROR", r) + return p0 + + def stop_raise(self, meta, items): + p0 = None + q = make_number(9, lineno=get_lineno(items[0])) if len(items) + 1 == 2 else items[1] + z = make_number(1, lineno=get_lineno(items[0])) + r = make_binary( + get_lineno(items[0]), "MINUS", make_typecast(Type.ubyte, q, get_lineno(items[0])), z, lambda x, y: x - y + ) + p0 = make_sentence(get_lineno(items[0]), "STOP", r) + return p0 - def check_bound(boundlist, remaining): - """Checks if constant vector bounds matches the array one""" - lineno = p.lineno(8) - if not boundlist: # Returns on empty list - if not isinstance(remaining, list): - return True # It's OK :-) + def loop(self, meta, items): + p0 = None + if items[0] == "LOOP": + p0 = None + else: + p0 = items[0] + return p0 - error( - lineno, - "Unexpected extra vector dimensions. It should be %i" % len(remaining), + def do_loop(self, meta, items): + p0 = None + if len(items) + 1 == 4: + q = make_block(items[1], items[2]) + else: + q = items[1] + if items[0] == "DO": + gl.LOOPS.append(LoopInfo(LoopType.DO, get_lineno(items[0]))) + if q is None: + warning(get_lineno(items[0]), "Infinite empty loop") + p0 = make_sentence(get_lineno(items[0]), "DO_LOOP", q) + gl.LOOPS.pop() + return p0 + + def do_loop_until(self, meta, items): + p0 = None + p0 = None + if len(items) + 1 == 6: + q = make_block(items[1], items[2]) + r = items[4] + else: + q = items[1] + r = items[3] + if items[0] == "DO": + gl.LOOPS.append(LoopInfo(LoopType.DO, get_lineno(items[0]))) + p0 = make_sentence(get_lineno(items[0]), "DO_UNTIL", r, q) + gl.LOOPS.pop() + if is_number(r): + errmsg.warning_condition_is_always(get_lineno(items[-2]), bool(r.value)) + if q is None: + errmsg.warning_empty_loop(get_lineno(items[-2])) + return p0 + + def data(self, meta, items): + p0 = None + label_ = make_label(gl.DATA_PTR_CURRENT, lineno=get_lineno(items[0])) + datas_ = [] + funcs = [] + if items[1] is None: + p0 = None + return p0 + if gl.FUNCTION_LEVEL: + errmsg.error(get_lineno(items[0]), "DATA not allowed within Functions nor Subs") + p0 = None + return p0 + for d in items[1].children: + value = d.value + if is_static(value): + datas_.append(d) + continue + new_lbl = f"__DATA__FUNCPTR__{len(gl.DATA_FUNCTIONS)}" + type_ = value.type_ + assert isinstance(type_, sym.TYPING) + if isinstance(type_, sym.TYPE): + type_ = sym.TYPEREF(value.type_, 0) + entry = make_func_declaration(new_lbl, get_lineno(items[0]), type_=type_, class_=CLASS.function) + if not entry: + continue + func = entry.entry + func.ref.convention = CONVENTION.fastcall + SYMBOL_TABLE.enter_scope(new_lbl) + func.ref.local_symbol_table = SYMBOL_TABLE.current_scope + func.ref.locals_size = SYMBOL_TABLE.leave_scope() + gl.DATA_FUNCTIONS.append(func) + sent = make_sentence(get_lineno(items[0]), "RETURN", func, value) + func.ref.body = make_block(sent) + datas_.append(entry) + funcs.append(entry) + gl.DATAS.append(src.api.dataref.DataRef(label_, datas_)) + id_ = src.api.utils.current_data_label() + gl.DATA_PTR_CURRENT = id_ + return p0 + + def restore(self, meta, items): + p0 = None + if len(items) + 1 == 2: + lbl = None + else: + lbl = check_and_make_label(items[1], get_lineno(items[0])) + p0 = make_sentence(get_lineno(items[0]), "RESTORE", lbl) + return p0 + + def read(self, meta, items): + p0 = None + gl.DATA_IS_USED = True + reads = [] + if items[1] is None: + return p0 + for arg in items[1]: + entry = arg.value + if entry is None: + p0 = None + return p0 + if entry.token == "VARARRAY": + errmsg.error(get_lineno(items[0]), "Cannot read '%s'. It's an array" % entry.name) + p0 = None + return p0 + if isinstance(entry, sym.ID): + if entry.class_ != CLASS.var: + errmsg.syntax_error_cannot_assign_not_a_var(get_lineno(items[1]), entry.name) + p0 = None + return p0 + mark_entry_as_accessed(entry) + if entry.type_ == Type.auto: + entry.type_ = _TYPE(gl.DEFAULT_TYPE) + errmsg.warning_implicit_type(get_lineno(items[1]), items[1], entry.type_.name) + reads.append(make_sentence(get_lineno(items[0]), "READ", entry)) + continue + if isinstance(entry, sym.ARRAYLOAD): + reads.append( + make_sentence( + get_lineno(items[0]), + "READ", + sym.ARRAYACCESS(entry.entry, entry.args, entry.lineno, gl.FILENAME), + ) + ) + continue + errmsg.error(get_lineno(items[0]), "Syntax error. Can only read a variable or an array element") + p0 = None + return p0 + p0 = make_block(*reads) + return p0 + + def do_loop_while(self, meta, items): + p0 = None + p0 = None + if len(items) + 1 == 6: + q = make_block(items[1], items[2]) + r = items[4] + else: + q = items[1] + r = items[3] + if items[0] == "DO": + gl.LOOPS.append(LoopInfo(LoopType.DO, get_lineno(items[0]))) + p0 = make_sentence(get_lineno(items[0]), "DO_WHILE", r, q) + gl.LOOPS.pop() + if is_number(r): + errmsg.warning_condition_is_always(get_lineno(items[-2]), bool(r.value)) + if q is None: + errmsg.warning_empty_loop(get_lineno(items[-2])) + return p0 + + def do_while_loop(self, meta, items): + p0 = None + r = items[0] + q = items[1] + if q == "LOOP": + q = None + p0 = make_sentence(get_lineno(items[0]), "WHILE_DO", r, q) + gl.LOOPS.pop() + if is_number(r): + errmsg.warning_condition_is_always(get_lineno(items[1]), bool(r.value)) + return p0 + + def do_until_loop(self, meta, items): + p0 = None + r = items[0] + q = items[1] + if q == "LOOP": + q = None + p0 = make_sentence(get_lineno(items[1]), "UNTIL_DO", r, q) + gl.LOOPS.pop() + if is_number(r): + errmsg.warning_condition_is_always(get_lineno(items[1]), bool(r.value)) + return p0 + + def do_while_start(self, meta, items): + p0 = None + p0 = items[2] + gl.LOOPS.append(LoopInfo(LoopType.DO, get_lineno(items[0]))) + return p0 + + def do_until_start(self, meta, items): + p0 = None + p0 = items[2] + gl.LOOPS.append(LoopInfo(LoopType.DO, get_lineno(items[0]))) + return p0 + + def do_start(self, meta, items): + p0 = None + gl.LOOPS.append(LoopInfo(LoopType.DO, get_lineno(items[0]))) + return p0 + + def label_end_while(self, meta, items): + p0 = None + if getattr(items[0], "type", None) in ("WEND", "END_WHILE") or items[0] in ("WEND", "END_WHILE"): + p0 = None + else: + p0 = items[0] + return p0 + + def while_sentence(self, meta, items): + p0 = None + gl.LOOPS.pop() + q = make_block(items[1], items[2]) + if is_number(items[0]): + errmsg.warning_condition_is_always(get_lineno(items[0]), bool(items[0].value)) + p0 = make_sentence(get_lineno(items[0]), "WHILE", items[0], q) + return p0 + + def while_start(self, meta, items): + p0 = None + p0 = items[1] + gl.LOOPS.append(LoopInfo(LoopType.WHILE, get_lineno(items[0]))) + if is_number(items[1]) and (not items[1].value): + errmsg.warning_condition_is_always(get_lineno(items[0])) + return p0 + + def exit(self, meta, items): + p0 = None + q = items[1] + p0 = make_sentence(get_lineno(items[0]), "EXIT_%s" % q) + for loop in gl.LOOPS: + if q == loop.type: + return p0 + error(get_lineno(items[0]), "Syntax Error: EXIT %s out of loop" % q) + return p0 + + def continue_(self, meta, items): + p0 = None + q = items[1] + p0 = make_sentence(get_lineno(items[0]), "CONTINUE_%s" % q) + for i in gl.LOOPS: + if q == i[0]: + return p0 + error(get_lineno(items[0]), "Syntax Error: CONTINUE %s out of loop" % q) + return p0 + + def print_sentence(self, meta, items): + p0 = None + global PRINT_IS_USED + p0 = items[1] + PRINT_IS_USED = True + return p0 + + def print_elem_expr(self, meta, items): + p0 = None + p0 = items[0] + if items[0] is not None and items[0].type_ == Type.boolean: + p0 = make_typecast(Type.ubyte, items[0], get_lineno(items[0])) + return p0 + + def print_list_expr(self, meta, items): + p0 = None + if items[0] in ("BOLD", "ITALIC"): + p0 = make_sentence( + get_lineno(items[0]), items[0] + "_TMP", make_typecast(Type.ubyte, items[1], get_lineno(items[0])) ) - return False - - if not isinstance(remaining, list): - error( - lineno, - "Mismatched vector size. Missing %i extra dimension(s)" % len(boundlist), + else: + p0 = items[0] + return p0 + + def attr_list(self, meta, items): + p0 = None + p0 = items[0] + return p0 + + def attr_list_list(self, meta, items): + p0 = None + p0 = make_block(items[0], items[1]) + return p0 + + def attr(self, meta, items): + p0 = None + p0 = make_sentence( + get_lineno(items[0]), items[0] + "_TMP", make_typecast(Type.ubyte, items[1], get_lineno(items[0])) + ) + return p0 + + def print_list_epsilon(self, meta, items): + p0 = None + p0 = None + return p0 + + def print_list_elem(self, meta, items): + p0 = None + p0 = make_sentence(meta_line(meta), "PRINT", items[0]) + p0.eol = True + return p0 + + def print_list(self, meta, items): + p0 = None + p0 = items[0] + p0.eol = items[2] is not None + if items[2] is not None: + p0.append_child(items[2]) + return p0 + + def print_list_comma(self, meta, items): + p0 = None + p0 = items[0] + p0.eol = items[2] is not None + p0.append_child(make_sentence(get_lineno(items[1]), "PRINT_COMMA")) + if items[2] is not None: + p0.append_child(items[2]) + return p0 + + def print_list_at(self, meta, items): + p0 = None + p0 = make_sentence( + get_lineno(items[0]), + "PRINT_AT", + make_typecast(Type.ubyte, items[1], get_lineno(items[0])), + make_typecast(Type.ubyte, items[3], get_lineno(items[2])), + ) + return p0 + + def print_list_tab(self, meta, items): + p0 = None + p0 = make_sentence(get_lineno(items[0]), "PRINT_TAB", make_typecast(Type.ubyte, items[1], get_lineno(items[0]))) + return p0 + + def on_goto(self, meta, items): + p0 = None + expr = make_typecast(Type.ubyte, items[1], get_lineno(items[0])) + p0 = make_sentence(get_lineno(items[0]), "ON_" + items[2], expr, *items[3]) + return p0 + + def label_list(self, meta, items): + p0 = None + entry = check_and_make_label(items[0], get_lineno(items[0])) + p0 = [entry] + return p0 + + def label_list_list(self, meta, items): + p0 = None + p0 = items[0] + entry = check_and_make_label(items[2], get_lineno(items[2])) + items[0].append(entry) + return p0 + + def return_(self, meta, items): + p0 = None + if not FUNCTION_LEVEL: + p0 = make_sentence(get_lineno(items[0]), "RETURN") + return p0 + if FUNCTION_LEVEL[-1].class_ != CLASS.sub: + error(get_lineno(items[0]), "Syntax Error: Function must RETURN a value.") + p0 = None + return p0 + p0 = make_sentence(get_lineno(items[0]), "RETURN", FUNCTION_LEVEL[-1]) + return p0 + + def return_expr(self, meta, items): + p0 = None + if not FUNCTION_LEVEL: + error(get_lineno(items[0]), "Syntax Error: Returning value out of FUNCTION") + p0 = None + return p0 + if FUNCTION_LEVEL[-1].class_ is CLASS.unknown: + p0 = None + return p0 + if FUNCTION_LEVEL[-1].class_ != CLASS.function: + error(get_lineno(items[0]), "Syntax Error: SUBs cannot return a value") + p0 = None + return p0 + if FUNCTION_LEVEL[-1].type_ is None: + p0 = None + return p0 + if is_numeric(items[1]) and FUNCTION_LEVEL[-1].type_.final == Type.string: + error(get_lineno(items[1]), "Type Error: Function must return a string, not a numeric value") + p0 = None + return p0 + if not is_numeric(items[1]) and FUNCTION_LEVEL[-1].type_.final != Type.string: + error(get_lineno(items[1]), "Type Error: Function must return a numeric value, not a string") + p0 = None + return p0 + p0 = make_sentence( + get_lineno(items[0]), + "RETURN", + FUNCTION_LEVEL[-1], + make_typecast(FUNCTION_LEVEL[-1].type_, items[1], get_lineno(items[0])), + ) + return p0 + + def pause(self, meta, items): + p0 = None + p0 = make_sentence(get_lineno(items[0]), "PAUSE", make_typecast(Type.uinteger, items[1], get_lineno(items[0]))) + return p0 + + def poke(self, meta, items): + p0 = None + i = 2 if isinstance(items[1], Symbol) or items[1] is None else 3 + if items[i - 1] is None or items[i + 2 - 1] is None: + p0 = None + return p0 + p0 = make_sentence( + get_lineno(items[0]), + "POKE", + make_typecast(Type.uinteger, items[i - 1], get_lineno(items[i + 1 - 1])), + make_typecast(Type.ubyte, items[i + 2 - 1], get_lineno(items[i + 1 - 1])), + ) + return p0 + + def poke2(self, meta, items): + p0 = None + i = 2 if isinstance(items[1], Symbol) or items[1] is None else 3 + if items[i + 1 - 1] is None or items[i + 3 - 1] is None: + p0 = None + return p0 + p0 = make_sentence( + get_lineno(items[0]), + "POKE", + make_typecast(Type.uinteger, items[i + 1 - 1], get_lineno(items[i + 2 - 1])), + make_typecast(items[i - 1], items[i + 3 - 1], get_lineno(items[i + 3 - 1])), + ) + return p0 + + def poke3(self, meta, items): + p0 = None + i = 2 if isinstance(items[1], Symbol) or items[1] is None else 3 + if items[i + 2 - 1] is None or items[i + 4 - 1] is None: + p0 = None + return p0 + p0 = make_sentence( + get_lineno(items[0]), + "POKE", + make_typecast(Type.uinteger, items[i + 2 - 1], get_lineno(items[i + 3 - 1])), + make_typecast(items[i - 1], items[i + 4 - 1], get_lineno(items[i + 5 - 1])), + ) + return p0 + + def out(self, meta, items): + p0 = None + p0 = make_sentence( + get_lineno(items[0]), + "OUT", + make_typecast(Type.uinteger, items[1], get_lineno(items[2])), + make_typecast(Type.ubyte, items[3], get_lineno(items[3])), + ) + return p0 + + def simple_instruction(self, meta, items): + p0 = None + p0 = make_sentence(get_lineno(items[0]), items[0], make_typecast(Type.ubyte, items[1], get_lineno(items[0]))) + return p0 + + def save_code(self, meta, items): + p0 = None + expr = items[1] + if expr.type_ != Type.string: + errmsg.syntax_error_expected_string(get_lineno(items[0]), expr.type_) + if len(items) + 1 == 4: + if items[2].upper() not in ("SCREEN", "SCREEN$"): + error(get_lineno(items[2]), 'Unexpected "%s" ID. Expected "SCREEN$" instead' % items[2]) + return None + start = make_number(16384, lineno=get_lineno(items[0])) + length = make_number(6912, lineno=get_lineno(items[0])) + else: + start = make_typecast(Type.uinteger, items[3], get_lineno(items[3])) + length = make_typecast(Type.uinteger, items[5], get_lineno(items[5])) + p0 = make_sentence(get_lineno(items[0]), items[0], expr, start, length) + return p0 + + def save_data(self, meta, items): + p0 = None + if items[1].type_ != Type.string: + errmsg.syntax_error_expected_string(get_lineno(items[0]), items[1].type_) + if len(items) + 1 != 4: + entry = SYMBOL_TABLE.access_id(items[3], get_lineno(items[3])) + if entry is None: + p0 = None + return p0 + mark_entry_as_accessed(entry) + access = entry + start = make_unary(get_lineno(items[3]), "ADDRESS", access, type_=Type.uinteger) + if entry.class_ == CLASS.array: + length = make_number(entry.memsize, lineno=get_lineno(items[3])) + else: + length = make_number(entry.type_.size, lineno=get_lineno(items[3])) + else: + access = SYMBOL_TABLE.access_label(gl.ZXBASIC_USER_DATA, get_lineno(items[2]), SYMBOL_TABLE.global_scope) + start = make_unary(get_lineno(items[2]), "ADDRESS", access, type_=Type.uinteger) + access = SYMBOL_TABLE.access_label( + gl.ZXBASIC_USER_DATA_LEN, get_lineno(items[2]), SYMBOL_TABLE.global_scope ) - return False + length = make_unary(get_lineno(items[2]), "ADDRESS", access, type_=Type.uinteger) + p0 = make_sentence(get_lineno(items[0]), items[0], items[1], start, length) + return p0 + + def load_or_verify(self, meta, items): + p0 = None + p0 = items[0] + return p0 + + def load_code(self, meta, items): + p0 = None + if items[1].type_ != Type.string: + errmsg.syntax_error_expected_string(get_lineno(items[2]), items[1].type_) + if len(items) + 1 == 4: + if items[2].upper() not in ("SCREEN", "SCREEN$", "CODE"): + error(get_lineno(items[2]), 'Unexpected "%s" ID. Expected "SCREEN$" instead' % items[2]) + return None + if items[2].upper() == "CODE": + start = make_number(0, lineno=get_lineno(items[2])) + length = make_number(0, lineno=get_lineno(items[2])) + else: + start = make_number(16384, lineno=get_lineno(items[2])) + length = make_number(6912, lineno=get_lineno(items[2])) + else: + start = make_typecast(Type.uinteger, items[3], get_lineno(items[2])) + if len(items) + 1 == 5: + length = make_number(0, lineno=get_lineno(items[2])) + else: + length = make_typecast(Type.uinteger, items[5], get_lineno(items[4])) + p0 = make_sentence(get_lineno(items[2]), items[0], items[1], start, length) + return p0 + + def load_data(self, meta, items): + p0 = None + if items[1].type_ != Type.string: + errmsg.syntax_error_expected_string(get_lineno(items[0]), items[1].type_) + if len(items) + 1 != 4: + entry = SYMBOL_TABLE.access_id(items[3], get_lineno(items[3])) + if entry is None: + p0 = None + return p0 + mark_entry_as_accessed(entry) + start = make_unary(get_lineno(items[3]), "ADDRESS", entry, type_=Type.uinteger) + if entry.class_ == CLASS.array: + length = make_number(entry.memsize, lineno=get_lineno(items[3])) + else: + length = make_number(entry.type_.size, lineno=get_lineno(items[3])) + else: + entry = SYMBOL_TABLE.access_label(gl.ZXBASIC_USER_DATA, get_lineno(items[2]), SYMBOL_TABLE.global_scope) + start = make_unary(get_lineno(items[2]), "ADDRESS", entry, type_=Type.uinteger) + entry = SYMBOL_TABLE.access_label(gl.ZXBASIC_USER_DATA_LEN, get_lineno(items[2]), SYMBOL_TABLE.global_scope) + length = make_unary(get_lineno(items[2]), "ADDRESS", entry, type_=Type.uinteger) + p0 = make_sentence(get_lineno(items[2]), items[0], items[1], start, length) + return p0 + + def numbertype(self, meta, items): + p0 = None + p0 = make_type(items[0].lower(), get_lineno(items[0])) + return p0 + + def expr_plus_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "PLUS", items[0], items[2], lambda x, y: x + y) + return p0 + + def expr_minus_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "MINUS", items[0], items[2], lambda x, y: x - y) + return p0 + + def expr_mul_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "MUL", items[0], items[2], lambda x, y: x * y) + return p0 + + def expr_div_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "DIV", items[0], items[2], lambda x, y: x / y) + return p0 + + def expr_mod_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "MOD", items[0], items[2], lambda x, y: x % y) + return p0 + + def expr_pow_expr(self, meta, items): + p0 = None + p0 = make_binary( + get_lineno(items[1]), + "POW", + make_typecast(Type.float_, items[0], get_lineno(items[1])), + make_typecast(Type.float_, items[2], meta_line(meta)), + lambda x, y: x**y, + ) + return p0 + + def expr_shl_expr(self, meta, items): + p0 = None + if items[0] is None or items[2] is None: + p0 = None + return p0 + if items[0].type_ in (Type.float_, Type.fixed): + items[0] = make_typecast(Type.ulong, items[0], get_lineno(items[1])) + p0 = make_binary( + get_lineno(items[1]), + "SHL", + items[0], + make_typecast(Type.ubyte, items[2], get_lineno(items[1])), + lambda x, y: x << y, + ) + return p0 + + def expr_shr_expr(self, meta, items): + p0 = None + if items[0] is None or items[2] is None: + p0 = None + return p0 + if items[0].type_ in (Type.float_, Type.fixed): + items[0] = make_typecast(Type.ulong, items[0], get_lineno(items[1])) + p0 = make_binary( + get_lineno(items[1]), + "SHR", + items[0], + make_typecast(Type.ubyte, items[2], get_lineno(items[1])), + lambda x, y: x >> y, + ) + return p0 + + def minus_expr(self, meta, items): + p0 = None + p0 = make_unary(get_lineno(items[0]), "MINUS", items[1], lambda x: -x) + return p0 + + def expr_eq_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "EQ", items[0], items[2], lambda x, y: x == y) + return p0 + + def expr_lt_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "LT", items[0], items[2], lambda x, y: x < y) + return p0 + + def expr_le_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "LE", items[0], items[2], lambda x, y: x <= y) + return p0 + + def expr_gt_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "GT", items[0], items[2], lambda x, y: x > y) + return p0 + + def expr_ge_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "GE", items[0], items[2], lambda x, y: x >= y) + return p0 + + def expr_ne_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "NE", items[0], items[2], lambda x, y: x != y) + return p0 + + def expr_or_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "OR", items[0], items[2], lambda x, y: x or y) + return p0 + + def expr_bor_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "BOR", items[0], items[2], lambda x, y: x | y) + return p0 + + def expr_xor_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "XOR", items[0], items[2], lambda x, y: x and (not y) or (not x and y)) + return p0 + + def expr_bxor_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "BXOR", items[0], items[2], lambda x, y: x ^ y) + return p0 + + def expr_and_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "AND", items[0], items[2], lambda x, y: x and y) + return p0 + + def expr_band_expr(self, meta, items): + p0 = None + p0 = make_binary(get_lineno(items[1]), "BAND", items[0], items[2], lambda x, y: x & y) + return p0 + + def not_expr(self, meta, items): + p0 = None + p0 = make_unary(get_lineno(items[0]), "NOT", items[1], lambda x: not x) + return p0 + + def bnot_expr(self, meta, items): + p0 = None + p0 = make_unary(get_lineno(items[0]), "BNOT", items[1], lambda x: ~x) + return p0 + + def lp_expr_rp(self, meta, items): + p0 = None + p0 = items[1] + return p0 + + def cast(self, meta, items): + p0 = None + p0 = make_typecast(items[2], items[4], get_lineno(items[5])) + return p0 + + def number_expr(self, meta, items): + p0 = None + p0 = make_number(items[0], lineno=get_lineno(items[0])) + return p0 + + def expr_pi(self, meta, items): + p0 = None + p0 = make_number(PI, lineno=get_lineno(items[0]), type_=Type.float_) + return p0 + + def expr_string(self, meta, items): + p0 = None + p0 = items[0] + return p0 + + def string_func_call(self, meta, items): + p0 = None + p0 = make_strslice(get_lineno(items[0]), items[0], items[1][0], items[1][1]) + return p0 + + def string_func_call_single(self, meta, items): + p0 = None + p0 = make_strslice(get_lineno(items[0]), items[0], items[2], items[2]) + return p0 + + def string_str(self, meta, items): + p0 = None + p0 = sym.STRING(items[0], get_lineno(items[0])) + return p0 + + def string_lprp(self, meta, items): + p0 = None + p0 = items[0] + return p0 + + def string_lp_expr_rp(self, meta, items): + p0 = None + p0 = make_strslice(get_lineno(items[1]), items[0], items[2], items[2]) + return p0 + + def expr_id_substr(self, meta, items): + p0 = None + entry = SYMBOL_TABLE.get_entry(items[0]) + if entry is not None and entry.type_ == Type.string and (entry.token == "CONST"): + p0 = make_strslice(get_lineno(items[0]), entry, items[1][0], items[1][1]) + return p0 + entry = SYMBOL_TABLE.access_var(items[0], get_lineno(items[0]), default_type=Type.string) + p0 = None + if entry is None: + return p0 + mark_entry_as_accessed(entry) + p0 = make_strslice(get_lineno(items[0]), entry, items[1][0], items[1][1]) + return p0 + + def string_substr(self, meta, items): + p0 = None + p0 = make_strslice(get_lineno(items[0]), items[0], items[1][0], items[1][1]) + return p0 - if len(remaining) != boundlist[0].count: + def string_expr_lp(self, meta, items): + p0 = None + if items[1].type_ != Type.string: error( - lineno, - "Mismatched vector size. Expected %i elements, got %i." % (boundlist[0].count, len(remaining)), + meta_line(meta), + "Expected a string type expression. Got %s type instead" % Type.to_string(items[1].type_), ) - return False # It's wrong. :-( - - for row in remaining: - if not check_bound(boundlist[1:], row): - return False - - return True - - p[0] = None - if p[4] is None or p[6] is None or p[8] is None: - return - - if not check_bound(p[4].children, p[8]): - return - - id_, lineno = p[2][0] - SYMBOL_TABLE.declare_array(id_, lineno, p[6], p[4], default_value=p[8]) - - entry = SYMBOL_TABLE.get_entry(id_) - if entry is None: - return - - if p[6] == TYPE.string or entry.type_ == TYPE.string: - errmsg.syntax_error_cannot_initialize_array_of_type(p.lineno(1), TYPE.string) - return - - -def p_bound_list(p): - """bound_list : bound""" - p[0] = make_bound_list(p[1]) - - -def p_bound_list_bound(p): - """bound_list : bound_list COMMA bound""" - p[0] = make_bound_list(p[1], p[3]) - - -def p_bound(p): - """bound : expr""" - p[0] = make_bound(make_number(OPTIONS.array_base, lineno=p.lineno(1)), p[1], p.lexer.lineno) - - -def p_bound_to_bound(p): - """bound : expr TO expr""" - p[0] = make_bound(p[1], p[3], p.lineno(2)) - + p0 = None + else: + p0 = make_strslice(meta_line(meta), items[1], items[3][0], items[3][1]) + return p0 + + def subind_str(self, meta, items): + p0 = None + p0 = ( + make_typecast(Type.uinteger, items[1], get_lineno(items[0])), + make_typecast(Type.uinteger, items[3], get_lineno(items[2])), + ) + return p0 -def p_const_vector(p): - """const_vector : LBRACE const_vector_list RBRACE - | LBRACE const_number_list RBRACE - """ - p[0] = p[2] + def subind_strto(self, meta, items): + p0 = None + p0 = ( + make_typecast(Type.uinteger, make_number(0, lineno=get_lineno(items[1])), get_lineno(items[0])), + make_typecast(Type.uinteger, items[2], get_lineno(items[1])), + ) + return p0 + def subind_tostr(self, meta, items): + p0 = None + p0 = ( + make_typecast(Type.uinteger, items[1], get_lineno(items[0])), + make_typecast( + Type.uinteger, + make_number(gl.MAX_STRSLICE_IDX, lineno=get_lineno(items[3])), + lineno=get_lineno(items[3]), + ), + get_lineno(items[2]), + ) + return p0 -def p_const_vector_elem_list(p): - """const_number_list : expr""" - if p[1] is None: - return + def subind_to(self, meta, items): + p0 = None + p0 = ( + make_typecast(Type.uinteger, make_number(0, lineno=get_lineno(items[1])), get_lineno(items[0])), + make_typecast( + Type.uinteger, make_number(gl.MAX_STRSLICE_IDX, lineno=get_lineno(items[2])), get_lineno(items[1]) + ), + ) + return p0 - if not is_static(p[1]): - if isinstance(p[1], sym.UNARY): - tmp = make_constexpr(p.lineno(1), p[1]) + def id_expr(self, meta, items): + p0 = None + entry = SYMBOL_TABLE.access_id(items[0], get_lineno(items[0]), default_class=CLASS.var) + if entry is None: + p0 = None + return p0 + mark_entry_as_accessed(entry) + if entry.type_ == Type.auto: + entry.type_ = _TYPEREF(gl.DEFAULT_TYPE) + errmsg.warning_implicit_type(get_lineno(items[0]), items[0], entry.type_.name) + p0 = entry + if p0 is not None: + if not hasattr(gl, "expr_linenos"): + gl.expr_linenos = {} + gl.expr_linenos[id(p0)] = get_lineno(items[0]) + if entry.class_ == CLASS.array: + if not LET_ASSIGNMENT: + error(get_lineno(items[0]), "Variable '%s' is an array and cannot be used in this context" % items[0]) + p0 = None + elif entry.class_ == CLASS.function: + p0 = make_call(items[0], get_lineno(items[0]), make_arg_list(None)) + elif entry.class_ == CLASS.sub: + errmsg.syntax_error_is_a_sub_not_a_func(get_lineno(items[0]), items[0]) + p0 = None + return p0 + + def addr_of_id(self, meta, items): + p0 = None + id_: Id = items[1] + entry = SYMBOL_TABLE.access_id(id_.name, id_.lineno, ignore_explicit_flag=True) + if entry is None: + p0 = None + return p0 + entry.has_address = True + mark_entry_as_accessed(entry) + result = make_unary(get_lineno(items[0]), "ADDRESS", entry, type_=_TYPE(gl.PTR_TYPE)) + if is_dynamic(entry): + p0 = result else: - errmsg.syntax_error_not_constant(p.lexer.lineno) - p[0] = None - return - else: - tmp = p[1] - - p[0] = [tmp] - + p0 = make_constexpr(get_lineno(items[0]), result) + return p0 + + def expr_bexpr(self, meta, items): + p0 = None + p0 = items[0] + return p0 + + def expr_funccall(self, meta, items): + p0 = None + p0 = items[0] + return p0 + + def idcall_expr(self, meta, items): + p0 = None + if items[1] is None: + p0 = None + return p0 + p0 = make_call(items[0], get_lineno(items[0]), items[1]) + if p0 is None: + return p0 + if p0.token in ("STRSLICE", "ID", "STRING") or (p0.token == "CONST" and p0.type_ == Type.string): + entry = SYMBOL_TABLE.access_call(items[0], get_lineno(items[0])) + mark_entry_as_accessed(entry) + return p0 + convert_to_function(p0.entry, CLASS.function, get_lineno(items[0])) + mark_entry_as_accessed(p0.entry) + return p0 + + def array_eq_error(self, meta, items): + p0 = None + error(get_lineno(items[3]), f"Invalid assignment. Variable {items[1]}() is an array") + p0 = None + return p0 + + def arr_access_expr(self, meta, items): + p0 = None + p0 = make_call(items[0], get_lineno(items[0]), items[1]) + if p0 is None: + return p0 + entry = SYMBOL_TABLE.access_call(items[0], get_lineno(items[0])) + mark_entry_as_accessed(entry) + return p0 + + def let_arr_substr(self, meta, items): + p0 = None + i = 2 if items[0].upper() == "LET" else 1 + id_ = items[i - 1] + arg_list = items[i + 1 - 1] + substr = items[i + 2 - 1] + expr_ = items[i + 4 - 1] + p0 = make_array_substr_assign(get_lineno(items[i - 1]), id_, arg_list, substr, expr_) + return p0 + + def let_arr_substr_single(self, meta, items): + p0 = None + i = 2 if items[0].upper() == "LET" else 1 + id_ = items[i - 1] + arg_list = items[i + 1 - 1] + substr = (items[i + 3 - 1], items[i + 3 - 1]) + expr_ = items[i + 6 - 1] + p0 = make_array_substr_assign(get_lineno(items[i - 1]), id_, arg_list, substr, expr_) + return p0 + + def let_arr_substr_in_args(self, meta, items): + p0 = None + i = 2 if items[0].upper() == "LET" else 1 + id_ = items[i - 1] + arg_list = items[i + 2 - 1] + substr = (arg_list.children.pop().value, make_number(gl.MAX_STRSLICE_IDX, lineno=get_lineno(items[i + 3 - 1]))) + expr_ = items[i + 6 - 1] + p0 = make_array_substr_assign(get_lineno(items[i - 1]), id_, arg_list, substr, expr_) + return p0 + + def let_arr_substr_in_args2(self, meta, items): + p0 = None + i = 2 if items[0].upper() == "LET" else 1 + id_ = items[i - 1] + arg_list = items[i + 2 - 1] + top_ = items[i + 5 - 1] + substr = (make_number(0, lineno=get_lineno(items[i + 4 - 1])), top_) + expr_ = items[i + 8 - 1] + p0 = make_array_substr_assign(get_lineno(items[i - 1]), id_, arg_list, substr, expr_) + return p0 + + def let_arr_substr_in_args3(self, meta, items): + p0 = None + i = 2 if items[0].upper() == "LET" else 1 + id_ = items[i - 1] + arg_list = items[i + 2 - 1] + substr = ( + make_number(0, lineno=get_lineno(items[i + 4 - 1])), + make_number(gl.MAX_STRSLICE_IDX, lineno=get_lineno(items[i + 3 - 1])), + ) + expr_ = items[i + 7 - 1] + p0 = make_array_substr_assign(get_lineno(items[i - 1]), id_, arg_list, substr, expr_) + return p0 + + def let_arr_substr_in_args4(self, meta, items): + p0 = None + i = 2 if items[0].upper() == "LET" else 1 + id_ = items[i - 1] + arg_list = items[i + 2 - 1] + substr = (arg_list.children.pop().value, items[i + 4 - 1]) + expr_ = items[i + 7 - 1] + p0 = make_array_substr_assign(get_lineno(items[i - 1]), id_, arg_list, substr, expr_) + return p0 + + def addr_of_array_element(self, meta, items): + p0 = None + p0 = None + if items[2] is None: + return p0 + result = make_array_access(items[1], get_lineno(items[1]), items[2]) + if result is None: + return p0 + mark_entry_as_accessed(result.entry) + p0 = make_unary(get_lineno(items[0]), "ADDRESS", result, type_=_TYPE(gl.PTR_TYPE)) + return p0 + + def err_undefined_arr_access(self, meta, items): + p0 = None + error(get_lineno(items[1]), 'Undeclared array "%s"' % items[1]) + p0 = None + return p0 + + def bexpr_func(self, meta, items): + p0 = None + args = make_arg_list(make_argument(items[1], get_lineno(items[1]))) + p0 = make_call(items[0], get_lineno(items[0]), args) + if p0 is None: + return p0 + if p0.token in ("STRSLICE", "VAR", "STRING"): + entry = SYMBOL_TABLE.access_call(items[0], get_lineno(items[0])) + mark_entry_as_accessed(entry) + return p0 + convert_to_function(p0.entry, CLASS.function, get_lineno(items[0])) + mark_entry_as_accessed(p0.entry) + return p0 + + def arg_list(self, meta, items): + p0 = None + p0 = make_arg_list(None) + return p0 + + def arg_list_arg(self, meta, items): + p0 = None + p0 = items[1] + return p0 + + def arguments(self, meta, items): + p0 = None + if items[0] is None: + p0 = None + return p0 + p0 = make_arg_list(items[0]) + return p0 + + def arguments_argument(self, meta, items): + p0 = None + if items[0] is None or items[2] is None: + p0 = None + else: + p0 = make_arg_list(items[0], items[2]) + return p0 + + def argument(self, meta, items): + p0 = None + p0 = make_argument(items[0], meta_line(meta)) + return p0 + + def named_argument(self, meta, items): + p0 = None + p0 = make_argument(items[2], meta_line(meta), name=items[0]) + return p0 + + def argument_array(self, meta, items): + p0 = None + entry = SYMBOL_TABLE.access_array(items[0], get_lineno(items[0])) + if entry is None: + p0 = None + return p0 + mark_entry_as_accessed(entry) + p0 = make_argument(entry, meta_line(meta)) + return p0 + + def funcdecl(self, meta, items): + p0 = None + if items[0] is None: + p0 = None + return p0 + p0 = items[0] + p0.local_symbol_table = SYMBOL_TABLE.current_scope + p0.locals_size = SYMBOL_TABLE.leave_scope() + FUNCTION_LEVEL.pop() + p0.entry.ref.body = items[1] + p0.local_symbol_table.owner = p0 + p0.entry.ref.forwarded = False + return p0 + + def funcdeclforward(self, meta, items): + p0 = None + if items[1] is None: + if FUNCTION_LEVEL: + FUNCTION_LEVEL.pop() + return p0 + if items[1].entry.forwarded: + error(get_lineno(items[0]), "duplicated declaration for function '%s'" % items[1].name) + items[1].entry.ref.forwarded = True + SYMBOL_TABLE.leave_scope(show_warnings=False) + FUNCTION_LEVEL.pop() + return p0 + + def function_header(self, meta, items): + p0 = None + p0 = items[0] + return p0 + + def function_header_pre(self, meta, items): + p0 = None + if items[0] is None or items[1] is None: + p0 = None + return p0 + forwarded = items[0].entry.forwarded + p0 = items[0] + p0.append_child(items[1]) + p0.params_size = items[1].size + lineno = get_lineno(items[2]) + previoustype_ = p0.type_ + if not items[2].implicit or p0.entry.type_ is None or p0.entry.type_ == Type.unknown: + p0.type_ = items[2] + if items[2].implicit and p0.entry.class_ == CLASS.function: + errmsg.warning_implicit_type(items[2].lineno, p0.entry.name, p0.type_.name) + if forwarded and previoustype_ != p0.type_: + errmsg.syntax_error_func_type_mismatch(lineno, p0.entry) + p0 = None + return p0 + if forwarded: + p1 = p0.entry.ref.params + p2 = items[1].children + if len(p1) != len(p2): + errmsg.syntax_error_parameter_mismatch(lineno, p0.entry) + p0 = None + return p0 + for a, b in zip(p1, p2): + if a.name != b.name: + warning( + lineno, "Parameter '%s' in function '%s' has been renamed to '%s'" % (a.name, p0.name, b.name) + ) + if a.type_ != b.type_ or a.byref != b.byref: + errmsg.syntax_error_parameter_mismatch(lineno, p0.entry) + p0 = None + return p0 + p0.entry.ref.params = items[1] + if FUNCTION_LEVEL[-1].class_ == CLASS.sub and (not items[2].implicit): + error(lineno, "SUBs cannot have a return type definition") + p0 = None + return p0 + if FUNCTION_LEVEL[-1].class_ == CLASS.function: + src.api.check.check_type_is_explicit(p0.lineno, p0.entry.name, items[2]) + if p0.entry.convention == CONVENTION.fastcall and len(items[1]) > 1: + class_ = "SUB" if FUNCTION_LEVEL[-1].class_ == CLASS.sub else "FUNCTION" + errmsg.warning_fastcall_with_N_parameters(lineno, class_, p0.entry.name, len(items[1])) + return p0 + + def function_def(self, meta, items): + p0 = None + convention = items[1] + name = items[2] + class_ = CLASS.sub if items[0] == "SUB" else CLASS.function + p0 = make_func_declaration(name, get_lineno(items[2]), class_) + SYMBOL_TABLE.enter_scope(name) + entry = SYMBOL_TABLE.get_entry(name) + FUNCTION_LEVEL.append(entry) + if entry.class_ in (CLASS.function, CLASS.sub): + FUNCTION_LEVEL[-1].ref.convention = convention + return p0 + + def convention(self, meta, items): + p0 = None + p0 = CONVENTION.stdcall + return p0 + + def convention2(self, meta, items): + p0 = None + p0 = CONVENTION.fastcall + return p0 + + def param_decl_none(self, meta, items): + p0 = None + p0 = make_param_list(None) + return p0 + + def param_decl(self, meta, items): + p0 = None + p0 = items[1] + return p0 + + def param_decl_list(self, meta, items): + p0 = None + p0 = make_param_list(items[0]) + return p0 + + def param_decl_list2(self, meta, items): + p0 = None + if items[0] is not None and items[2] is not None: + if items[2].default_value is None and items[0][-1].default_value is not None: + errmsg.syntax_error_mandatory_param_after_optional(items[2].lineno, items[0][-1].name, items[2].name) + p0 = make_param_list(items[0], items[2]) + return p0 + + def param_byref_definition(self, meta, items): + p0 = None + p0 = items[1] + if p0 is not None: + p0.ref.byref = True + return p0 + + def param_byval_definition(self, meta, items): + p0 = None + param_def = items[1] + p0 = param_def + if p0 is not None: + if param_def.class_ == CLASS.array: + errmsg.syntax_error_cannot_pass_array_by_value(get_lineno(items[0]), param_def.name) + p0 = None + return p0 + param_def.ref.byref = False + return p0 + + def param_definition(self, meta, items): + p0 = None + param_def = items[0] + p0 = param_def + if p0 is not None: + if param_def.class_ == CLASS.array: + param_def.ref.byref = True + else: + param_def.ref.byref = OPTIONS.default_byref + return p0 + + def param_def_array(self, meta, items): + p0 = None + typeref = items[3] + if typeref is None: + p0 = None + return p0 + lineno = items[0].lineno + id_ = items[0].name + src.api.check.check_type_is_explicit(lineno, id_, typeref) + p0 = make_param_decl(id_, lineno, typeref, is_array=True) + return p0 + + def param_def_type(self, meta, items): + p0 = None + id_: Id = items[0] + typedef = items[1] + if typedef is not None: + src.api.check.check_type_is_explicit(id_.lineno, id_.name, typedef) + default_value = make_typecast(typedef, items[2], id_.lineno) + p0 = make_param_decl(id_.name, id_.lineno, typedef, is_array=False, default_value=default_value) + return p0 + + def param_def_default_arg_value(self, meta, items): + p0 = None + if len(items) + 1 == 1: + p0 = None + return p0 + p0 = items[1] + return p0 + return p0 + + def function_body(self, meta, items): + p0 = None + if not FUNCTION_LEVEL: + end_tok = items[-1] + error(get_lineno(end_tok), "Unexpected token '%s'. No Function or Sub has been defined." % end_tok) + p0 = None + return None + a = FUNCTION_LEVEL[-1].class_ + if a not in (CLASS.sub, CLASS.function): + p0 = None + return None + end_tok = items[-1] + b = end_tok.split()[-1].lower() + if a != b: + error(get_lineno(end_tok), "Unexpected token '%s'. Should be 'END %s'" % (end_tok.upper(), a.upper())) + p0 = None + else: + p0 = make_block() if len(items) == 1 else items[0] + return p0 + + def type_def_empty(self, meta, items): + p0 = None + p0 = make_type(_TYPE(gl.DEFAULT_TYPE).name, meta_line(meta), implicit=True) + return p0 + + def type_def(self, meta, items): + p0 = None + p0 = make_type(items[1], get_lineno(items[1]), implicit=False) + return p0 + + def type_def_id(self, meta, items): + p0 = None + errmsg.error(get_lineno(items[1]), "Syntax Error. Unexpected token '%s' " % items[1]) + p0 = make_type("float", get_lineno(items[1]), implicit=False) + return p0 + + def type(self, meta, items): + p0 = None + p0 = items[0].lower() + return p0 + + def preproc_line_init(self, meta, items): + p0 = None + INITS.add(items[1]) + return p0 + + def preproc_line_require(self, meta, items): + p0 = None + arch.target.backend.REQUIRES.add(items[1]) + return p0 + + def preproc_line_pragma_option(self, meta, items): + p0 = None + try: + setattr(OPTIONS, items[1], items[3]) + except src.api.options.UndefinedOptionError: + errmsg.warning_ignoring_unknown_pragma(get_lineno(items[1]), items[1]) + return p0 -def p_const_vector_elem_list_list(p): - """const_number_list : const_number_list COMMA expr""" - if p[1] is None or p[3] is None: - return + def preproc_pragma_push(self, meta, items): + p0 = None + try: + OPTIONS[items[3]].push() + except src.api.options.UndefinedOptionError: + errmsg.warning_ignoring_unknown_pragma(get_lineno(items[3]), items[3]) + return p0 - if not is_static(p[3]): - if isinstance(p[3], sym.UNARY): - tmp = make_constexpr(p.lineno(2), p[3]) + def preproc_pragma_pop(self, meta, items): + p0 = None + try: + OPTIONS[items[3]].pop() + except src.api.options.UndefinedOptionError: + errmsg.warning_ignoring_unknown_pragma(get_lineno(items[3]), items[3]) + return p0 + + def expr_usr(self, meta, items): + p0 = None + if items[1].type_ == Type.string: + p0 = make_builtin(get_lineno(items[0]), "USR_STR", items[1], type_=Type.uinteger) else: - errmsg.syntax_error_not_constant(p.lineno(2)) - p[0] = None - return - else: - tmp = p[3] + p0 = make_builtin( + get_lineno(items[0]), + "USR", + make_typecast(Type.uinteger, items[1], get_lineno(items[0])), + type_=Type.uinteger, + ) + return p0 - if p[1] is not None: - p[1].append(tmp) - p[0] = p[1] + def expr_rnd(self, meta, items): + p0 = None + p0 = make_builtin(get_lineno(items[0]), "RND", None, type_=Type.float_) + return p0 + def expr_peek(self, meta, items): + p0 = None + p0 = make_builtin( + get_lineno(items[0]), "PEEK", make_typecast(Type.uinteger, items[1], get_lineno(items[0])), type_=Type.ubyte + ) + return p0 + + def expr_peektype_(self, meta, items): + p0 = None + if items[2] is None or items[4] is None: + p0 = None + return p0 + p0 = make_builtin( + get_lineno(items[0]), + "PEEK", + make_typecast(Type.uinteger, items[4], get_lineno(items[3])), + type_=cast(sym.TYPEREF, items[2]).type_, + ) + return p0 -def p_const_vector_list(p): - """const_vector_list : const_vector""" - p[0] = [p[1]] + def expr_in(self, meta, items): + p0 = None + p0 = make_builtin( + get_lineno(items[0]), "IN", make_typecast(Type.uinteger, items[1], get_lineno(items[0])), type_=Type.ubyte + ) + return p0 + def expr_lbound(self, meta, items): + p0 = None + entry = SYMBOL_TABLE.access_array(items[2], get_lineno(items[2])) + if entry is None: + p0 = None + return p0 + mark_entry_as_accessed(entry) + if entry.scope == SCOPE.parameter: + num = make_number(0, get_lineno(items[2]), Type.uinteger) + p0 = make_builtin(get_lineno(items[0]), items[0], [entry, num], type_=Type.uinteger) + else: + p0 = make_number(len(entry.bounds), get_lineno(items[2]), Type.uinteger) + return p0 + + def expr_lbound_expr(self, meta, items): + p0 = None + expr = items[4] + if expr is None: + p0 = None + return p0 + entry = SYMBOL_TABLE.access_array(items[2], get_lineno(items[2])) + if entry is None: + p0 = None + return p0 + mark_entry_as_accessed(entry) + num = make_typecast(Type.uinteger, expr, get_lineno(items[5])) + if num is None: + p0 = None + return p0 + if is_number(num) and entry.scope in (SCOPE.local, SCOPE.global_): + val = num.value + if val < 0 or val > len(entry.bounds): + error(get_lineno(items[5]), "Dimension out of range") + p0 = None + return p0 + if not val: + p0 = make_number(len(entry.bounds), get_lineno(items[2]), Type.uinteger) + elif items[0] == "LBOUND": + p0 = make_number(entry.bounds[val - 1].lower, get_lineno(items[2]), Type.uinteger) + else: + p0 = make_number(entry.bounds[val - 1].upper, get_lineno(items[2]), Type.uinteger) + return p0 + if items[0] == "LBOUND": + entry.ref.lbound_used = True + else: + entry.ref.ubound_used = True + p0 = make_builtin(get_lineno(items[0]), items[0], [entry, num], type_=Type.uinteger) + return p0 + + def len(self, meta, items): + p0 = None + arg = items[1] + if arg is None: + p0 = None + elif arg.token == "VAR" and arg.class_ == CLASS.array: + p0 = make_number(len(arg.bounds), lineno=get_lineno(items[0])) + elif arg.type_ != Type.string: + errmsg.syntax_error_expected_string(get_lineno(items[0]), Type.to_string(arg.type_)) + p0 = None + elif is_string(arg): + p0 = make_number(len(arg.value), lineno=get_lineno(items[0])) + else: + p0 = make_builtin(get_lineno(items[0]), "LEN", arg, type_=Type.uinteger) + return p0 -def p_const_vector_vector_list(p): - """const_vector_list : const_vector_list COMMA const_vector""" - if len(p[3]) != len(p[1][0]): - error(p.lineno(2), "All rows must have the same number of elements") - p[0] = None - return + def sizeof(self, meta, items): + p0 = None + if Type.to_type(items[2].lower()) is not None: + p0 = make_number(Type.size(Type.to_type(items[2].lower())), lineno=get_lineno(items[2])) + else: + entry = SYMBOL_TABLE.get_id_or_make_var(items[2], get_lineno(items[0])) + p0 = make_number(Type.size(entry.type_), lineno=get_lineno(items[2])) + return p0 + + def str(self, meta, items): + p0 = None + if is_number(items[1]): + p0 = sym.STRING(str(items[1].value), get_lineno(items[0])) + else: + p0 = make_builtin( + get_lineno(items[0]), + "STR", + make_typecast(Type.float_, items[1], get_lineno(items[0])), + type_=Type.string, + ) + return p0 + + def inkey(self, meta, items): + p0 = None + p0 = make_builtin(get_lineno(items[0]), "INKEY", None, type_=Type.string) + return p0 + + def chr_one(self, meta, items): + p0 = None + arg_list = make_arg_list(make_argument(items[1], get_lineno(items[0]))) + arg_list[0].value = make_typecast(Type.ubyte, arg_list[0].value, get_lineno(items[0])) + p0 = make_builtin(get_lineno(items[0]), "CHR", arg_list, type_=Type.string) + return p0 + + def chr(self, meta, items): + p0 = None + if len(items[1]) < 1: + error(get_lineno(items[0]), "CHR$ function need at less 1 parameter") + p0 = None + return p0 + for i in range(len(items[1])): + items[1][i].value = make_typecast(Type.ubyte, items[1][i].value, get_lineno(items[0])) + p0 = make_builtin(get_lineno(items[0]), "CHR", items[1], type_=Type.string) + return p0 + + def val(self, meta, items): + p0 = None + + def val(s): + try: + x = float(eval(s, {}, {})) + except: + x = 0 + warning(get_lineno(items[0]), f"Invalid string numeric constant '{s}' evaluated as 0") + return x + + if items[1].type_ != Type.string: + errmsg.syntax_error_expected_string(get_lineno(items[0]), Type.to_string(items[1].type_)) + p0 = None + else: + p0 = make_builtin(get_lineno(items[0]), "VAL", items[1], val, type_=Type.float_) + return p0 + + def code(self, meta, items): + p0 = None + + def asc(x): + if len(x): + return ord(x[0]) + return 0 + + if items[1] is None: + p0 = None + return p0 + if items[1].type_ != Type.string: + errmsg.syntax_error_expected_string(get_lineno(items[0]), Type.to_string(items[1].type_)) + p0 = None + else: + p0 = make_builtin(get_lineno(items[0]), "CODE", items[1], asc, type_=Type.ubyte) + return p0 + + def sgn(self, meta, items): + p0 = None + sgn = lambda x: x < 0 and -1 or (x > 0 and 1) or 0 + if items[1].type_ == Type.string: + error(get_lineno(items[0]), "Expected a numeric expression, got TYPE.string instead") + p0 = None + else: + if is_unsigned(items[1]) and (not is_number(items[1])): + warning(get_lineno(items[0]), "Sign of unsigned value is always 0 or 1") + p0 = make_builtin(get_lineno(items[0]), "SGN", items[1], sgn, type_=Type.byte_) + return p0 + + def expr_trig(self, meta, items): + p0 = None + p0 = make_builtin( + get_lineno(items[0]), + items[0], + make_typecast(Type.float_, items[1], get_lineno(items[0])), + { + "SIN": math.sin, + "COS": math.cos, + "TAN": math.tan, + "ASN": math.asin, + "ACS": math.acos, + "ATN": math.atan, + "LN": lambda y: math.log(y, math.exp(1)), + "EXP": math.exp, + "SQR": math.sqrt, + }[items[0]], + type_=Type.float_, + ) + return p0 + + def math_fn(self, meta, items): + p0 = None + p0 = items[0] + return p0 + + def expr_int(self, meta, items): + p0 = None + p0 = make_typecast(Type.long_, items[1], get_lineno(items[0])) + return p0 + + def abs(self, meta, items): + p0 = None + if is_unsigned(items[1]): + p0 = items[1] + warning(get_lineno(items[0]), "Redundant operation ABS for unsigned value") + return p0 + p0 = make_builtin(get_lineno(items[0]), "ABS", items[1], lambda x: x if x >= 0 else -x) + return p0 + + +def handle_parse_error(e): + if not isinstance(e, UnexpectedInput): + raise e + gl.reporting_syntax_error = True + try: + return _handle_parse_error_inner(e) + finally: + gl.reporting_syntax_error = False - p[0] = p[1] + [p[3]] - - -def p_staement_func_decl(p): - """statement : function_declaration""" - p[0] = p[1] - - -def p_statement_border(p): - """statement : BORDER expr""" - p[0] = make_sentence(p.lineno(1), "BORDER", make_typecast(TYPE.ubyte, p[2], p.lineno(1))) - - -def p_statement_plot(p): - """statement : PLOT expr COMMA expr""" - p[0] = make_sentence( - p.lineno(1), - "PLOT", - make_typecast(TYPE.ubyte, p[2], p.lineno(3)), - make_typecast(TYPE.ubyte, p[4], p.lineno(3)), - ) +def _handle_parse_error_inner(e): + from src.api import errmsg + from src.api.errmsg import error -def p_statement_plot_attr(p): - """statement : PLOT attr_list expr COMMA expr""" - p[0] = make_sentence( - p.lineno(1), - "PLOT", - make_typecast(TYPE.ubyte, p[3], p.lineno(4)), - make_typecast(TYPE.ubyte, p[5], p.lineno(4)), - p[2], - ) + from .zxbparser_standalone import UnexpectedEOF, UnexpectedToken + is_eof = isinstance(e, UnexpectedEOF) or (isinstance(e, UnexpectedToken) and e.token.type == "$END") -def p_statement_draw3(p): - """statement : DRAW expr COMMA expr COMMA expr""" - p[0] = make_sentence( - p.lineno(1), - "DRAW3", - make_typecast(TYPE.integer, p[2], p.lineno(3)), - make_typecast(TYPE.integer, p[4], p.lineno(5)), - make_typecast(TYPE.float_, p[6], p.lineno(5)), - ) - - -def p_statement_draw3_attr(p): - """statement : DRAW attr_list expr COMMA expr COMMA expr""" - p[0] = make_sentence( - p.lineno(1), - "DRAW3", - make_typecast(TYPE.integer, p[3], p.lineno(4)), - make_typecast(TYPE.integer, p[5], p.lineno(6)), - make_typecast(TYPE.float_, p[7], p.lineno(6)), - p[2], - ) - - -def p_statement_draw(p): - """statement : DRAW expr COMMA expr""" - p[0] = make_sentence( - p.lineno(1), - "DRAW", - make_typecast(TYPE.integer, p[2], p.lineno(3)), - make_typecast(TYPE.integer, p[4], p.lineno(3)), - ) - - -def p_statement_draw_attr(p): - """statement : DRAW attr_list expr COMMA expr""" - p[0] = make_sentence( - p.lineno(1), - "DRAW", - make_typecast(TYPE.integer, p[3], p.lineno(4)), - make_typecast(TYPE.integer, p[5], p.lineno(4)), - p[2], - ) - - -def p_statement_circle(p): - """statement : CIRCLE expr COMMA expr COMMA expr""" - p[0] = make_sentence( - p.lineno(1), - "CIRCLE", - make_typecast(TYPE.byte_, p[2], p.lineno(3)), - make_typecast(TYPE.byte_, p[4], p.lineno(5)), - make_typecast(TYPE.byte_, p[6], p.lineno(5)), - ) - - -def p_statement_circle_attr(p): - """statement : CIRCLE attr_list expr COMMA expr COMMA expr""" - p[0] = make_sentence( - p.lineno(1), - "CIRCLE", - make_typecast(TYPE.byte_, p[3], p.lineno(4)), - make_typecast(TYPE.byte_, p[5], p.lineno(6)), - make_typecast(TYPE.byte_, p[7], p.lineno(6)), - p[2], - ) - - -def p_statement_cls(p): - """statement : CLS""" - p[0] = make_sentence(p.lineno(1), "CLS") - - -def p_statement_asm(p): - """statement : ASM""" - p[0] = make_asm_sentence(p[1], p.lineno(1)) - - -def p_statement_randomize(p): - """statement : RANDOMIZE""" - p[0] = make_sentence(p.lineno(1), "RANDOMIZE", make_number(0, lineno=p.lineno(1), type_=TYPE.ulong)) - - -def p_statement_randomize_expr(p): - """statement : RANDOMIZE expr""" - p[0] = make_sentence(p.lineno(1), "RANDOMIZE", make_typecast(TYPE.ulong, p[2], p.lineno(1))) - - -def p_statement_beep(p): - """statement : BEEP expr COMMA expr""" - p[0] = make_sentence( - p.lineno(1), - "BEEP", - make_typecast(TYPE.float_, p[2], p.lineno(1)), - make_typecast(TYPE.float_, p[4], p.lineno(3)), - ) - - -def p_statement_call(p): - """statement : ID arg_list - | ID arguments - | ID - """ - if len(p) > 2 and p[2] is None: - p[0] = None - elif len(p) == 2: - entry = SYMBOL_TABLE.get_entry(p[1]) - if entry is not None and entry.class_ in (CLASS.label, CLASS.unknown): - p[0] = make_label(p[1], p.lineno(1)) + if not is_eof: + # UnexpectedToken, not EOF + tok = e.token + t_type = tok.type + t_val = tok.value + if t_type.startswith("END_") and "_" in t_type: + t_type = "END" + t_val = t_val.split()[0] + if t_type == "NEWLINE": + msg = "Unexpected end of line" else: - p[0] = make_sub_call(p[1], p.lineno(1), make_arg_list(None)) - else: - p[0] = make_sub_call(p[1], p.lineno(1), p[2]) - - -def p_assignment(p): - """statement : lexpr expr""" - global LET_ASSIGNMENT - - LET_ASSIGNMENT = False # Mark we're no longer using LET - p[0] = None - q = p[1:] - i = 1 + msg = "Syntax Error. Unexpected token '%s' <%s>" % (t_val, t_type) + if hasattr(gl, "tokens_rejected"): + gl.tokens_rejected += 1 + yielded = getattr(gl, "tokens_yielded", 0) + rejected = getattr(gl, "tokens_rejected", 0) + shifted = yielded - rejected + shifted_at_last = getattr(gl, "shifted_at_last_error", 0) + if shifted - shifted_at_last < 3: + return True + gl.shifted_at_last_error = shifted + key = (getattr(gl, "FILENAME", ""), tok.line) + last_key = getattr(handle_parse_error, "last_key", None) + if key == last_key: + return True + handle_parse_error.last_key = key + gl.syntax_error_occurred = True + error(tok.line, msg) + return True - if q[1] is None: - return + # EOF cases + if gl.LOOPS: # some loop(s) are not closed + loop_info = gl.LOOPS[-1] + if loop_info.type == LoopType.FOR: + errmsg.syntax_error_for_without_next(loop_info.lineno) + else: + errmsg.syntax_error_loop_not_closed(loop_info.lineno, loop_info.type.value) - if q[1].token == "VAR" and q[1].class_ == CLASS.unknown: - q[1] = SYMBOL_TABLE.access_var(q[1].name, p.lineno(i)) - - q1class_ = q[1].class_ if q[1].token == "VAR" else CLASS.unknown - variable = SYMBOL_TABLE.access_id(q[0], p.lineno(i), default_type=q[1].type_, default_class=q1class_) - if variable is None: - return # HINT: This only happens if variable was not declared with DIM and --strict flag is in use - - if variable.class_ == CLASS.unknown: # The variable is implicit - variable = variable.to_var() - - if variable.class_ not in (CLASS.var, CLASS.array): - errmsg.syntax_error_cannot_assign_not_a_var(p.lineno(i), variable.name) - return - - if variable.class_ == CLASS.var and q1class_ == CLASS.array: - error(p.lineno(i), "Cannot assign an array to an scalar variable") - return - - expr = make_typecast(variable.type_, q[1], p.lineno(i)) - p[0] = make_sentence(p.lineno(1), "LET", variable, expr) - - -def p_lexpr(p): - """lexpr : ID EQ - | LET ID EQ - """ - global LET_ASSIGNMENT - - LET_ASSIGNMENT = True # Mark we're about to start a LET sentence - - if p[1] == "LET": - p[0] = p[2] - i = 2 - else: - p[0] = p[1] - i = 1 - - SYMBOL_TABLE.access_id(p[i], p.lineno(i)) - - -def p_array_copy(p): - """statement : ARRAY_ID EQ ARRAY_ID - | LET ARRAY_ID EQ ARRAY_ID - """ - if p[1] == "LET": - array_id1, array_id2 = p[2], p[4] - l1, l2 = p.lineno(2), p.lineno(4) - else: - array_id1, array_id2 = p[1], p[3] - l1, l2 = p.lineno(1), p.lineno(3) - - larray = SYMBOL_TABLE.access_id(array_id1, l1) - rarray = SYMBOL_TABLE.access_id(array_id2, l2) - - if larray is None or rarray is None: - p[0] = None - return - - if larray.type_ != rarray.type_: - error(l1, "Arrays must have the same element type") - return - - if larray.ref.memsize != rarray.ref.memsize: - error(l1, "Arrays '%s' and '%s' must have the same size" % (array_id1, array_id2)) - return - - if larray.ref.count != rarray.ref.count: - warning( - l1, - "Arrays '%s' and '%s' don't have the same number of dimensions" % (larray.name, rarray.name), - ) - else: - for b1, b2 in zip(larray.ref.bounds, rarray.ref.bounds): - if b1.count != b2.count: - warning( - l1, - "Arrays '%s' and '%s' don't have the same dimensions" % (array_id1, array_id2), - ) - break - # Array copy - mark_entry_as_accessed(larray) - mark_entry_as_accessed(rarray) - p[0] = make_sentence(p.lineno(1), "ARRAYCOPY", larray, rarray) - - -def p_arr_assignment(p): - """statement : ARRAY_ID arg_list EQ expr - | LET ARRAY_ID arg_list EQ expr - """ - i = 2 if p[1].upper() == "LET" else 1 - id_ = p[i] - arg_list = p[i + 1] - expr = p[i + 3] - - p[0] = None - if arg_list is None or expr is None: - return # There were errors - - entry = SYMBOL_TABLE.access_call(id_, p.lineno(i)) - if entry is None: - return - - if entry.type_ == TYPE.string: - variable = gl.SYMBOL_TABLE.access_array(id_, p.lineno(i)) - if len(variable.ref.bounds) + 1 == len(arg_list): - ss = arg_list.children.pop().value - p[0] = make_array_substr_assign(p.lineno(i), id_, arg_list, (ss, ss), expr) - return - - arr = make_array_access(id_, p.lineno(i), arg_list) - if arr is None: - return - - expr = make_typecast(arr.type_, expr, p.lineno(i)) - if entry is None: - return - - if entry.addr is not None: # has addr? - mark_entry_as_accessed(entry) - - p[0] = make_sentence(p.lineno(1), "LETARRAY", arr, expr) - - -def p_substr_assignment_no_let(p): - """statement : ID LP expr RP EQ expr""" - # This can be only a substr assignment like a$(i + 3) = ".", since arrays - # have ARRAY_ID already - entry = SYMBOL_TABLE.access_call(p[1], p.lineno(1)) - if entry is None: - return - - if entry.class_ == CLASS.unknown: - entry.class_ = CLASS.var - - if p[6].type_ != TYPE.string: - errmsg.syntax_error_expected_string(p.lineno(5), p[6].type_) - - lineno = p.lineno(2) - base = make_number(OPTIONS.string_base, lineno, _TYPE(gl.STR_INDEX_TYPE)) - substr = make_typecast(_TYPE(gl.STR_INDEX_TYPE), p[3], lineno) - p[0] = make_sentence( - p.lineno(1), - "LETSUBSTR", - entry, - make_binary(lineno, "MINUS", substr, base, func=lambda x, y: x - y), - make_binary(lineno, "MINUS", substr, base, func=lambda x, y: x - y), - p[6], - ) - - -def p_substr_assignment(p): - """statement : LET ID arg_list EQ expr""" - if p[3] is None or p[5] is None: - return # There were errors - - p[0] = None - entry = SYMBOL_TABLE.access_call(p[2], p.lineno(2)) - if entry is None: - return - - if entry.class_ == CLASS.unknown: - entry = entry.to_var() - - if entry.class_ != CLASS.var: - errmsg.syntax_error_cannot_assign_not_a_var(p.lineno(2), p[2]) - return - - if entry.type_ != TYPE.string: - errmsg.syntax_error_expected_string(p.lineno(2), entry.type_) - return - - if p[5].type_ != TYPE.string: - errmsg.syntax_error_expected_string(p.lineno(4), p[5].type_) - return - - if len(p[3]) > 1: - error(p.lineno(2), "Accessing string with too many indexes. Expected only one.") - return - - if len(p[3]) == 1: - substr = ( - make_typecast(_TYPE(gl.STR_INDEX_TYPE), p[3][0].value, p.lineno(2)), - make_typecast(_TYPE(gl.STR_INDEX_TYPE), p[3][0].value, p.lineno(2)), - ) - else: - substr = ( - make_typecast( - _TYPE(gl.STR_INDEX_TYPE), - make_number(gl.MIN_STRSLICE_IDX, lineno=p.lineno(2)), - p.lineno(2), - ), - make_typecast( - _TYPE(gl.STR_INDEX_TYPE), - make_number(gl.MAX_STRSLICE_IDX, lineno=p.lineno(2)), - p.lineno(2), - ), - ) - - lineno = p.lineno(2) - base = make_number(OPTIONS.string_base, lineno, _TYPE(gl.STR_INDEX_TYPE)) - p[0] = make_sentence( - p.lineno(1), - "LETSUBSTR", - entry, - make_binary(lineno, "MINUS", substr[0], base, func=lambda x, y: x - y), - make_binary(lineno, "MINUS", substr[1], base, func=lambda x, y: x - y), - p[5], - ) - - -def p_str_assign(p): - """statement : ID substr EQ expr - | LET ID substr EQ expr - """ - if p[1].upper() != "LET": - q = p[1] - r = p[4] - s = p[2] - lineno = p.lineno(3) - else: - q = p[2] - r = p[5] - s = p[3] - lineno = p.lineno(4) - - if q is None or s is None: - p[0] = None - return - - if r.type_ != TYPE.string: - errmsg.syntax_error_expected_string(lineno, r.type_) - - entry = SYMBOL_TABLE.access_var(q, lineno, default_type=TYPE.string) - if entry is None: - p[0] = None - return - - p[0] = make_sentence(p.lineno(1), "LETSUBSTR", entry, s[0], s[1], r) - - -def p_goto(p): - """statement : goto NUMBER - | goto ID - """ - entry = check_and_make_label(p[2], p.lineno(2)) - if entry is not None: - p[0] = make_sentence(p.lineno(1), p[1].upper(), entry) - else: - p[0] = None - - -def p_go(p): - """goto : GO TO - | GO SUB - | GOTO - | GOSUB - """ - p[0] = p[1] - if p[0] == "GO": - p[0] += p[2] - - if p[0] == "GOSUB" and FUNCTION_LEVEL: # GOSUB not in global scope? - error(p.lineno(1), "GOSUB not allowed within SUB or FUNCTION") - - -# region [IF sentence] -def p_if_sentence(p): - """statement : if_then_part NEWLINE program_co endif - | if_then_part NEWLINE endif - | if_then_part NEWLINE statements_co endif - | if_then_part NEWLINE co_statements_co endif - | if_then_part NEWLINE label statements_co endif - """ - cond_ = p[1] - if len(p) == 6: - lbl = p[3] - stat_ = make_block(lbl, p[4]) - endif_ = p[5] - elif len(p) == 5: - stat_ = p[3] - endif_ = p[4] - else: - stat_ = make_nop() - endif_ = p[3] - - p[0] = make_sentence(p.lineno(2), "IF", cond_, make_block(stat_, endif_)) - - -def p_endif(p): - """endif : END IF - | label END IF - | ENDIF - | label ENDIF - """ - p[0] = make_nop() if p[1] in ("END", "ENDIF") else p[1] - - -def p_statement_if(p): - """statement : if_inline %prec RP""" - p[0] = p[1] - - -def p_statement_if_then_endif(p): - """statement : if_then_part statements_co endif - | if_then_part co_statements_co endif - """ - cond_ = p[1] - stat_ = p[2] - endif_ = p[3] - p[0] = make_sentence(p.lineno(1), "IF", cond_, make_block(stat_, endif_)) - - -def p_single_line_if(p): - """if_inline : if_then_part statements %prec ID - | if_then_part co_statements_co %prec NEWLINE - | if_then_part statements_co %prec NEWLINE - | if_then_part co_statements %prec ID - """ - cond_ = p[1] - stat_ = p[2] - p[0] = make_sentence(p.lineno(1), "IF", cond_, stat_) - - -def p_if_elseif(p): - """statement : if_then_part NEWLINE program_co elseiflist - | if_then_part NEWLINE elseiflist - """ - cond_ = p[1] - stats_ = p[3] if len(p) == 5 else make_nop() - eliflist = p[4] if len(p) == 5 else p[3] - p[0] = make_sentence(p.lineno(2), "IF", cond_, stats_, eliflist) - - -def p_elseif_part(p): - """elseif_expr : ELSEIF expr then - | label ELSEIF expr then - """ - if p[1] == "ELSEIF": - label_ = make_nop() # No label - cond_ = p[2] - else: - label_ = p[1] - cond_ = p[3] - - p[0] = label_, cond_ - - -def p_elseif_list(p): - """elseiflist : elseif_expr program_co endif - | elseif_expr program_co else_part - """ - label_, cond_ = p[1] - then_ = p[2] - else_ = p[3] - - if isinstance(else_, list): # it's an else part - else_ = make_block(*else_) - else: - then_ = make_block(then_, else_) - else_ = None - - p[0] = make_block(label_, make_sentence(p.lineno(1), "IF", cond_, then_, else_)) - - -def p_elseif_elseiflist(p): - """elseiflist : elseif_expr program_co elseiflist""" - label_, cond_ = p[1] - then_ = p[2] - else_ = p[3] - p[0] = make_block(label_, make_sentence(p.lineno(1), "IF", cond_, then_, else_)) - - -def p_else_part_endif(p): - """else_part_inline : ELSE NEWLINE program_co endif - | ELSE NEWLINE statements_co endif - | ELSE NEWLINE co_statements_co endif - | ELSE NEWLINE endif - | ELSE NEWLINE label statements_co endif - | ELSE NEWLINE label co_statements_co endif - | ELSE statements_co endif - | ELSE co_statements_co endif - """ - if p[2] == "\n": - if len(p) == 4: - p[0] = [make_nop(), p[3]] - elif len(p) == 6: - p[0] = [p[3], p[4], p[5]] - else: - p[0] = [p[3], p[4]] - else: - p[0] = [p[2], p[3]] - - -def p_else_part(p): - """else_part_inline : ELSE statements_co - | ELSE co_statements_co - | ELSE co_statements - | ELSE statements - """ - p[0] = [p[2], make_nop()] - - -def p_else_part_is_inline(p): - """else_part : else_part_inline""" - p[0] = p[1] - - -def p_else_part_label(p): - """else_part : label ELSE program_co endif - | label ELSE statements_co endif - | label ELSE co_statements_co endif - """ - lbl = p[1] - p[0] = [make_block(lbl, p[3]), p[4]] - - -def p_if_then_part(p): - """if_then_part : IF expr then""" - expr = p[2] - if expr is None: - p[0] = None - return - - if is_number(expr): - errmsg.warning_condition_is_always(p.lineno(1), bool(expr.value)) - - p[0] = expr - - -def p_if_inline(p): - """statement : if_inline else_part_inline""" - p[1].append_child(make_block(p[2][0], p[2][1])) - p[0] = p[1] - - -def p_if_else(p): - """statement : if_then_part NEWLINE program_co else_part""" - cond_ = p[1] - then_ = p[3] - else_ = p[4][0] - endif = p[4][1] - p[0] = make_sentence(p.lineno(2), "IF", cond_, then_, make_block(else_, endif)) - - -def p_then(p): - """then : - | THEN - """ - - -# endregion - - -# region [FOR sentence] -def p_for_sentence(p): - """statement : for_start program_co label_next - | for_start co_statements_co label_next - | for_start program label_next - """ - p[0] = p[1] - if is_null(p[0]): - return - p[1].append_child(make_block(p[2], p[3])) - gl.LOOPS.pop() - - -def p_next(p): - """label_next : label NEXT - | NEXT - """ - p[0] = make_nop() if p[1] == "NEXT" else p[1] - - -def p_next1(p): - """label_next : label NEXT ID - | NEXT ID - """ - if p[1] == "NEXT": - p1 = make_nop() - p3 = p[2] - else: - p1 = p[1] - p3 = p[3] - - if p3 != gl.LOOPS[-1].var: - errmsg.syntax_error_wrong_for_var(p.lineno(2), gl.LOOPS[-1].var, p3) - p[0] = make_nop() - return - - p[0] = p1 - - -def p_for_sentence_start(p): - """for_start : FOR ID EQ expr TO expr step""" - gl.LOOPS.append(LoopInfo(type=LoopType.FOR, lineno=p.lineno(1), var=p[2])) - p[0] = None - - if p[4] is None or p[6] is None or p[7] is None: - return - - if is_number(p[4], p[6], p[7]): - if p[4].value != p[6].value and p[7].value == 0: - warning(p.lineno(5), "STEP value is 0 and FOR might loop forever") - - if p[4].value > p[6].value and p[7].value > 0: - warning( - p.lineno(5), - "FOR start value is greater than end. This FOR loop is useless", - ) - - if p[4].value < p[6].value and p[7].value < 0: - warning( - p.lineno(2), - "FOR start value is lower than end. This FOR loop is useless", - ) - - id_type = common_type(common_type(p[4], p[6]), p[7]) - variable = SYMBOL_TABLE.access_var(p[2], p.lineno(2), default_type=id_type) - if variable is None: - return - - mark_entry_as_accessed(variable) - expr1 = make_typecast(variable.type_, p[4], p.lineno(3)) - expr2 = make_typecast(variable.type_, p[6], p.lineno(5)) - expr3 = make_typecast(variable.type_, p[7], p.lexer.lineno) - - p[0] = make_sentence(p.lineno(1), "FOR", variable, expr1, expr2, expr3) - - -def p_step(p): - """step :""" - p[0] = make_number(1, lineno=p.lexer.lineno) - - -def p_step_expr(p): - """step : STEP expr""" - p[0] = p[2] - - -# endregion - - -def p_end(p): - """statement : END expr - | END - """ - q = make_number(0, lineno=p.lineno(1)) if len(p) == 2 else p[2] - p[0] = make_sentence(p.lineno(1), "END", q) - - -def p_error_raise(p): - """statement : ERROR expr""" - q = make_number(1, lineno=p.lineno(2)) - r = make_binary( - p.lineno(1), - "MINUS", - make_typecast(TYPE.ubyte, p[2], p.lineno(1)), - q, - lambda x, y: x - y, - ) - p[0] = make_sentence(p.lineno(1), "ERROR", r) - - -def p_stop_raise(p): - """statement : STOP expr - | STOP - """ - q = make_number(9, lineno=p.lineno(1)) if len(p) == 2 else p[2] - z = make_number(1, lineno=p.lineno(1)) - r = make_binary( - p.lineno(1), - "MINUS", - make_typecast(TYPE.ubyte, q, p.lineno(1)), - z, - lambda x, y: x - y, - ) - p[0] = make_sentence(p.lineno(1), "STOP", r) - - -def p_loop(p): - """label_loop : label LOOP - | LOOP - """ - if p[1] == "LOOP": - p[0] = None - else: - p[0] = p[1] - - -def p_do_loop(p): - """statement : do_start program_co label_loop - | do_start label_loop - | DO label_loop - """ - if len(p) == 4: - q = make_block(p[2], p[3]) - else: - q = p[2] - - if p[1] == "DO": - gl.LOOPS.append(LoopInfo(LoopType.DO, p.lineno(1))) - - if q is None: - warning(p.lineno(1), "Infinite empty loop") - - # An infinite loop and no warnings - p[0] = make_sentence(p.lineno(1), "DO_LOOP", q) - gl.LOOPS.pop() - - -def p_do_loop_until(p): - """statement : do_start program_co label_loop UNTIL expr - | do_start label_loop UNTIL expr - | DO label_loop UNTIL expr - """ - if len(p) == 6: - q = make_block(p[2], p[3]) - r = p[5] - else: - q = p[2] - r = p[4] - - if p[1] == "DO": - gl.LOOPS.append(LoopInfo(LoopType.DO, p.lineno(1))) - - p[0] = make_sentence(p.lineno(1), "DO_UNTIL", r, q) - - gl.LOOPS.pop() - if is_number(r): - errmsg.warning_condition_is_always(p.lineno(3), bool(r.value)) - if q is None: - errmsg.warning_empty_loop(p.lineno(3)) - - -def p_data(p): - """statement : DATA arguments""" - label_ = make_label(gl.DATA_PTR_CURRENT, lineno=p.lineno(1)) - datas_ = [] - funcs = [] - - if p[2] is None: - p[0] = None - return - - if gl.FUNCTION_LEVEL: - errmsg.error(p.lineno(1), "DATA not allowed within Functions nor Subs") - p[0] = None - return - - for d in p[2].children: - value = d.value - if is_static(value): - datas_.append(d) - continue - - new_lbl = f"__DATA__FUNCPTR__{len(gl.DATA_FUNCTIONS)}" - entry = make_func_declaration(new_lbl, p.lineno(1), type_=value.type_, class_=CLASS.function) - if not entry: - continue - - func = entry.entry - func.ref.convention = CONVENTION.fastcall - SYMBOL_TABLE.enter_scope(new_lbl) - func.ref.local_symbol_table = SYMBOL_TABLE.current_scope - func.ref.locals_size = SYMBOL_TABLE.leave_scope() - - gl.DATA_FUNCTIONS.append(func) - sent = make_sentence(p.lineno(1), "RETURN", func, value) - func.ref.body = make_block(sent) - datas_.append(entry) - funcs.append(entry) - - gl.DATAS.append(src.api.dataref.DataRef(label_, datas_)) - id_ = src.api.utils.current_data_label() - gl.DATA_PTR_CURRENT = id_ - - -def p_restore(p): - """statement : RESTORE - | RESTORE ID - | RESTORE NUMBER - """ - if len(p) == 2: - lbl = None - else: - lbl = check_and_make_label(p[2], p.lineno(1)) - - p[0] = make_sentence(p.lineno(1), "RESTORE", lbl) - - -def p_read(p): - """statement : READ arguments""" - gl.DATA_IS_USED = True - reads = [] - - if p[2] is None: - return - - for arg in p[2]: - entry = arg.value - if entry is None: - p[0] = None - return - - if entry.token == "VARARRAY": - errmsg.error(p.lineno(1), "Cannot read '%s'. It's an array" % entry.name) - p[0] = None - return - - if isinstance(entry, sym.ID): - if entry.class_ != CLASS.var: - errmsg.syntax_error_cannot_assign_not_a_var(p.lineno(2), entry.name) - p[0] = None - return - - mark_entry_as_accessed(entry) - if entry.type_ == TYPE.auto: - entry.type_ = _TYPE(gl.DEFAULT_TYPE) - errmsg.warning_implicit_type(p.lineno(2), p[2], entry.type_) - - reads.append(make_sentence(p.lineno(1), "READ", entry)) - continue - - if isinstance(entry, sym.ARRAYLOAD): - reads.append( - make_sentence( - p.lineno(1), - "READ", - sym.ARRAYACCESS(entry.entry, entry.args, entry.lineno, gl.FILENAME), - ) - ) - continue - - errmsg.error(p.lineno(1), "Syntax error. Can only read a variable or an array element") - p[0] = None - return - - p[0] = make_block(*reads) - - -def p_do_loop_while(p): - """statement : do_start program_co label_loop WHILE expr - | do_start label_loop WHILE expr - | DO label_loop WHILE expr - """ - if len(p) == 6: - q = make_block(p[2], p[3]) - r = p[5] - else: - q = p[2] - r = p[4] - - if p[1] == "DO": - gl.LOOPS.append(LoopInfo(LoopType.DO, p.lineno(1))) - - p[0] = make_sentence(p.lineno(1), "DO_WHILE", r, q) - gl.LOOPS.pop() - - if is_number(r): - errmsg.warning_condition_is_always(p.lineno(3), bool(r.value)) - if q is None: - errmsg.warning_empty_loop(p.lineno(3)) - - -def p_do_while_loop(p): - """statement : do_while_start program_co LOOP - | do_while_start co_statements_co LOOP - | do_while_start LOOP - """ - r = p[1] - q = p[2] - if q == "LOOP": - q = None - - p[0] = make_sentence(p.lineno(1), "WHILE_DO", r, q) - gl.LOOPS.pop() - - if is_number(r): - errmsg.warning_condition_is_always(p.lineno(2), bool(r.value)) - - -def p_do_until_loop(p): - """statement : do_until_start program_co LOOP - | do_until_start co_statements_co LOOP - | do_until_start LOOP - """ - r = p[1] - q = p[2] - if q == "LOOP": - q = None - - p[0] = make_sentence(p.lineno(2), "UNTIL_DO", r, q) - gl.LOOPS.pop() - - if is_number(r): - errmsg.warning_condition_is_always(p.lineno(2), bool(r.value)) - - -def p_do_while_start(p): - """do_while_start : DO WHILE expr""" - p[0] = p[3] - gl.LOOPS.append(LoopInfo(LoopType.DO, p.lineno(1))) - - -def p_do_until_start(p): - """do_until_start : DO UNTIL expr""" - p[0] = p[3] - gl.LOOPS.append(LoopInfo(LoopType.DO, p.lineno(1))) - - -def p_do_start(p): - """do_start : DO CO - | DO NEWLINE - """ - gl.LOOPS.append(LoopInfo(LoopType.DO, p.lineno(1))) - - -def p_label_end_while(p): - """label_end_while : label WEND - | label END WHILE - | WEND - | END WHILE - """ - if p[1] in ("WEND", "END"): - p[0] = None - else: - p[0] = p[1] - - -def p_while_sentence(p): - """statement : while_start co_statements_co label_end_while - | while_start program_co label_end_while - """ - gl.LOOPS.pop() - q = make_block(p[2], p[3]) - - if is_number(p[1]): - errmsg.warning_condition_is_always(p.lineno(1), bool(p[1].value)) - - p[0] = make_sentence(p.lineno(1), "WHILE", p[1], q) - - -def p_while_start(p): - """while_start : WHILE expr""" - p[0] = p[2] - gl.LOOPS.append(LoopInfo(LoopType.WHILE, p.lineno(1))) - if is_number(p[2]) and not p[2].value: - errmsg.warning_condition_is_always(p.lineno(1)) - - -def p_exit(p): - """statement : EXIT WHILE - | EXIT DO - | EXIT FOR - """ - q = p[2] - p[0] = make_sentence(p.lineno(1), "EXIT_%s" % q) - - for loop in gl.LOOPS: - if q == loop.type: - return - - error(p.lineno(1), "Syntax Error: EXIT %s out of loop" % q) - - -def p_continue(p): - """statement : CONTINUE WHILE - | CONTINUE DO - | CONTINUE FOR - """ - q = p[2] - p[0] = make_sentence(p.lineno(1), "CONTINUE_%s" % q) - - for i in gl.LOOPS: - if q == i[0]: - return - - error(p.lineno(1), "Syntax Error: CONTINUE %s out of loop" % q) - - -def p_print_sentence(p): - """statement : PRINT print_list""" - global PRINT_IS_USED - - p[0] = p[2] - PRINT_IS_USED = True - - -def p_print_elem_expr(p): - """print_elem : expr""" - p[0] = p[1] - if p[1] is not None and p[1].type_ == TYPE.boolean: - p[0] = make_typecast(TYPE.ubyte, p[1], p.lineno(1)) - - -def p_print_list_expr(p): - """print_elem : print_at - | print_tab - | attr - | BOLD expr - | ITALIC expr - """ - if p[1] in ("BOLD", "ITALIC"): - p[0] = make_sentence(p.lineno(1), p[1] + "_TMP", make_typecast(TYPE.ubyte, p[2], p.lineno(1))) - else: - p[0] = p[1] - - -def p_attr_list(p): - """attr_list : attr SC""" - p[0] = p[1] - - -def p_attr_list_list(p): - """attr_list : attr_list attr SC""" - p[0] = make_block(p[1], p[2]) - - -def p_attr(p): - """attr : OVER expr - | INVERSE expr - | INK expr - | PAPER expr - | BRIGHT expr - | FLASH expr - """ - # ATTR_LIST are used by drawing commands: PLOT, DRAW, CIRCLE - # BOLD and ITALIC are ignored by them, so we put them out of the - # attr definition so something like DRAW BOLD 1; .... will raise - # a syntax error - p[0] = make_sentence(p.lineno(1), p[1] + "_TMP", make_typecast(TYPE.ubyte, p[2], p.lineno(1))) - - -def p_print_list_epsilon(p): - """print_elem :""" - p[0] = None - - -def p_print_list_elem(p): - """print_list : print_elem""" - p[0] = make_sentence(p.lexer.lineno, "PRINT", p[1]) - p[0].eol = True - - -def p_print_list(p): - """print_list : print_list SC print_elem""" - p[0] = p[1] - p[0].eol = p[3] is not None - - if p[3] is not None: - p[0].append_child(p[3]) - - -def p_print_list_comma(p): - """print_list : print_list COMMA print_elem""" - p[0] = p[1] - p[0].eol = p[3] is not None - p[0].append_child(make_sentence(p.lineno(2), "PRINT_COMMA")) - - if p[3] is not None: - p[0].append_child(p[3]) - - -def p_print_list_at(p): - """print_at : AT expr COMMA expr""" - p[0] = make_sentence( - p.lineno(1), - "PRINT_AT", - make_typecast(TYPE.ubyte, p[2], p.lineno(1)), - make_typecast(TYPE.ubyte, p[4], p.lineno(3)), - ) - - -def p_print_list_tab(p): - """print_tab : TAB expr""" - p[0] = make_sentence(p.lineno(1), "PRINT_TAB", make_typecast(TYPE.ubyte, p[2], p.lineno(1))) - - -def p_on_goto(p): - """statement : ON expr goto label_list""" - expr = make_typecast(TYPE.ubyte, p[2], p.lineno(1)) - p[0] = make_sentence(p.lineno(1), "ON_" + p[3], expr, *p[4]) - - -def p_label_list(p): - """label_list : ID - | NUMBER - """ - entry = check_and_make_label(p[1], p.lineno(1)) - p[0] = [entry] - - -def p_label_list_list(p): - """label_list : label_list COMMA ID - | label_list COMMA NUMBER - """ - p[0] = p[1] - entry = check_and_make_label(p[3], p.lineno(3)) - p[1].append(entry) - - -def p_return(p): - """statement : RETURN""" - if not FUNCTION_LEVEL: # At less one level, otherwise, this return is from a GOSUB - p[0] = make_sentence(p.lineno(1), "RETURN") - return - - if FUNCTION_LEVEL[-1].class_ != CLASS.sub: - error(p.lineno(1), "Syntax Error: Function must RETURN a value.") - p[0] = None - return - - p[0] = make_sentence(p.lineno(1), "RETURN", FUNCTION_LEVEL[-1]) - - -def p_return_expr(p): - """statement : RETURN expr""" - if not FUNCTION_LEVEL: # At less one level - error(p.lineno(1), "Syntax Error: Returning value out of FUNCTION") - p[0] = None - return - - if FUNCTION_LEVEL[-1].class_ is CLASS.unknown: # This function was not correctly declared. - p[0] = None - return - - if FUNCTION_LEVEL[-1].class_ != CLASS.function: - error(p.lineno(1), "Syntax Error: SUBs cannot return a value") - p[0] = None - return - - if FUNCTION_LEVEL[-1].type_ is None: # There was an error in the Function declaration - p[0] = None - return - - if is_numeric(p[2]) and FUNCTION_LEVEL[-1].type_ == TYPE.string: - error( - p.lineno(2), - "Type Error: Function must return a string, not a numeric value", - ) - p[0] = None - return - - if not is_numeric(p[2]) and FUNCTION_LEVEL[-1].type_ != TYPE.string: - error( - p.lineno(2), - "Type Error: Function must return a numeric value, not a string", - ) - p[0] = None - return - - p[0] = make_sentence( - p.lineno(1), - "RETURN", - FUNCTION_LEVEL[-1], - make_typecast(FUNCTION_LEVEL[-1].type_, p[2], p.lineno(1)), - ) - - -def p_pause(p): - """statement : PAUSE expr""" - p[0] = make_sentence(p.lineno(1), "PAUSE", make_typecast(TYPE.uinteger, p[2], p.lineno(1))) - - -def p_poke(p): - """statement : POKE expr COMMA expr - | POKE LP expr COMMA expr RP - """ - i = 2 if isinstance(p[2], Symbol) or p[2] is None else 3 - if p[i] is None or p[i + 2] is None: - p[0] = None - return - p[0] = make_sentence( - p.lineno(1), - "POKE", - make_typecast(TYPE.uinteger, p[i], p.lineno(i + 1)), - make_typecast(TYPE.ubyte, p[i + 2], p.lineno(i + 1)), - ) - - -def p_poke2(p): - """statement : POKE numbertype expr COMMA expr - | POKE LP numbertype expr COMMA expr RP - """ - i = 2 if isinstance(p[2], Symbol) or p[2] is None else 3 - if p[i + 1] is None or p[i + 3] is None: - p[0] = None - return - p[0] = make_sentence( - p.lineno(1), - "POKE", - make_typecast(TYPE.uinteger, p[i + 1], p.lineno(i + 2)), - make_typecast(p[i], p[i + 3], p.lineno(i + 3)), - ) - - -def p_poke3(p): - """statement : POKE numbertype COMMA expr COMMA expr - | POKE LP numbertype COMMA expr COMMA expr RP - """ - i = 2 if isinstance(p[2], Symbol) or p[2] is None else 3 - if p[i + 2] is None or p[i + 4] is None: - p[0] = None - return - p[0] = make_sentence( - p.lineno(1), - "POKE", - make_typecast(TYPE.uinteger, p[i + 2], p.lineno(i + 3)), - make_typecast(p[i], p[i + 4], p.lineno(i + 5)), - ) - - -def p_out(p): - """statement : OUT expr COMMA expr""" - p[0] = make_sentence( - p.lineno(1), - "OUT", - make_typecast(TYPE.uinteger, p[2], p.lineno(3)), - make_typecast(TYPE.ubyte, p[4], p.lineno(4)), - ) - - -def p_simple_instruction(p): - """statement : ITALIC expr - | BOLD expr - | INK expr - | PAPER expr - | BRIGHT expr - | FLASH expr - | OVER expr - | INVERSE expr - """ - p[0] = make_sentence(p.lineno(1), p[1], make_typecast(TYPE.ubyte, p[2], p.lineno(1))) - - -def p_save_code(p): - """statement : SAVE expr CODE expr COMMA expr - | SAVE expr ID - | SAVE expr ARRAY_ID - """ - expr = p[2] - if expr.type_ != TYPE.string: - errmsg.syntax_error_expected_string(p.lineno(1), expr.type_) - - if len(p) == 4: - if p[3].upper() not in ("SCREEN", "SCREEN$"): - error(p.lineno(3), 'Unexpected "%s" ID. Expected "SCREEN$" instead' % p[3]) - return None - # ZX Spectrum screen start + length - # This should be stored in a architecture-dependant file - start = make_number(16384, lineno=p.lineno(1)) - length = make_number(6912, lineno=p.lineno(1)) - else: - start = make_typecast(TYPE.uinteger, p[4], p.lineno(4)) - length = make_typecast(TYPE.uinteger, p[6], p.lineno(6)) - - p[0] = make_sentence(p.lineno(1), p[1], expr, start, length) - - -def p_save_data(p): - """statement : SAVE expr DATA - | SAVE expr DATA ID - | SAVE expr DATA ID LP RP - """ - if p[2].type_ != TYPE.string: - errmsg.syntax_error_expected_string(p.lineno(1), p[2].type_) - - if len(p) != 4: - entry = SYMBOL_TABLE.access_id(p[4], p.lineno(4)) - if entry is None: - p[0] = None - return - - mark_entry_as_accessed(entry) - access = entry - start = make_unary(p.lineno(4), "ADDRESS", access, type_=TYPE.uinteger) - - if entry.class_ == CLASS.array: - length = make_number(entry.memsize, lineno=p.lineno(4)) - else: - length = make_number(entry.type_.size, lineno=p.lineno(4)) - else: - access = SYMBOL_TABLE.access_label(gl.ZXBASIC_USER_DATA, p.lineno(3), SYMBOL_TABLE.global_scope) - start = make_unary(p.lineno(3), "ADDRESS", access, type_=TYPE.uinteger) - - access = SYMBOL_TABLE.access_label(gl.ZXBASIC_USER_DATA_LEN, p.lineno(3), SYMBOL_TABLE.global_scope) - length = make_unary(p.lineno(3), "ADDRESS", access, type_=TYPE.uinteger) - - p[0] = make_sentence(p.lineno(1), p[1], p[2], start, length) - - -def p_load_or_verify(p): - """load_or_verify : LOAD - | VERIFY - """ - p[0] = p[1] - - -def p_load_code(p): - """statement : load_or_verify expr ID - | load_or_verify expr CODE - | load_or_verify expr CODE expr - | load_or_verify expr CODE expr COMMA expr - """ - if p[2].type_ != TYPE.string: - errmsg.syntax_error_expected_string(p.lineno(3), p[2].type_) - - if len(p) == 4: - if p[3].upper() not in ("SCREEN", "SCREEN$", "CODE"): - error(p.lineno(3), 'Unexpected "%s" ID. Expected "SCREEN$" instead' % p[3]) - return None - if p[3].upper() == "CODE": # LOAD "..." CODE - start = make_number(0, lineno=p.lineno(3)) - length = make_number(0, lineno=p.lineno(3)) - else: # SCREEN$ - start = make_number(16384, lineno=p.lineno(3)) - length = make_number(6912, lineno=p.lineno(3)) - else: - start = make_typecast(TYPE.uinteger, p[4], p.lineno(3)) - - if len(p) == 5: - length = make_number(0, lineno=p.lineno(3)) - else: - length = make_typecast(TYPE.uinteger, p[6], p.lineno(5)) - - p[0] = make_sentence(p.lineno(3), p[1], p[2], start, length) - - -def p_load_data(p): - """statement : load_or_verify expr DATA - | load_or_verify expr DATA ID - | load_or_verify expr DATA ID LP RP - """ - if p[2].type_ != TYPE.string: - errmsg.syntax_error_expected_string(p.lineno(1), p[2].type_) - - if len(p) != 4: - entry = SYMBOL_TABLE.access_id(p[4], p.lineno(4)) - if entry is None: - p[0] = None - return - - mark_entry_as_accessed(entry) - start = make_unary(p.lineno(4), "ADDRESS", entry, type_=TYPE.uinteger) - - if entry.class_ == CLASS.array: - length = make_number(entry.memsize, lineno=p.lineno(4)) - else: - length = make_number(entry.type_.size, lineno=p.lineno(4)) - else: - entry = SYMBOL_TABLE.access_label(gl.ZXBASIC_USER_DATA, p.lineno(3), SYMBOL_TABLE.global_scope) - start = make_unary(p.lineno(3), "ADDRESS", entry, type_=TYPE.uinteger) - - entry = SYMBOL_TABLE.access_label(gl.ZXBASIC_USER_DATA_LEN, p.lineno(3), SYMBOL_TABLE.global_scope) - length = make_unary(p.lineno(3), "ADDRESS", entry, type_=TYPE.uinteger) - - p[0] = make_sentence(p.lineno(3), p[1], p[2], start, length) - - -def p_numbertype(p): - """numbertype : BYTE - | UBYTE - | INTEGER - | UINTEGER - | LONG - | ULONG - | FIXED - | FLOAT - """ - p[0] = make_type(p[1].lower(), p.lineno(1)) - - -def p_expr_plus_expr(p): - """expr : expr PLUS expr""" - p[0] = make_binary(p.lineno(2), "PLUS", p[1], p[3], lambda x, y: x + y) - - -def p_expr_minus_expr(p): - """expr : expr MINUS expr""" - p[0] = make_binary(p.lineno(2), "MINUS", p[1], p[3], lambda x, y: x - y) - - -def p_expr_mul_expr(p): - """expr : expr MUL expr""" - p[0] = make_binary(p.lineno(2), "MUL", p[1], p[3], lambda x, y: x * y) - - -def p_expr_div_expr(p): - """expr : expr DIV expr""" - p[0] = make_binary(p.lineno(2), "DIV", p[1], p[3], lambda x, y: x / y) - - -def p_expr_mod_expr(p): - """expr : expr MOD expr""" - p[0] = make_binary(p.lineno(2), "MOD", p[1], p[3], lambda x, y: x % y) - - -def p_expr_pow_expr(p): - """expr : expr POW expr""" - p[0] = make_binary( - p.lineno(2), - "POW", - make_typecast(TYPE.float_, p[1], p.lineno(2)), - make_typecast(TYPE.float_, p[3], p.lexer.lineno), - lambda x, y: x**y, - ) - - -def p_expr_shl_expr(p): - """expr : expr SHL expr""" - if p[1] is None or p[3] is None: - p[0] = None - return - - if p[1].type_ in (TYPE.float_, TYPE.fixed): - p[1] = make_typecast(TYPE.ulong, p[1], p.lineno(2)) - - p[0] = make_binary( - p.lineno(2), - "SHL", - p[1], - make_typecast(TYPE.ubyte, p[3], p.lineno(2)), - lambda x, y: x << y, - ) - - -def p_expr_shr_expr(p): - """expr : expr SHR expr""" - if p[1] is None or p[3] is None: - p[0] = None - return - - if p[1].type_ in (TYPE.float_, TYPE.fixed): - p[1] = make_typecast(TYPE.ulong, p[1], p.lineno(2)) - - p[0] = make_binary( - p.lineno(2), - "SHR", - p[1], - make_typecast(TYPE.ubyte, p[3], p.lineno(2)), - lambda x, y: x >> y, - ) - - -def p_minus_expr(p): - """expr : MINUS expr %prec UMINUS""" - p[0] = make_unary(p.lineno(1), "MINUS", p[2], lambda x: -x) - - -def p_expr_EQ_expr(p): - """expr : expr EQ expr""" - p[0] = make_binary(p.lineno(2), "EQ", p[1], p[3], lambda x, y: x == y) - - -def p_expr_LT_expr(p): - """expr : expr LT expr""" - p[0] = make_binary(p.lineno(2), "LT", p[1], p[3], lambda x, y: x < y) - - -def p_expr_LE_expr(p): - """expr : expr LE expr""" - p[0] = make_binary(p.lineno(2), "LE", p[1], p[3], lambda x, y: x <= y) - - -def p_expr_GT_expr(p): - """expr : expr GT expr""" - p[0] = make_binary(p.lineno(2), "GT", p[1], p[3], lambda x, y: x > y) - - -def p_expr_GE_expr(p): - """expr : expr GE expr""" - p[0] = make_binary(p.lineno(2), "GE", p[1], p[3], lambda x, y: x >= y) - - -def p_expr_NE_expr(p): - """expr : expr NE expr""" - p[0] = make_binary(p.lineno(2), "NE", p[1], p[3], lambda x, y: x != y) - - -def p_expr_OR_expr(p): - """expr : expr OR expr""" - p[0] = make_binary(p.lineno(2), "OR", p[1], p[3], lambda x, y: x or y) - - -def p_expr_BOR_expr(p): - """expr : expr BOR expr""" - p[0] = make_binary(p.lineno(2), "BOR", p[1], p[3], lambda x, y: x | y) - - -def p_expr_XOR_expr(p): - """expr : expr XOR expr""" - p[0] = make_binary(p.lineno(2), "XOR", p[1], p[3], lambda x, y: (x and not y) or (not x and y)) - - -def p_expr_BXOR_expr(p): - """expr : expr BXOR expr""" - p[0] = make_binary(p.lineno(2), "BXOR", p[1], p[3], lambda x, y: x ^ y) - - -def p_expr_AND_expr(p): - """expr : expr AND expr""" - p[0] = make_binary(p.lineno(2), "AND", p[1], p[3], lambda x, y: x and y) - - -def p_expr_BAND_expr(p): - """expr : expr BAND expr""" - p[0] = make_binary(p.lineno(2), "BAND", p[1], p[3], lambda x, y: x & y) - - -def p_NOT_expr(p): - """expr : NOT expr""" - p[0] = make_unary(p.lineno(1), "NOT", p[2], lambda x: not x) - - -def p_BNOT_expr(p): - """expr : BNOT expr""" - p[0] = make_unary(p.lineno(1), "BNOT", p[2], lambda x: ~x) - - -def p_lp_expr_rp(p): - """bexpr : LP expr RP %prec ID""" - p[0] = p[2] - - -def p_cast(p): - """expr : CAST LP numbertype COMMA expr RP""" - p[0] = make_typecast(p[3], p[5], p.lineno(6)) - - -def p_number_expr(p): - """bexpr : NUMBER""" - p[0] = make_number(p[1], lineno=p.lineno(1)) - - -def p_expr_PI(p): - """bexpr : PI""" - p[0] = make_number(PI, lineno=p.lineno(1), type_=TYPE.float_) - - -def p_expr_string(p): - """bexpr : string %prec ID""" - p[0] = p[1] - - -def p_string_func_call(p): - """string : func_call substr""" - p[0] = make_strslice(p.lineno(1), p[1], p[2][0], p[2][1]) - - -def p_string_func_call_single(p): - """string : func_call LP expr RP""" - p[0] = make_strslice(p.lineno(1), p[1], p[3], p[3]) - - -def p_string_str(p): - """string : STRC""" - p[0] = sym.STRING(p[1], p.lineno(1)) - - -def p_string_lprp(p): - """string : string LP RP""" - p[0] = p[1] - - -def p_string_lp_expr_rp(p): - """string : string LP expr RP""" - p[0] = make_strslice(p.lineno(2), p[1], p[3], p[3]) - - -def p_expr_id_substr(p): - """string : ID substr""" - entry = SYMBOL_TABLE.get_entry(p[1]) - if entry is not None and entry.type_ == TYPE.string and entry.token == "CONST": - p[0] = make_strslice(p.lineno(1), entry, p[2][0], p[2][1]) - return - - entry = SYMBOL_TABLE.access_var(p[1], p.lineno(1), default_type=TYPE.string) - p[0] = None - if entry is None: - return - - mark_entry_as_accessed(entry) - p[0] = make_strslice(p.lineno(1), entry, p[2][0], p[2][1]) - - -def p_string_substr(p): - """string : string substr""" - p[0] = make_strslice(p.lineno(1), p[1], p[2][0], p[2][1]) - - -def p_string_expr_lp(p): - """string : LP expr RP substr""" - if p[2].type_ != TYPE.string: - error( - p.lexer.lineno, - "Expected a string type expression. Got %s type instead" % TYPE.to_string(p[2].type_), - ) - p[0] = None - else: - p[0] = make_strslice(p.lexer.lineno, p[2], p[4][0], p[4][1]) - - -def p_subind_str(p): - """substr : LP expr TO expr RP""" - p[0] = ( - make_typecast(TYPE.uinteger, p[2], p.lineno(1)), - make_typecast(TYPE.uinteger, p[4], p.lineno(3)), - ) - - -def p_subind_strTO(p): - """substr : LP TO expr RP""" - p[0] = ( - make_typecast(TYPE.uinteger, make_number(0, lineno=p.lineno(2)), p.lineno(1)), - make_typecast(TYPE.uinteger, p[3], p.lineno(2)), - ) - - -def p_subind_TOstr(p): - """substr : LP expr TO RP""" - p[0] = ( - make_typecast(TYPE.uinteger, p[2], p.lineno(1)), - make_typecast( - TYPE.uinteger, - make_number(gl.MAX_STRSLICE_IDX, lineno=p.lineno(4)), - lineno=p.lineno(4), - ), - p.lineno(3), - ) - - -def p_subind_TO(p): - """substr : LP TO RP""" - p[0] = ( - make_typecast(TYPE.uinteger, make_number(0, lineno=p.lineno(2)), p.lineno(1)), - make_typecast( - TYPE.uinteger, - make_number(gl.MAX_STRSLICE_IDX, lineno=p.lineno(3)), - p.lineno(2), - ), - ) - - -def p_id_expr(p): - """bexpr : ID""" - entry = SYMBOL_TABLE.access_id(p[1], p.lineno(1), default_class=CLASS.var) - if entry is None: - p[0] = None - return - - mark_entry_as_accessed(entry) - if entry.type_ == TYPE.auto: - entry.type_ = _TYPE(gl.DEFAULT_TYPE) - errmsg.warning_implicit_type(p.lineno(1), p[1], entry.type_) - - p[0] = entry - - if entry.class_ == CLASS.array: # HINT: This should never happen now - if not LET_ASSIGNMENT: - error( - p.lineno(1), - "Variable '%s' is an array and cannot be used in this context" % p[1], - ) - p[0] = None - elif entry.class_ == CLASS.function: # Function call with 0 args - p[0] = make_call(p[1], p.lineno(1), make_arg_list(None)) - elif entry.class_ == CLASS.sub: # Forbidden for subs - errmsg.syntax_error_is_a_sub_not_a_func(p.lineno(1), p[1]) - p[0] = None - - -def p_addr_of_id(p): - """bexpr : ADDRESSOF singleid""" - id_: Id = p[2] - # Access id. For @ operator we ignore the explicit flag - entry = SYMBOL_TABLE.access_id(id_.name, id_.lineno, ignore_explicit_flag=True) - if entry is None: - p[0] = None - return - - entry.has_address = True - mark_entry_as_accessed(entry) - result = make_unary(p.lineno(1), "ADDRESS", entry, type_=_TYPE(gl.PTR_TYPE)) - - if is_dynamic(entry): - p[0] = result - else: - p[0] = make_constexpr(p.lineno(1), result) - - -def p_expr_bexpr(p): - """expr : bexpr""" - p[0] = p[1] - - -def p_expr_funccall(p): - """bexpr : func_call %prec ID""" - p[0] = p[1] - - -def p_idcall_expr(p): - """func_call : ID arg_list %prec UMINUS""" # This can be a function call or a string index - if p[2] is None: - p[0] = None - return - - p[0] = make_call(p[1], p.lineno(1), p[2]) - if p[0] is None: - return - - if p[0].token in ("STRSLICE", "ID", "STRING") or p[0].token == "CONST" and p[0].type_ == TYPE.string: - entry = SYMBOL_TABLE.access_call(p[1], p.lineno(1)) - mark_entry_as_accessed(entry) - return - - convert_to_function(p[0].entry, CLASS.function, p.lineno(1)) - mark_entry_as_accessed(p[0].entry) - - -def p_array_eq_error(p): - """statement : LET ARRAY_ID EQ expr""" - error(p.lineno(4), f"Invalid assignment. Variable {p[2]}() is an array") - p[0] = None - - -def p_arr_access_expr(p): - """func_call : ARRAY_ID arg_list""" # This is an array access - p[0] = make_call(p[1], p.lineno(1), p[2]) - if p[0] is None: - return - - entry = SYMBOL_TABLE.access_call(p[1], p.lineno(1)) - mark_entry_as_accessed(entry) - - -def p_let_arr_substr(p): - """statement : LET ARRAY_ID arg_list substr EQ expr - | ARRAY_ID arg_list substr EQ expr - """ - i = 2 if p[1].upper() == "LET" else 1 - - id_ = p[i] - arg_list = p[i + 1] - substr = p[i + 2] - expr_ = p[i + 4] - p[0] = make_array_substr_assign(p.lineno(i), id_, arg_list, substr, expr_) - - -def p_let_arr_substr_single(p): - """statement : LET ARRAY_ID arg_list LP expr RP EQ expr - | ARRAY_ID arg_list LP expr RP EQ expr - """ - i = 2 if p[1].upper() == "LET" else 1 - - id_ = p[i] - arg_list = p[i + 1] - substr = (p[i + 3], p[i + 3]) - expr_ = p[i + 6] - p[0] = make_array_substr_assign(p.lineno(i), id_, arg_list, substr, expr_) - - -def p_let_arr_substr_in_args(p): - """statement : LET ARRAY_ID LP arguments TO RP EQ expr - | ARRAY_ID LP arguments TO RP EQ expr - """ - i = 2 if p[1].upper() == "LET" else 1 - - id_ = p[i] - arg_list = p[i + 2] - substr = ( - arg_list.children.pop().value, - make_number(gl.MAX_STRSLICE_IDX, lineno=p.lineno(i + 3)), - ) - expr_ = p[i + 6] - p[0] = make_array_substr_assign(p.lineno(i), id_, arg_list, substr, expr_) - - -def p_let_arr_substr_in_args2(p): - """statement : LET ARRAY_ID LP arguments COMMA TO expr RP EQ expr - | ARRAY_ID LP arguments COMMA TO expr RP EQ expr - """ - i = 2 if p[1].upper() == "LET" else 1 - - id_ = p[i] - arg_list = p[i + 2] - top_ = p[i + 5] - substr = (make_number(0, lineno=p.lineno(i + 4)), top_) - expr_ = p[i + 8] - p[0] = make_array_substr_assign(p.lineno(i), id_, arg_list, substr, expr_) - - -def p_let_arr_substr_in_args3(p): - """statement : LET ARRAY_ID LP arguments COMMA TO RP EQ expr - | ARRAY_ID LP arguments COMMA TO RP EQ expr - """ - i = 2 if p[1].upper() == "LET" else 1 - - id_ = p[i] - arg_list = p[i + 2] - substr = ( - make_number(0, lineno=p.lineno(i + 4)), - make_number(gl.MAX_STRSLICE_IDX, lineno=p.lineno(i + 3)), - ) - expr_ = p[i + 7] - p[0] = make_array_substr_assign(p.lineno(i), id_, arg_list, substr, expr_) - - -def p_let_arr_substr_in_args4(p): - """statement : LET ARRAY_ID LP arguments TO expr RP EQ expr - | ARRAY_ID LP arguments TO expr RP EQ expr - """ - i = 2 if p[1].upper() == "LET" else 1 - - id_ = p[i] - arg_list = p[i + 2] - substr = (arg_list.children.pop().value, p[i + 4]) - expr_ = p[i + 7] - p[0] = make_array_substr_assign(p.lineno(i), id_, arg_list, substr, expr_) - - -def p_addr_of_array_element(p): - """bexpr : ADDRESSOF ARRAY_ID arg_list""" - p[0] = None - - if p[3] is None: - return - - result = make_array_access(p[2], p.lineno(2), p[3]) - if result is None: - return - - mark_entry_as_accessed(result.entry) - p[0] = make_unary(p.lineno(1), "ADDRESS", result, type_=_TYPE(gl.PTR_TYPE)) - - -def p_err_undefined_arr_access(p): - """bexpr : ADDRESSOF ID arg_list""" - error(p.lineno(2), 'Undeclared array "%s"' % p[2]) - p[0] = None - - -def p_bexpr_func(p): - """bexpr : ID bexpr""" - args = make_arg_list(make_argument(p[2], p.lineno(2))) - p[0] = make_call(p[1], p.lineno(1), args) - if p[0] is None: - return - - if p[0].token in ("STRSLICE", "VAR", "STRING"): - entry = SYMBOL_TABLE.access_call(p[1], p.lineno(1)) - mark_entry_as_accessed(entry) - return - - convert_to_function(p[0].entry, CLASS.function, p.lineno(1)) - mark_entry_as_accessed(p[0].entry) - - -def p_arg_list(p): - """arg_list : LP RP""" - p[0] = make_arg_list(None) - - -def p_arg_list_arg(p): - """arg_list : LP arguments RP""" - p[0] = p[2] - - -def p_arguments(p): - """arguments : argument""" - if p[1] is None: - p[0] = None - return - - p[0] = make_arg_list(p[1]) - - -def p_arguments_argument(p): - """arguments : arguments COMMA argument""" - if p[1] is None or p[3] is None: - p[0] = None - else: - p[0] = make_arg_list(p[1], p[3]) - - -def p_argument(p): - """argument : expr %prec ID""" - p[0] = make_argument(p[1], p.lineno(1)) - - -def p_named_argument(p): - """argument : ID WEQ expr %prec ID""" - p[0] = make_argument(p[3], p.lineno(1), name=p[1]) - - -def p_argument_array(p): - """argument : ARRAY_ID""" - entry = SYMBOL_TABLE.access_array(p[1], p.lineno(1)) - if entry is None: - p[0] = None - return - - mark_entry_as_accessed(entry) - p[0] = make_argument(entry, p.lineno(1)) - - -def p_funcdecl(p): - """function_declaration : function_header function_body""" - if p[1] is None: - p[0] = None - return - - p[0] = p[1] - p[0].local_symbol_table = SYMBOL_TABLE.current_scope - p[0].locals_size = SYMBOL_TABLE.leave_scope() - FUNCTION_LEVEL.pop() - p[0].entry.ref.body = p[2] - p[0].local_symbol_table.owner = p[0] - p[0].entry.ref.forwarded = False - - -def p_funcdeclforward(p): - """function_declaration : DECLARE function_header_pre""" - if p[2] is None: - if FUNCTION_LEVEL: - FUNCTION_LEVEL.pop() - return - - if p[2].entry.forwarded: - error(p.lineno(1), "duplicated declaration for function '%s'" % p[2].name) - - p[2].entry.ref.forwarded = True - SYMBOL_TABLE.leave_scope(show_warnings=False) - FUNCTION_LEVEL.pop() - - -def p_function_header(p): - """function_header : function_header_pre CO - | function_header_pre NEWLINE - """ - p[0] = p[1] - - -def p_function_header_error(p): - """function_header : function_def error CO - | function_def error NEWLINE - """ - p[0] = None - - -def p_function_header_pre(p): - """function_header_pre : function_def param_decl typedef""" - if p[1] is None or p[2] is None: - p[0] = None - return - - forwarded = p[1].entry.forwarded - - p[0] = p[1] - p[0].append_child(p[2]) - p[0].params_size = p[2].size - lineno = p.lineno(3) - - previoustype_ = p[0].type_ - if not p[3].implicit or p[0].entry.type_ is None or p[0].entry.type_ == TYPE.unknown: - p[0].type_ = p[3] - if p[3].implicit and p[0].entry.class_ == CLASS.function: - errmsg.warning_implicit_type(p[3].lineno, p[0].entry.name, p[0].type_) - - if forwarded and previoustype_ != p[0].type_: - errmsg.syntax_error_func_type_mismatch(lineno, p[0].entry) - p[0] = None - return - - if forwarded: # Was predeclared, check parameters match - p1 = p[0].entry.ref.params # Param list previously declared - p2 = p[2].children - - if len(p1) != len(p2): - errmsg.syntax_error_parameter_mismatch(lineno, p[0].entry) - p[0] = None - return - - for a, b in zip(p1, p2): - if a.name != b.name: - warning( - lineno, - "Parameter '%s' in function '%s' has been renamed to '%s'" % (a.name, p[0].name, b.name), - ) - - if a.type_ != b.type_ or a.byref != b.byref: - errmsg.syntax_error_parameter_mismatch(lineno, p[0].entry) - p[0] = None - return - - p[0].entry.ref.params = p[2] - - if FUNCTION_LEVEL[-1].class_ == CLASS.sub and not p[3].implicit: - error(lineno, "SUBs cannot have a return type definition") - p[0] = None - return - - if FUNCTION_LEVEL[-1].class_ == CLASS.function: - src.api.check.check_type_is_explicit(p[0].lineno, p[0].entry.name, p[3]) - - if p[0].entry.convention == CONVENTION.fastcall and len(p[2]) > 1: - class_ = "SUB" if FUNCTION_LEVEL[-1].class_ == CLASS.sub else "FUNCTION" - errmsg.warning_fastcall_with_N_parameters(lineno, class_, p[0].entry.name, len(p[2])) - - -def p_function_error(p): - """function_declaration : function_header program_co END error""" - p[0] = None - error( - p.lineno(3), - "Unexpected token 'END'. Expected 'END FUNCTION' or 'END SUB' instead.", - ) - - -def p_function_def(p): - """function_def : FUNCTION convention ID - | SUB convention ID - """ - convention = p[2] - name = p[3] - class_ = CLASS.sub if p[1] == "SUB" else CLASS.function # Must be 'function' or 'sub' - - p[0] = make_func_declaration(name, p.lineno(3), class_) - SYMBOL_TABLE.enter_scope(name) - entry = SYMBOL_TABLE.get_entry(name) - FUNCTION_LEVEL.append(entry) - - if entry.class_ in (CLASS.function, CLASS.sub): # Was correctly declared? - FUNCTION_LEVEL[-1].ref.convention = convention - - -def p_convention(p): - """convention : - | STDCALL - """ - p[0] = CONVENTION.stdcall - - -def p_convention2(p): - """convention : FASTCALL""" - p[0] = CONVENTION.fastcall - - -def p_param_decl_none(p): - """param_decl : - | LP RP - """ - p[0] = make_param_list(None) - - -def p_param_decl(p): - """param_decl : LP param_decl_list RP""" - p[0] = p[2] - - -def p_param_decl_errpr(p): - """param_decl : LP error RP""" - p[0] = None - - -def p_param_decl_list(p): - """param_decl_list : param_definition""" - p[0] = make_param_list(p[1]) - - -def p_param_decl_list2(p): - """param_decl_list : param_decl_list COMMA param_definition""" - if p[1] is not None and p[3] is not None: # No errors in parsing - if p[3].default_value is None and p[1][-1].default_value is not None: - errmsg.syntax_error_mandatory_param_after_optional(p[3].lineno, p[1][-1].name, p[3].name) - - p[0] = make_param_list(p[1], p[3]) - - -def p_param_byref_definition(p): - """param_definition : BYREF param_def""" - p[0] = p[2] - - if p[0] is not None: - p[0].ref.byref = True - - -def p_param_byval_definition(p): - """param_definition : BYVAL param_def""" - param_def = p[2] - p[0] = param_def - - if p[0] is not None: - if param_def.class_ == CLASS.array: - errmsg.syntax_error_cannot_pass_array_by_value(p.lineno(1), param_def.name) - p[0] = None - return - param_def.ref.byref = False - - -def p_param_definition(p): - """param_definition : param_def""" - param_def = p[1] - p[0] = param_def - if p[0] is not None: - if param_def.class_ == CLASS.array: - param_def.ref.byref = True - else: - param_def.ref.byref = OPTIONS.default_byref - - -def p_param_def_array(p): - """param_def : singleid LP RP typedef""" - typeref = p[4] - if typeref is None: - p[0] = None - return - - lineno = p[1].lineno - id_ = p[1].name - - src.api.check.check_type_is_explicit(lineno, id_, typeref) - p[0] = make_param_decl(id_, lineno, typeref, is_array=True) - - -def p_param_def_type(p): - """param_def : singleid typedef default_arg_value""" - id_: Id = p[1] - typedef = p[2] - if typedef is not None: - src.api.check.check_type_is_explicit(id_.lineno, id_.name, typedef) - - default_value = make_typecast(typedef, p[3], id_.lineno) - p[0] = make_param_decl( - id_.name, - id_.lineno, - typedef, - is_array=False, - default_value=default_value, - ) - - -def p_param_def_default_arg_value(p): - """default_arg_value : - | EQ expr - """ - if len(p) == 1: - p[0] = None - return - - p[0] = p[2] - return - - -def p_function_body(p): - """function_body : program_co END FUNCTION - | program_co END SUB - | statements_co END FUNCTION - | statements_co END SUB - | co_statements_co END FUNCTION - | co_statements_co END SUB - | END FUNCTION - | END SUB - """ - if not FUNCTION_LEVEL: - error( - p.lineno(3), - "Unexpected token 'END %s'. No Function or Sub has been defined." % p[2], - ) - p[0] = None - return - - a = FUNCTION_LEVEL[-1].class_ - if a not in ( - CLASS.sub, - CLASS.function, - ): # This function/sub was not correctly declared, so exit now - p[0] = None - return - - i = 2 if p[1] == "END" else 3 - b = p[i].lower() - - if a != b: - error( - p.lineno(i), - "Unexpected token 'END %s'. Should be 'END %s'" % (b.upper(), a.upper()), - ) - p[0] = None - else: - p[0] = make_block() if p[1] == "END" else p[1] - - -def p_type_def_empty(p): - """typedef :""" # Epsilon. Defaults to float - p[0] = make_type(_TYPE(gl.DEFAULT_TYPE).name, p.lexer.lineno, implicit=True) - - -def p_type_def(p): - """typedef : AS type""" # Epsilon. Defaults to float - p[0] = make_type(p[2], p.lineno(2), implicit=False) - - -def p_type(p): - """type : BYTE - | UBYTE - | INTEGER - | UINTEGER - | LONG - | ULONG - | FIXED - | FLOAT - | STRING - """ - p[0] = p[1].lower() - - -# region PREPROC - -# ---------------------------------------- -# PREPROCESSOR lines starting with: -# -# #pragma -# #init -# #require -# -# are processed here and not in the lexer -# ---------------------------------------- - - -def p_preproc_line_init(p): - """preproc_line : _INIT ID""" - INITS.add(p[2]) - - -def p_preproc_line_require(p): - """preproc_line : _REQUIRE STRING""" - arch.target.backend.REQUIRES.add(p[2]) - - -def p_preproc_line_pragma_option(p): - """preproc_line : _PRAGMA ID EQ ID - | _PRAGMA ID EQ STRING - | _PRAGMA ID EQ INTEGER - """ - try: - setattr(OPTIONS, p[2], p[4]) - except src.api.options.UndefinedOptionError: - errmsg.warning_ignoring_unknown_pragma(p.lineno(2), p[2]) - - -def p_preproc_pragma_push(p): - """preproc_line : _PRAGMA _PUSH LP ID RP""" - try: - OPTIONS[p[4]].push() - except src.api.options.UndefinedOptionError: - errmsg.warning_ignoring_unknown_pragma(p.lineno(4), p[4]) - - -def p_preproc_pragma_pop(p): - """preproc_line : _PRAGMA _POP LP ID RP""" - try: - OPTIONS[p[4]].pop() - except src.api.options.UndefinedOptionError: - errmsg.warning_ignoring_unknown_pragma(p.lineno(4), p[4]) - - -# region INTERNAL FUNCTIONS -# ------------------------------------------- -# INTERNAL BASIC Functions -# These will be implemented in the translator -# module as a CALL to an ASM function -# ------------------------------------------- - - -def p_expr_usr(p): - """bexpr : USR bexpr %prec UMINUS""" - if p[2].type_ == TYPE.string: - p[0] = make_builtin(p.lineno(1), "USR_STR", p[2], type_=TYPE.uinteger) - else: - p[0] = make_builtin( - p.lineno(1), - "USR", - make_typecast(TYPE.uinteger, p[2], p.lineno(1)), - type_=TYPE.uinteger, - ) - - -def p_expr_rnd(p): - """bexpr : RND %prec ID - | RND LP RP - """ - p[0] = make_builtin(p.lineno(1), "RND", None, type_=TYPE.float_) - - -def p_expr_peek(p): - """bexpr : PEEK bexpr %prec UMINUS""" - p[0] = make_builtin( - p.lineno(1), - "PEEK", - make_typecast(TYPE.uinteger, p[2], p.lineno(1)), - type_=TYPE.ubyte, - ) - - -def p_expr_peektype_(p): - """bexpr : PEEK LP numbertype COMMA expr RP""" - p[0] = make_builtin(p.lineno(1), "PEEK", make_typecast(TYPE.uinteger, p[5], p.lineno(4)), type_=p[3]) - - -def p_expr_in(p): - """bexpr : IN bexpr %prec UMINUS""" - p[0] = make_builtin( - p.lineno(1), - "IN", - make_typecast(TYPE.uinteger, p[2], p.lineno(1)), - type_=TYPE.ubyte, - ) - - -def p_expr_lbound(p): - """bexpr : LBOUND LP ARRAY_ID RP - | UBOUND LP ARRAY_ID RP - """ - entry = SYMBOL_TABLE.access_array(p[3], p.lineno(3)) - if entry is None: - p[0] = None - return - - mark_entry_as_accessed(entry) - - if entry.scope == SCOPE.parameter: - num = make_number(0, p.lineno(3), TYPE.uinteger) - p[0] = make_builtin(p.lineno(1), p[1], [entry, num], type_=TYPE.uinteger) - else: - p[0] = make_number(len(entry.bounds), p.lineno(3), TYPE.uinteger) - - -def p_expr_lbound_expr(p): - """bexpr : LBOUND LP ARRAY_ID COMMA expr RP - | UBOUND LP ARRAY_ID COMMA expr RP - """ - expr = p[5] - if expr is None: - p[0] = None - return - - entry = SYMBOL_TABLE.access_array(p[3], p.lineno(3)) - if entry is None: - p[0] = None - return - - mark_entry_as_accessed(entry) - num = make_typecast(TYPE.uinteger, expr, p.lineno(6)) - if num is None: - p[0] = None - return - - if is_number(num) and entry.scope in ( - SCOPE.local, - SCOPE.global_, - ): # Try constant propagation - val = num.value - if val < 0 or val > len(entry.bounds): - error(p.lineno(6), "Dimension out of range") - p[0] = None - return - - if not val: # 0 => number of dims - p[0] = make_number(len(entry.bounds), p.lineno(3), TYPE.uinteger) - elif p[1] == "LBOUND": - p[0] = make_number(entry.bounds[val - 1].lower, p.lineno(3), TYPE.uinteger) - else: - p[0] = make_number(entry.bounds[val - 1].upper, p.lineno(3), TYPE.uinteger) - return - - if p[1] == "LBOUND": - entry.ref.lbound_used = True - else: - entry.ref.ubound_used = True - - p[0] = make_builtin(p.lineno(1), p[1], [entry, num], type_=TYPE.uinteger) - - -def p_len(p): - """bexpr : LEN bexpr %prec UMINUS""" - arg = p[2] - if arg is None: - p[0] = None - elif arg.token == "VAR" and arg.class_ == CLASS.array: - p[0] = make_number(len(arg.bounds), lineno=p.lineno(1)) # Do constant folding - elif arg.type_ != TYPE.string: - errmsg.syntax_error_expected_string(p.lineno(1), TYPE.to_string(arg.type_)) - p[0] = None - elif is_string(arg): # Constant string? - p[0] = make_number(len(arg.value), lineno=p.lineno(1)) # Do constant folding - else: - p[0] = make_builtin(p.lineno(1), "LEN", arg, type_=TYPE.uinteger) - - -def p_sizeof(p): - """bexpr : SIZEOF LP type RP - | SIZEOF LP ID RP - | SIZEOF LP ARRAY_ID RP - """ - if TYPE.to_type(p[3].lower()) is not None: - p[0] = make_number(TYPE.size(TYPE.to_type(p[3].lower())), lineno=p.lineno(3)) - else: - entry = SYMBOL_TABLE.get_id_or_make_var(p[3], p.lineno(1)) - p[0] = make_number(TYPE.size(entry.type_), lineno=p.lineno(3)) - - -def p_str(p): - """string : STR expr %prec UMINUS""" - if is_number(p[2]): # A constant is converted to string directly - p[0] = sym.STRING(str(p[2].value), p.lineno(1)) - else: - p[0] = make_builtin( - p.lineno(1), - "STR", - make_typecast(TYPE.float_, p[2], p.lineno(1)), - type_=TYPE.string, - ) - - -def p_inkey(p): - """string : INKEY""" - p[0] = make_builtin(p.lineno(1), "INKEY", None, type_=TYPE.string) - - -def p_chr_one(p): - """string : CHR bexpr %prec UMINUS""" - arg_list = make_arg_list(make_argument(p[2], p.lineno(1))) - arg_list[0].value = make_typecast(TYPE.ubyte, arg_list[0].value, p.lineno(1)) - p[0] = make_builtin(p.lineno(1), "CHR", arg_list, type_=TYPE.string) - - -def p_chr(p): - """string : CHR arg_list""" - if len(p[2]) < 1: - error(p.lineno(1), "CHR$ function need at less 1 parameter") - p[0] = None - return - - for i in range(len(p[2])): # Convert every argument to 8bit unsigned - p[2][i].value = make_typecast(TYPE.ubyte, p[2][i].value, p.lineno(1)) - - p[0] = make_builtin(p.lineno(1), "CHR", p[2], type_=TYPE.string) - - -def p_val(p): - """bexpr : VAL bexpr %prec UMINUS""" - - def val(s): - try: - x = float(eval(s, {}, {})) - except: - x = 0 - warning(p.lineno(1), f"Invalid string numeric constant '{s}' evaluated as 0") - return x - - if p[2].type_ != TYPE.string: - errmsg.syntax_error_expected_string(p.lineno(1), TYPE.to_string(p[2].type_)) - p[0] = None - else: - p[0] = make_builtin(p.lineno(1), "VAL", p[2], lambda x: val(x), type_=TYPE.float_) - - -def p_code(p): - """bexpr : CODE bexpr %prec UMINUS""" - - def asc(x): - if len(x): - return ord(x[0]) - - return 0 - - if p[2] is None: - p[0] = None - return - - if p[2].type_ != TYPE.string: - errmsg.syntax_error_expected_string(p.lineno(1), TYPE.to_string(p[2].type_)) - p[0] = None - else: - p[0] = make_builtin(p.lineno(1), "CODE", p[2], lambda x: asc(x), type_=TYPE.ubyte) - - -def p_sgn(p): - """bexpr : SGN bexpr %prec UMINUS""" - sgn = lambda x: x < 0 and -1 or x > 0 and 1 or 0 - - if p[2].type_ == TYPE.string: - error(p.lineno(1), "Expected a numeric expression, got TYPE.string instead") - p[0] = None - else: - if is_unsigned(p[2]) and not is_number(p[2]): - warning(p.lineno(1), "Sign of unsigned value is always 0 or 1") - - p[0] = make_builtin(p.lineno(1), "SGN", p[2], lambda x: sgn(x), type_=TYPE.byte_) - - -# ---------------------------------------- -# Trigonometric and LN, EXP, SQR -# ---------------------------------------- -def p_expr_trig(p): - """bexpr : math_fn bexpr %prec UMINUS""" - p[0] = make_builtin( - p.lineno(1), - p[1], - make_typecast(TYPE.float_, p[2], p.lineno(1)), - { - "SIN": math.sin, - "COS": math.cos, - "TAN": math.tan, - "ASN": math.asin, - "ACS": math.acos, - "ATN": math.atan, - "LN": lambda y: math.log(y, math.exp(1)), # LN(x) - "EXP": math.exp, - "SQR": math.sqrt, - }[p[1]], - type_=TYPE.float_, - ) - - -def p_math_fn(p): - """math_fn : SIN - | COS - | TAN - | ASN - | ACS - | ATN - | LN - | EXP - | SQR - """ - p[0] = p[1] - - -# ---------------------------------------- -# Other important functions -# ---------------------------------------- -def p_expr_int(p): - """bexpr : INT bexpr %prec UMINUS""" - p[0] = make_typecast(TYPE.long_, p[2], p.lineno(1)) - - -def p_abs(p): - """bexpr : ABS bexpr %prec UMINUS""" - if is_unsigned(p[2]): - p[0] = p[2] - warning(p.lineno(1), "Redundant operation ABS for unsigned value") - return - - p[0] = make_builtin(p.lineno(1), "ABS", p[2], lambda x: x if x >= 0 else -x) - - -# endregion - - -# ---------------------------------------- -# The yyerror function -# ---------------------------------------- -def p_error(p): - if p is not None: - if p.type != "NEWLINE": - msg = "Syntax Error. Unexpected token '%s' <%s>" % (p.value, p.type) - else: - msg = "Unexpected end of line" - error(p.lineno, msg) - return - - # Try to give some hints - if gl.LOOPS: # some loop(s) are not closed - loop_info = gl.LOOPS[-1] - if loop_info.type == LoopType.FOR: - errmsg.syntax_error_for_without_next(loop_info.lineno) - else: - errmsg.syntax_error_loop_not_closed(loop_info.lineno, loop_info.type.value) - # If there were previous errors, stop here - # since this end of file is due to previous errors - if gl.has_errors: - return + if gl.has_errors: + return False msg = "Unexpected end of file" error(zxblex.lexer.lineno, msg) + return False + + +class LarkParserWrapper: + def __init__(self, lark_instance): + self.lark = lark_instance + + # Wrap LALR parser callbacks for inline execution during parsing + lalr_parser = lark_instance.parser.parser.parser + transformer = ZXBasicTransformer() + wrapped_callbacks = {} + for rule, callback in list(lalr_parser.callbacks.items()): + method_name = rule.alias if rule.alias is not None else rule.origin.name + if hasattr(transformer, method_name): + method = getattr(transformer, method_name) + + def make_wrapper(m=method): + def wrapper(children): + class MockMeta: + @property + def line(self): + for child in children: + lineno = get_lineno(child) + if lineno: + return lineno + return zxblex.lexer.lineno if hasattr(zxblex, "lexer") else 0 + + meta = MockMeta() + return m(meta, children) + + return wrapper + + wrapped_callbacks[rule] = make_wrapper() + else: + wrapped_callbacks[rule] = callback + lalr_parser.callbacks = wrapped_callbacks + + def parse(self, text, lexer=None, tracking=True, debug=False): + if hasattr(handle_parse_error, "last_key"): + handle_parse_error.last_key = None + if lexer is not None: + lexer.input(text) + try: + self.lark.parse(lexer, on_error=handle_parse_error) + except Exception as e: + if not isinstance(e, UnexpectedInput): + raise e + else: + try: + self.lark.parse(text, on_error=handle_parse_error) + except Exception as e: + if not isinstance(e, UnexpectedInput): + raise e -# ---------------------------------------- -# Initialization -# ---------------------------------------- -parser = src.api.utils.get_or_create("zxbparser", lambda: yacc.yacc(debug=True)) - -ast = None -data_ast = None # Global Variables AST -optemps = opcodestemps.OpcodesTemps() +# DO NOT pass transformer=ZXBasicTransformer() to Lark constructor when using meta args +lark_parser = Lark_StandAlone(lexer=ZXBasicLarkLexerAdapter, propagate_positions=True) +parser = LarkParserWrapper(lark_parser) diff --git a/src/zxbc/zxbparser_standalone.py b/src/zxbc/zxbparser_standalone.py new file mode 100644 index 000000000..e5c3924f5 --- /dev/null +++ b/src/zxbc/zxbparser_standalone.py @@ -0,0 +1,3574 @@ +# The file was automatically generated by Lark v1.3.1 +__version__ = "1.3.1" + +# +# +# Lark Stand-alone Generator Tool +# ---------------------------------- +# Generates a stand-alone LALR(1) parser +# +# Git: https://github.com/erezsh/lark +# Author: Erez Shinan (erezshin@gmail.com) +# +# +# >>> LICENSE +# +# This tool and its generated code use a separate license from Lark, +# and are subject to the terms of the Mozilla Public License, v. 2.0. +# If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +# If you wish to purchase a commercial license for this tool and its +# generated code, you may contact me via email or otherwise. +# +# If MPL2 is incompatible with your free or open-source project, +# contact me and we'll work it out. +# +# + +from copy import deepcopy +from abc import ABC, abstractmethod +from types import ModuleType +from typing import ( + TypeVar, Generic, Type, Tuple, List, Dict, Iterator, Collection, Callable, Optional, FrozenSet, Any, + Union, Iterable, IO, TYPE_CHECKING, overload, Sequence, + Pattern as REPattern, ClassVar, Set, Mapping +) + + +class LarkError(Exception): + pass + + +class ConfigurationError(LarkError, ValueError): + pass + + +def assert_config(value, options: Collection, msg='Got %r, expected one of %s'): + if value not in options: + raise ConfigurationError(msg % (value, options)) + + +class GrammarError(LarkError): + pass + + +class ParseError(LarkError): + pass + + +class LexError(LarkError): + pass + +T = TypeVar('T') + +class UnexpectedInput(LarkError): + #-- + line: int + column: int + pos_in_stream = None + state: Any + _terminals_by_name = None + interactive_parser: 'InteractiveParser' + + def get_context(self, text: str, span: int=40) -> str: + #-- + pos = self.pos_in_stream or 0 + start = max(pos - span, 0) + end = pos + span + if not isinstance(text, bytes): + before = text[start:pos].rsplit('\n', 1)[-1] + after = text[pos:end].split('\n', 1)[0] + return before + after + '\n' + ' ' * len(before.expandtabs()) + '^\n' + else: + before = text[start:pos].rsplit(b'\n', 1)[-1] + after = text[pos:end].split(b'\n', 1)[0] + return (before + after + b'\n' + b' ' * len(before.expandtabs()) + b'^\n').decode("ascii", "backslashreplace") + + def match_examples(self, parse_fn: 'Callable[[str], Tree]', + examples: Union[Mapping[T, Iterable[str]], Iterable[Tuple[T, Iterable[str]]]], + token_type_match_fallback: bool=False, + use_accepts: bool=True + ) -> Optional[T]: + #-- + assert self.state is not None, "Not supported for this exception" + + if isinstance(examples, Mapping): + examples = examples.items() + + candidate = (None, False) + for i, (label, example) in enumerate(examples): + assert not isinstance(example, str), "Expecting a list" + + for j, malformed in enumerate(example): + try: + parse_fn(malformed) + except UnexpectedInput as ut: + if ut.state == self.state: + if ( + use_accepts + and isinstance(self, UnexpectedToken) + and isinstance(ut, UnexpectedToken) + and ut.accepts != self.accepts + ): + logger.debug("Different accepts with same state[%d]: %s != %s at example [%s][%s]" % + (self.state, self.accepts, ut.accepts, i, j)) + continue + if ( + isinstance(self, (UnexpectedToken, UnexpectedEOF)) + and isinstance(ut, (UnexpectedToken, UnexpectedEOF)) + ): + if ut.token == self.token: ## + + logger.debug("Exact Match at example [%s][%s]" % (i, j)) + return label + + if token_type_match_fallback: + ## + + if (ut.token.type == self.token.type) and not candidate[-1]: + logger.debug("Token Type Fallback at example [%s][%s]" % (i, j)) + candidate = label, True + + if candidate[0] is None: + logger.debug("Same State match at example [%s][%s]" % (i, j)) + candidate = label, False + + return candidate[0] + + def _format_expected(self, expected): + if self._terminals_by_name: + d = self._terminals_by_name + expected = [d[t_name].user_repr() if t_name in d else t_name for t_name in expected] + return "Expected one of: \n\t* %s\n" % '\n\t* '.join(expected) + + +class UnexpectedEOF(ParseError, UnexpectedInput): + #-- + expected: 'List[Token]' + + def __init__(self, expected, state=None, terminals_by_name=None): + super(UnexpectedEOF, self).__init__() + + self.expected = expected + self.state = state + from .lexer import Token + self.token = Token("", "") ## + + self.pos_in_stream = -1 + self.line = -1 + self.column = -1 + self._terminals_by_name = terminals_by_name + + + def __str__(self): + message = "Unexpected end-of-input. " + message += self._format_expected(self.expected) + return message + + +class UnexpectedCharacters(LexError, UnexpectedInput): + #-- + + allowed: Set[str] + considered_tokens: Set[Any] + + def __init__(self, seq, lex_pos, line, column, allowed=None, considered_tokens=None, state=None, token_history=None, + terminals_by_name=None, considered_rules=None): + super(UnexpectedCharacters, self).__init__() + + ## + + self.line = line + self.column = column + self.pos_in_stream = lex_pos + self.state = state + self._terminals_by_name = terminals_by_name + + self.allowed = allowed + self.considered_tokens = considered_tokens + self.considered_rules = considered_rules + self.token_history = token_history + + if isinstance(seq, bytes): + self.char = seq[lex_pos:lex_pos + 1].decode("ascii", "backslashreplace") + else: + self.char = seq[lex_pos] + self._context = self.get_context(seq) + + + def __str__(self): + message = "No terminal matches '%s' in the current parser context, at line %d col %d" % (self.char, self.line, self.column) + message += '\n\n' + self._context + if self.allowed: + message += self._format_expected(self.allowed) + if self.token_history: + message += '\nPrevious tokens: %s\n' % ', '.join(repr(t) for t in self.token_history) + return message + + +class UnexpectedToken(ParseError, UnexpectedInput): + #-- + + expected: Set[str] + considered_rules: Set[str] + + def __init__(self, token, expected, considered_rules=None, state=None, interactive_parser=None, terminals_by_name=None, token_history=None): + super(UnexpectedToken, self).__init__() + + ## + + self.line = getattr(token, 'line', '?') + self.column = getattr(token, 'column', '?') + self.pos_in_stream = getattr(token, 'start_pos', None) + self.state = state + + self.token = token + self.expected = expected ## + + self._accepts = NO_VALUE + self.considered_rules = considered_rules + self.interactive_parser = interactive_parser + self._terminals_by_name = terminals_by_name + self.token_history = token_history + + + @property + def accepts(self) -> Set[str]: + if self._accepts is NO_VALUE: + self._accepts = self.interactive_parser and self.interactive_parser.accepts() + return self._accepts + + def __str__(self): + message = ("Unexpected token %r at line %s, column %s.\n%s" + % (self.token, self.line, self.column, self._format_expected(self.accepts or self.expected))) + if self.token_history: + message += "Previous tokens: %r\n" % self.token_history + + return message + + + +class VisitError(LarkError): + #-- + + obj: 'Union[Tree, Token]' + orig_exc: Exception + + def __init__(self, rule, obj, orig_exc): + message = 'Error trying to process rule "%s":\n\n%s' % (rule, orig_exc) + super(VisitError, self).__init__(message) + + self.rule = rule + self.obj = obj + self.orig_exc = orig_exc + + +class MissingVariableError(LarkError): + pass + + +import sys, re +import logging +from dataclasses import dataclass +from typing import Generic, AnyStr + +logger: logging.Logger = logging.getLogger("lark") +logger.addHandler(logging.StreamHandler()) +## + +## + +logger.setLevel(logging.CRITICAL) + + +NO_VALUE = object() + +T = TypeVar("T") + + +def classify(seq: Iterable, key: Optional[Callable] = None, value: Optional[Callable] = None) -> Dict: + d: Dict[Any, Any] = {} + for item in seq: + k = key(item) if (key is not None) else item + v = value(item) if (value is not None) else item + try: + d[k].append(v) + except KeyError: + d[k] = [v] + return d + + +def _deserialize(data: Any, namespace: Dict[str, Any], memo: Dict) -> Any: + if isinstance(data, dict): + if '__type__' in data: ## + + class_ = namespace[data['__type__']] + return class_.deserialize(data, memo) + elif '@' in data: + return memo[data['@']] + return {key:_deserialize(value, namespace, memo) for key, value in data.items()} + elif isinstance(data, list): + return [_deserialize(value, namespace, memo) for value in data] + return data + + +_T = TypeVar("_T", bound="Serialize") + +class Serialize: + #-- + + def memo_serialize(self, types_to_memoize: List) -> Any: + memo = SerializeMemoizer(types_to_memoize) + return self.serialize(memo), memo.serialize() + + def serialize(self, memo = None) -> Dict[str, Any]: + if memo and memo.in_types(self): + return {'@': memo.memoized.get(self)} + + fields = getattr(self, '__serialize_fields__') + res = {f: _serialize(getattr(self, f), memo) for f in fields} + res['__type__'] = type(self).__name__ + if hasattr(self, '_serialize'): + self._serialize(res, memo) + return res + + @classmethod + def deserialize(cls: Type[_T], data: Dict[str, Any], memo: Dict[int, Any]) -> _T: + namespace = getattr(cls, '__serialize_namespace__', []) + namespace = {c.__name__:c for c in namespace} + + fields = getattr(cls, '__serialize_fields__') + + if '@' in data: + return memo[data['@']] + + inst = cls.__new__(cls) + for f in fields: + try: + setattr(inst, f, _deserialize(data[f], namespace, memo)) + except KeyError as e: + raise KeyError("Cannot find key for class", cls, e) + + if hasattr(inst, '_deserialize'): + inst._deserialize() + + return inst + + +class SerializeMemoizer(Serialize): + #-- + + __serialize_fields__ = 'memoized', + + def __init__(self, types_to_memoize: List) -> None: + self.types_to_memoize = tuple(types_to_memoize) + self.memoized = Enumerator() + + def in_types(self, value: Serialize) -> bool: + return isinstance(value, self.types_to_memoize) + + def serialize(self) -> Dict[int, Any]: ## + + return _serialize(self.memoized.reversed(), None) + + @classmethod + def deserialize(cls, data: Dict[int, Any], namespace: Dict[str, Any], memo: Dict[Any, Any]) -> Dict[int, Any]: ## + + return _deserialize(data, namespace, memo) + + +try: + import regex + _has_regex = True +except ImportError: + _has_regex = False + +if sys.version_info >= (3, 11): + import re._parser as sre_parse + import re._constants as sre_constants +else: + import sre_parse + import sre_constants + +categ_pattern = re.compile(r'\\p{[A-Za-z_]+}') + +def get_regexp_width(expr: str) -> Union[Tuple[int, int], List[int]]: + if _has_regex: + ## + + ## + + ## + + regexp_final = re.sub(categ_pattern, 'A', expr) + else: + if re.search(categ_pattern, expr): + raise ImportError('`regex` module must be installed in order to use Unicode categories.', expr) + regexp_final = expr + try: + ## + + return [int(x) for x in sre_parse.parse(regexp_final).getwidth()] + except sre_constants.error: + if not _has_regex: + raise ValueError(expr) + else: + ## + + ## + + c = regex.compile(regexp_final) + ## + + ## + + MAXWIDTH = getattr(sre_parse, "MAXWIDTH", sre_constants.MAXREPEAT) + if c.match('') is None: + ## + + return 1, int(MAXWIDTH) + else: + return 0, int(MAXWIDTH) + + +@dataclass(frozen=True) +class TextSlice(Generic[AnyStr]): + #-- + text: AnyStr + start: int + end: int + + def __post_init__(self): + if not isinstance(self.text, (str, bytes)): + raise TypeError("text must be str or bytes") + + if self.start < 0: + object.__setattr__(self, 'start', self.start + len(self.text)) + assert self.start >=0 + + if self.end is None: + object.__setattr__(self, 'end', len(self.text)) + elif self.end < 0: + object.__setattr__(self, 'end', self.end + len(self.text)) + assert self.end <= len(self.text) + + @classmethod + def cast_from(cls, text: 'TextOrSlice') -> 'TextSlice[AnyStr]': + if isinstance(text, TextSlice): + return text + + return cls(text, 0, len(text)) + + def is_complete_text(self): + return self.start == 0 and self.end == len(self.text) + + def __len__(self): + return self.end - self.start + + def count(self, substr: AnyStr): + return self.text.count(substr, self.start, self.end) + + def rindex(self, substr: AnyStr): + return self.text.rindex(substr, self.start, self.end) + + +TextOrSlice = Union[AnyStr, 'TextSlice[AnyStr]'] +LarkInput = Union[AnyStr, TextSlice[AnyStr], Any] + + + +class Meta: + + empty: bool + line: int + column: int + start_pos: int + end_line: int + end_column: int + end_pos: int + orig_expansion: 'List[TerminalDef]' + match_tree: bool + + def __init__(self): + self.empty = True + + +_Leaf_T = TypeVar("_Leaf_T") +Branch = Union[_Leaf_T, 'Tree[_Leaf_T]'] + + +class Tree(Generic[_Leaf_T]): + #-- + + data: str + children: 'List[Branch[_Leaf_T]]' + + def __init__(self, data: str, children: 'List[Branch[_Leaf_T]]', meta: Optional[Meta]=None) -> None: + self.data = data + self.children = children + self._meta = meta + + @property + def meta(self) -> Meta: + if self._meta is None: + self._meta = Meta() + return self._meta + + def __repr__(self): + return 'Tree(%r, %r)' % (self.data, self.children) + + __match_args__ = ("data", "children") + + def _pretty_label(self): + return self.data + + def _pretty(self, level, indent_str): + yield f'{indent_str*level}{self._pretty_label()}' + if len(self.children) == 1 and not isinstance(self.children[0], Tree): + yield f'\t{self.children[0]}\n' + else: + yield '\n' + for n in self.children: + if isinstance(n, Tree): + yield from n._pretty(level+1, indent_str) + else: + yield f'{indent_str*(level+1)}{n}\n' + + def pretty(self, indent_str: str=' ') -> str: + #-- + return ''.join(self._pretty(0, indent_str)) + + def __rich__(self, parent:Optional['rich.tree.Tree']=None) -> 'rich.tree.Tree': + #-- + return self._rich(parent) + + def _rich(self, parent): + if parent: + tree = parent.add(f'[bold]{self.data}[/bold]') + else: + import rich.tree + tree = rich.tree.Tree(self.data) + + for c in self.children: + if isinstance(c, Tree): + c._rich(tree) + else: + tree.add(f'[green]{c}[/green]') + + return tree + + def __eq__(self, other): + try: + return self.data == other.data and self.children == other.children + except AttributeError: + return False + + def __ne__(self, other): + return not (self == other) + + def __hash__(self) -> int: + return hash((self.data, tuple(self.children))) + + def iter_subtrees(self) -> 'Iterator[Tree[_Leaf_T]]': + #-- + queue = [self] + subtrees = dict() + for subtree in queue: + subtrees[id(subtree)] = subtree + queue += [c for c in reversed(subtree.children) + if isinstance(c, Tree) and id(c) not in subtrees] + + del queue + return reversed(list(subtrees.values())) + + def iter_subtrees_topdown(self): + #-- + stack = [self] + stack_append = stack.append + stack_pop = stack.pop + while stack: + node = stack_pop() + if not isinstance(node, Tree): + continue + yield node + for child in reversed(node.children): + stack_append(child) + + def find_pred(self, pred: 'Callable[[Tree[_Leaf_T]], bool]') -> 'Iterator[Tree[_Leaf_T]]': + #-- + return filter(pred, self.iter_subtrees()) + + def find_data(self, data: str) -> 'Iterator[Tree[_Leaf_T]]': + #-- + return self.find_pred(lambda t: t.data == data) + + +from functools import wraps, update_wrapper +from inspect import getmembers, getmro + +_Return_T = TypeVar('_Return_T') +_Return_V = TypeVar('_Return_V') +_Leaf_T = TypeVar('_Leaf_T') +_Leaf_U = TypeVar('_Leaf_U') +_R = TypeVar('_R') +_FUNC = Callable[..., _Return_T] +_DECORATED = Union[_FUNC, type] + +class _DiscardType: + #-- + + def __repr__(self): + return "lark.visitors.Discard" + +Discard = _DiscardType() + +## + + +class _Decoratable: + #-- + + @classmethod + def _apply_v_args(cls, visit_wrapper): + mro = getmro(cls) + assert mro[0] is cls + libmembers = {name for _cls in mro[1:] for name, _ in getmembers(_cls)} + for name, value in getmembers(cls): + + ## + + if name.startswith('_') or (name in libmembers and name not in cls.__dict__): + continue + if not callable(value): + continue + + ## + + if isinstance(cls.__dict__[name], _VArgsWrapper): + continue + + setattr(cls, name, _VArgsWrapper(cls.__dict__[name], visit_wrapper)) + return cls + + def __class_getitem__(cls, _): + return cls + + +class Transformer(_Decoratable, ABC, Generic[_Leaf_T, _Return_T]): + #-- + __visit_tokens__ = True ## + + + def __init__(self, visit_tokens: bool=True) -> None: + self.__visit_tokens__ = visit_tokens + + def _call_userfunc(self, tree, new_children=None): + ## + + children = new_children if new_children is not None else tree.children + try: + f = getattr(self, tree.data) + except AttributeError: + return self.__default__(tree.data, children, tree.meta) + else: + try: + wrapper = getattr(f, 'visit_wrapper', None) + if wrapper is not None: + return f.visit_wrapper(f, tree.data, children, tree.meta) + else: + return f(children) + except GrammarError: + raise + except Exception as e: + raise VisitError(tree.data, tree, e) + + def _call_userfunc_token(self, token): + try: + f = getattr(self, token.type) + except AttributeError: + return self.__default_token__(token) + else: + try: + return f(token) + except GrammarError: + raise + except Exception as e: + raise VisitError(token.type, token, e) + + def _transform_children(self, children): + for c in children: + if isinstance(c, Tree): + res = self._transform_tree(c) + elif self.__visit_tokens__ and isinstance(c, Token): + res = self._call_userfunc_token(c) + else: + res = c + + if res is not Discard: + yield res + + def _transform_tree(self, tree): + children = list(self._transform_children(tree.children)) + return self._call_userfunc(tree, children) + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + #-- + res = list(self._transform_children([tree])) + if not res: + return None ## + + assert len(res) == 1 + return res[0] + + def __mul__( + self: 'Transformer[_Leaf_T, Tree[_Leaf_U]]', + other: 'Union[Transformer[_Leaf_U, _Return_V], TransformerChain[_Leaf_U, _Return_V,]]' + ) -> 'TransformerChain[_Leaf_T, _Return_V]': + #-- + return TransformerChain(self, other) + + def __default__(self, data, children, meta): + #-- + return Tree(data, children, meta) + + def __default_token__(self, token): + #-- + return token + + +def merge_transformers(base_transformer=None, **transformers_to_merge): + #-- + if base_transformer is None: + base_transformer = Transformer() + for prefix, transformer in transformers_to_merge.items(): + for method_name in dir(transformer): + method = getattr(transformer, method_name) + if not callable(method): + continue + if method_name.startswith("_") or method_name == "transform": + continue + prefixed_method = prefix + "__" + method_name + if hasattr(base_transformer, prefixed_method): + raise AttributeError("Cannot merge: method '%s' appears more than once" % prefixed_method) + + setattr(base_transformer, prefixed_method, method) + + return base_transformer + + +class InlineTransformer(Transformer): ## + + def _call_userfunc(self, tree, new_children=None): + ## + + children = new_children if new_children is not None else tree.children + try: + f = getattr(self, tree.data) + except AttributeError: + return self.__default__(tree.data, children, tree.meta) + else: + return f(*children) + + +class TransformerChain(Generic[_Leaf_T, _Return_T]): + + transformers: 'Tuple[Union[Transformer, TransformerChain], ...]' + + def __init__(self, *transformers: 'Union[Transformer, TransformerChain]') -> None: + self.transformers = transformers + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + for t in self.transformers: + tree = t.transform(tree) + return cast(_Return_T, tree) + + def __mul__( + self: 'TransformerChain[_Leaf_T, Tree[_Leaf_U]]', + other: 'Union[Transformer[_Leaf_U, _Return_V], TransformerChain[_Leaf_U, _Return_V]]' + ) -> 'TransformerChain[_Leaf_T, _Return_V]': + return TransformerChain(*self.transformers + (other,)) + + +class Transformer_InPlace(Transformer[_Leaf_T, _Return_T]): + #-- + def _transform_tree(self, tree): ## + + return self._call_userfunc(tree) + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + for subtree in tree.iter_subtrees(): + subtree.children = list(self._transform_children(subtree.children)) + + return self._transform_tree(tree) + + +class Transformer_NonRecursive(Transformer[_Leaf_T, _Return_T]): + #-- + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + ## + + rev_postfix = [] + q: List[Branch[_Leaf_T]] = [tree] + while q: + t = q.pop() + rev_postfix.append(t) + if isinstance(t, Tree): + q += t.children + + ## + + stack: List = [] + for x in reversed(rev_postfix): + if isinstance(x, Tree): + size = len(x.children) + if size: + args = stack[-size:] + del stack[-size:] + else: + args = [] + + res = self._call_userfunc(x, args) + if res is not Discard: + stack.append(res) + + elif self.__visit_tokens__ and isinstance(x, Token): + res = self._call_userfunc_token(x) + if res is not Discard: + stack.append(res) + else: + stack.append(x) + + result, = stack ## + + ## + + ## + + ## + + return cast(_Return_T, result) + + +class Transformer_InPlaceRecursive(Transformer[_Leaf_T, _Return_T]): + #-- + def _transform_tree(self, tree): + tree.children = list(self._transform_children(tree.children)) + return self._call_userfunc(tree) + + +## + + +class VisitorBase: + def _call_userfunc(self, tree): + return getattr(self, tree.data, self.__default__)(tree) + + def __default__(self, tree): + #-- + return tree + + def __class_getitem__(cls, _): + return cls + + +class Visitor(VisitorBase, ABC, Generic[_Leaf_T]): + #-- + + def visit(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + for subtree in tree.iter_subtrees(): + self._call_userfunc(subtree) + return tree + + def visit_topdown(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + for subtree in tree.iter_subtrees_topdown(): + self._call_userfunc(subtree) + return tree + + +class Visitor_Recursive(VisitorBase, Generic[_Leaf_T]): + #-- + + def visit(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + for child in tree.children: + if isinstance(child, Tree): + self.visit(child) + + self._call_userfunc(tree) + return tree + + def visit_topdown(self,tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + self._call_userfunc(tree) + + for child in tree.children: + if isinstance(child, Tree): + self.visit_topdown(child) + + return tree + + +class Interpreter(_Decoratable, ABC, Generic[_Leaf_T, _Return_T]): + #-- + + def visit(self, tree: Tree[_Leaf_T]) -> _Return_T: + ## + + ## + + ## + + return self._visit_tree(tree) + + def _visit_tree(self, tree: Tree[_Leaf_T]): + f = getattr(self, tree.data) + wrapper = getattr(f, 'visit_wrapper', None) + if wrapper is not None: + return f.visit_wrapper(f, tree.data, tree.children, tree.meta) + else: + return f(tree) + + def visit_children(self, tree: Tree[_Leaf_T]) -> List: + return [self._visit_tree(child) if isinstance(child, Tree) else child + for child in tree.children] + + def __getattr__(self, name): + return self.__default__ + + def __default__(self, tree): + return self.visit_children(tree) + + +_InterMethod = Callable[[Type[Interpreter], _Return_T], _R] + +def visit_children_decor(func: _InterMethod) -> _InterMethod: + #-- + @wraps(func) + def inner(cls, tree): + values = cls.visit_children(tree) + return func(cls, values) + return inner + +## + + +def _apply_v_args(obj, visit_wrapper): + try: + _apply = obj._apply_v_args + except AttributeError: + return _VArgsWrapper(obj, visit_wrapper) + else: + return _apply(visit_wrapper) + + +class _VArgsWrapper: + #-- + base_func: Callable + + def __init__(self, func: Callable, visit_wrapper: Callable[[Callable, str, list, Any], Any]): + if isinstance(func, _VArgsWrapper): + func = func.base_func + self.base_func = func + self.visit_wrapper = visit_wrapper + update_wrapper(self, func) + + def __call__(self, *args, **kwargs): + return self.base_func(*args, **kwargs) + + def __get__(self, instance, owner=None): + try: + ## + + ## + + g = type(self.base_func).__get__ + except AttributeError: + return self + else: + return _VArgsWrapper(g(self.base_func, instance, owner), self.visit_wrapper) + + def __set_name__(self, owner, name): + try: + f = type(self.base_func).__set_name__ + except AttributeError: + return + else: + f(self.base_func, owner, name) + + +def _vargs_inline(f, _data, children, _meta): + return f(*children) +def _vargs_meta_inline(f, _data, children, meta): + return f(meta, *children) +def _vargs_meta(f, _data, children, meta): + return f(meta, children) +def _vargs_tree(f, data, children, meta): + return f(Tree(data, children, meta)) + + +def v_args(inline: bool = False, meta: bool = False, tree: bool = False, wrapper: Optional[Callable] = None) -> Callable[[_DECORATED], _DECORATED]: + #-- + if tree and (meta or inline): + raise ValueError("Visitor functions cannot combine 'tree' with 'meta' or 'inline'.") + + func = None + if meta: + if inline: + func = _vargs_meta_inline + else: + func = _vargs_meta + elif inline: + func = _vargs_inline + elif tree: + func = _vargs_tree + + if wrapper is not None: + if func is not None: + raise ValueError("Cannot use 'wrapper' along with 'tree', 'meta' or 'inline'.") + func = wrapper + + def _visitor_args_dec(obj): + return _apply_v_args(obj, func) + return _visitor_args_dec + + + +TOKEN_DEFAULT_PRIORITY = 0 + + +class Symbol(Serialize): + __slots__ = ('name',) + + name: str + is_term: ClassVar[bool] = NotImplemented + + def __init__(self, name: str) -> None: + self.name = name + + def __eq__(self, other): + if not isinstance(other, Symbol): + return NotImplemented + return self.is_term == other.is_term and self.name == other.name + + def __ne__(self, other): + return not (self == other) + + def __hash__(self): + return hash(self.name) + + def __repr__(self): + return '%s(%r)' % (type(self).__name__, self.name) + + fullrepr = property(__repr__) + + def renamed(self, f): + return type(self)(f(self.name)) + + +class Terminal(Symbol): + __serialize_fields__ = 'name', 'filter_out' + + is_term: ClassVar[bool] = True + + def __init__(self, name: str, filter_out: bool = False) -> None: + self.name = name + self.filter_out = filter_out + + @property + def fullrepr(self): + return '%s(%r, %r)' % (type(self).__name__, self.name, self.filter_out) + + def renamed(self, f): + return type(self)(f(self.name), self.filter_out) + + +class NonTerminal(Symbol): + __serialize_fields__ = 'name', + + is_term: ClassVar[bool] = False + + def serialize(self, memo=None) -> Dict[str, Any]: + ## + + ## + + return {'name': str(self.name), '__type__': 'NonTerminal'} + + +class RuleOptions(Serialize): + __serialize_fields__ = 'keep_all_tokens', 'expand1', 'priority', 'template_source', 'empty_indices' + + keep_all_tokens: bool + expand1: bool + priority: Optional[int] + template_source: Optional[str] + empty_indices: Tuple[bool, ...] + + def __init__(self, keep_all_tokens: bool=False, expand1: bool=False, priority: Optional[int]=None, template_source: Optional[str]=None, empty_indices: Tuple[bool, ...]=()) -> None: + self.keep_all_tokens = keep_all_tokens + self.expand1 = expand1 + self.priority = priority + self.template_source = template_source + self.empty_indices = empty_indices + + def __repr__(self): + return 'RuleOptions(%r, %r, %r, %r)' % ( + self.keep_all_tokens, + self.expand1, + self.priority, + self.template_source + ) + + +class Rule(Serialize): + #-- + __slots__ = ('origin', 'expansion', 'alias', 'options', 'order', '_hash') + + __serialize_fields__ = 'origin', 'expansion', 'order', 'alias', 'options' + __serialize_namespace__ = Terminal, NonTerminal, RuleOptions + + origin: NonTerminal + expansion: Sequence[Symbol] + order: int + alias: Optional[str] + options: RuleOptions + _hash: int + + def __init__(self, origin: NonTerminal, expansion: Sequence[Symbol], + order: int=0, alias: Optional[str]=None, options: Optional[RuleOptions]=None): + self.origin = origin + self.expansion = expansion + self.alias = alias + self.order = order + self.options = options or RuleOptions() + self._hash = hash((self.origin, tuple(self.expansion))) + + def _deserialize(self): + self._hash = hash((self.origin, tuple(self.expansion))) + + def __str__(self): + return '<%s : %s>' % (self.origin.name, ' '.join(x.name for x in self.expansion)) + + def __repr__(self): + return 'Rule(%r, %r, %r, %r)' % (self.origin, self.expansion, self.alias, self.options) + + def __hash__(self): + return self._hash + + def __eq__(self, other): + if not isinstance(other, Rule): + return False + return self.origin == other.origin and self.expansion == other.expansion + + + +from contextlib import suppress +from copy import copy + +try: ## + + has_interegular = bool(interegular) +except NameError: + has_interegular = False + +class Pattern(Serialize, ABC): + #-- + + value: str + flags: Collection[str] + raw: Optional[str] + type: ClassVar[str] + + def __init__(self, value: str, flags: Collection[str] = (), raw: Optional[str] = None) -> None: + self.value = value + self.flags = frozenset(flags) + self.raw = raw + + def __repr__(self): + return repr(self.to_regexp()) + + ## + + def __hash__(self): + return hash((type(self), self.value, self.flags)) + + def __eq__(self, other): + return type(self) == type(other) and self.value == other.value and self.flags == other.flags + + @abstractmethod + def to_regexp(self) -> str: + raise NotImplementedError() + + @property + @abstractmethod + def min_width(self) -> int: + raise NotImplementedError() + + @property + @abstractmethod + def max_width(self) -> int: + raise NotImplementedError() + + def _get_flags(self, value): + for f in self.flags: + value = ('(?%s:%s)' % (f, value)) + return value + + +class PatternStr(Pattern): + __serialize_fields__ = 'value', 'flags', 'raw' + + type: ClassVar[str] = "str" + + def to_regexp(self) -> str: + return self._get_flags(re.escape(self.value)) + + @property + def min_width(self) -> int: + return len(self.value) + + @property + def max_width(self) -> int: + return len(self.value) + + +class PatternRE(Pattern): + __serialize_fields__ = 'value', 'flags', 'raw', '_width' + + type: ClassVar[str] = "re" + + def to_regexp(self) -> str: + return self._get_flags(self.value) + + _width = None + def _get_width(self): + if self._width is None: + self._width = get_regexp_width(self.to_regexp()) + return self._width + + @property + def min_width(self) -> int: + return self._get_width()[0] + + @property + def max_width(self) -> int: + return self._get_width()[1] + + +class TerminalDef(Serialize): + #-- + __serialize_fields__ = 'name', 'pattern', 'priority' + __serialize_namespace__ = PatternStr, PatternRE + + name: str + pattern: Pattern + priority: int + + def __init__(self, name: str, pattern: Pattern, priority: int = TOKEN_DEFAULT_PRIORITY) -> None: + assert isinstance(pattern, Pattern), pattern + self.name = name + self.pattern = pattern + self.priority = priority + + def __repr__(self): + return '%s(%r, %r)' % (type(self).__name__, self.name, self.pattern) + + def user_repr(self) -> str: + if self.name.startswith('__'): ## + + return self.pattern.raw or self.name + else: + return self.name + +_T = TypeVar('_T', bound="Token") + +class Token(str): + #-- + __slots__ = ('type', 'start_pos', 'value', 'line', 'column', 'end_line', 'end_column', 'end_pos') + + __match_args__ = ('type', 'value') + + type: str + start_pos: Optional[int] + value: Any + line: Optional[int] + column: Optional[int] + end_line: Optional[int] + end_column: Optional[int] + end_pos: Optional[int] + + + @overload + def __new__( + cls, + type: str, + value: Any, + start_pos: Optional[int] = None, + line: Optional[int] = None, + column: Optional[int] = None, + end_line: Optional[int] = None, + end_column: Optional[int] = None, + end_pos: Optional[int] = None + ) -> 'Token': + ... + + @overload + def __new__( + cls, + type_: str, + value: Any, + start_pos: Optional[int] = None, + line: Optional[int] = None, + column: Optional[int] = None, + end_line: Optional[int] = None, + end_column: Optional[int] = None, + end_pos: Optional[int] = None + ) -> 'Token': ... + + def __new__(cls, *args, **kwargs): + if "type_" in kwargs: + warnings.warn("`type_` is deprecated use `type` instead", DeprecationWarning) + + if "type" in kwargs: + raise TypeError("Error: using both 'type' and the deprecated 'type_' as arguments.") + kwargs["type"] = kwargs.pop("type_") + + return cls._future_new(*args, **kwargs) + + + @classmethod + def _future_new(cls, type, value, start_pos=None, line=None, column=None, end_line=None, end_column=None, end_pos=None): + inst = super(Token, cls).__new__(cls, value) + + inst.type = type + inst.start_pos = start_pos + inst.value = value + inst.line = line + inst.column = column + inst.end_line = end_line + inst.end_column = end_column + inst.end_pos = end_pos + return inst + + @overload + def update(self, type: Optional[str] = None, value: Optional[Any] = None) -> 'Token': + ... + + @overload + def update(self, type_: Optional[str] = None, value: Optional[Any] = None) -> 'Token': + ... + + def update(self, *args, **kwargs): + if "type_" in kwargs: + warnings.warn("`type_` is deprecated use `type` instead", DeprecationWarning) + + if "type" in kwargs: + raise TypeError("Error: using both 'type' and the deprecated 'type_' as arguments.") + kwargs["type"] = kwargs.pop("type_") + + return self._future_update(*args, **kwargs) + + def _future_update(self, type: Optional[str] = None, value: Optional[Any] = None) -> 'Token': + return Token.new_borrow_pos( + type if type is not None else self.type, + value if value is not None else self.value, + self + ) + + @classmethod + def new_borrow_pos(cls: Type[_T], type_: str, value: Any, borrow_t: 'Token') -> _T: + return cls(type_, value, borrow_t.start_pos, borrow_t.line, borrow_t.column, borrow_t.end_line, borrow_t.end_column, borrow_t.end_pos) + + def __reduce__(self): + return (self.__class__, (self.type, self.value, self.start_pos, self.line, self.column)) + + def __repr__(self): + return 'Token(%r, %r)' % (self.type, self.value) + + def __deepcopy__(self, memo): + return Token(self.type, self.value, self.start_pos, self.line, self.column) + + def __eq__(self, other): + if isinstance(other, Token) and self.type != other.type: + return False + + return str.__eq__(self, other) + + __hash__ = str.__hash__ + + +class LineCounter: + #-- + + __slots__ = 'char_pos', 'line', 'column', 'line_start_pos', 'newline_char' + + def __init__(self, newline_char): + self.newline_char = newline_char + self.char_pos = 0 + self.line = 1 + self.column = 1 + self.line_start_pos = 0 + + def __eq__(self, other): + if not isinstance(other, LineCounter): + return NotImplemented + + return self.char_pos == other.char_pos and self.newline_char == other.newline_char + + def feed(self, token: TextOrSlice, test_newline=True): + #-- + if test_newline: + newlines = token.count(self.newline_char) + if newlines: + self.line += newlines + self.line_start_pos = self.char_pos + token.rindex(self.newline_char) + 1 + + self.char_pos += len(token) + self.column = self.char_pos - self.line_start_pos + 1 + + +class UnlessCallback: + def __init__(self, scanner: 'Scanner'): + self.scanner = scanner + + def __call__(self, t: Token): + res = self.scanner.fullmatch(t.value) + if res is not None: + t.type = res + return t + + +class CallChain: + def __init__(self, callback1, callback2, cond): + self.callback1 = callback1 + self.callback2 = callback2 + self.cond = cond + + def __call__(self, t): + t2 = self.callback1(t) + return self.callback2(t) if self.cond(t2) else t2 + + +def _get_match(re_, regexp, s, flags): + m = re_.match(regexp, s, flags) + if m: + return m.group(0) + +def _create_unless(terminals, g_regex_flags, re_, use_bytes): + tokens_by_type = classify(terminals, lambda t: type(t.pattern)) + assert len(tokens_by_type) <= 2, tokens_by_type.keys() + embedded_strs = set() + callback = {} + for retok in tokens_by_type.get(PatternRE, []): + unless = [] + for strtok in tokens_by_type.get(PatternStr, []): + if strtok.priority != retok.priority: + continue + s = strtok.pattern.value + if s == _get_match(re_, retok.pattern.to_regexp(), s, g_regex_flags): + unless.append(strtok) + if strtok.pattern.flags <= retok.pattern.flags: + embedded_strs.add(strtok) + if unless: + callback[retok.name] = UnlessCallback(Scanner(unless, g_regex_flags, re_, use_bytes=use_bytes)) + + new_terminals = [t for t in terminals if t not in embedded_strs] + return new_terminals, callback + + +class Scanner: + def __init__(self, terminals, g_regex_flags, re_, use_bytes): + self.terminals = terminals + self.g_regex_flags = g_regex_flags + self.re_ = re_ + self.use_bytes = use_bytes + + self.allowed_types = {t.name for t in self.terminals} + + self._mres = self._build_mres(terminals, len(terminals)) + + def _build_mres(self, terminals, max_size): + ## + + ## + + ## + + mres = [] + while terminals: + pattern = u'|'.join(u'(?P<%s>%s)' % (t.name, t.pattern.to_regexp()) for t in terminals[:max_size]) + if self.use_bytes: + pattern = pattern.encode('latin-1') + try: + mre = self.re_.compile(pattern, self.g_regex_flags) + except AssertionError: ## + + return self._build_mres(terminals, max_size // 2) + + mres.append(mre) + terminals = terminals[max_size:] + return mres + + def match(self, text: TextSlice, pos): + for mre in self._mres: + m = mre.match(text.text, pos, text.end) + if m: + return m.group(0), m.lastgroup + + + def fullmatch(self, text: str) -> Optional[str]: + for mre in self._mres: + m = mre.fullmatch(text) + if m: + return m.lastgroup + return None + +def _regexp_has_newline(r: str): + #-- + return '\n' in r or '\\n' in r or '\\s' in r or '[^' in r or ('(?s' in r and '.' in r) + + +class LexerState: + #-- + + __slots__ = 'text', 'line_ctr', 'last_token' + + text: TextSlice + line_ctr: LineCounter + last_token: Optional[Token] + + def __init__(self, text: TextSlice, line_ctr: Optional[LineCounter] = None, last_token: Optional[Token]=None): + if isinstance(text, TextSlice): + if line_ctr is None: + line_ctr = LineCounter(b'\n' if isinstance(text.text, bytes) else '\n') + + if text.start > 0: + ## + + line_ctr.feed(TextSlice(text.text, 0, text.start)) + + if not (text.start <= line_ctr.char_pos <= text.end): + raise ValueError("LineCounter.char_pos is out of bounds") + + self.text = text + if line_ctr is None: + line_ctr = LineCounter(b'\n' if isinstance(text, bytes) or (hasattr(text, 'text') and isinstance(text.text, bytes)) else '\n') + self.line_ctr = line_ctr + self.last_token = last_token + + + def __eq__(self, other): + if not isinstance(other, LexerState): + return NotImplemented + + return self.text == other.text and self.line_ctr == other.line_ctr and self.last_token == other.last_token + + def __copy__(self): + return type(self)(self.text, copy(self.line_ctr), self.last_token) + + +class LexerThread: + #-- + + def __init__(self, lexer: 'Lexer', lexer_state: Optional[LexerState]): + self.lexer = lexer + self.state = lexer_state + + @classmethod + def from_text(cls, lexer: 'Lexer', text_or_slice: TextOrSlice) -> 'LexerThread': + text = TextSlice.cast_from(text_or_slice) + return cls(lexer, LexerState(text)) + + @classmethod + def from_custom_input(cls, lexer: 'Lexer', text: Any) -> 'LexerThread': + return cls(lexer, LexerState(text)) + + def lex(self, parser_state): + if self.state is None: + raise TypeError("Cannot lex: No text assigned to lexer state") + return self.lexer.lex(self.state, parser_state) + + def __copy__(self): + return type(self)(self.lexer, copy(self.state)) + + _Token = Token + + +_Callback = Callable[[Token], Token] + +class Lexer(ABC): + #-- + @abstractmethod + def lex(self, lexer_state: LexerState, parser_state: Any) -> Iterator[Token]: + return NotImplemented + + def make_lexer_state(self, text: str): + #-- + return LexerState(TextSlice.cast_from(text)) + + +def _check_regex_collisions(terminal_to_regexp: Dict[TerminalDef, str], comparator, strict_mode, max_collisions_to_show=8): + if not comparator: + comparator = interegular.Comparator.from_regexes(terminal_to_regexp) + + ## + + ## + + max_time = 2 if strict_mode else 0.2 + + ## + + if comparator.count_marked_pairs() >= max_collisions_to_show: + return + for group in classify(terminal_to_regexp, lambda t: t.priority).values(): + for a, b in comparator.check(group, skip_marked=True): + assert a.priority == b.priority + ## + + comparator.mark(a, b) + + ## + + message = f"Collision between Terminals {a.name} and {b.name}. " + try: + example = comparator.get_example_overlap(a, b, max_time).format_multiline() + except ValueError: + ## + + example = "No example could be found fast enough. However, the collision does still exists" + if strict_mode: + raise LexError(f"{message}\n{example}") + logger.warning("%s The lexer will choose between them arbitrarily.\n%s", message, example) + if comparator.count_marked_pairs() >= max_collisions_to_show: + logger.warning("Found 8 regex collisions, will not check for more.") + return + + +class AbstractBasicLexer(Lexer): + terminals_by_name: Dict[str, TerminalDef] + + @abstractmethod + def __init__(self, conf: 'LexerConf', comparator=None) -> None: + ... + + @abstractmethod + def next_token(self, lex_state: LexerState, parser_state: Any = None) -> Token: + ... + + def lex(self, state: LexerState, parser_state: Any) -> Iterator[Token]: + with suppress(EOFError): + while True: + yield self.next_token(state, parser_state) + + +class BasicLexer(AbstractBasicLexer): + terminals: Collection[TerminalDef] + ignore_types: FrozenSet[str] + newline_types: FrozenSet[str] + user_callbacks: Dict[str, _Callback] + callback: Dict[str, _Callback] + re: ModuleType + + def __init__(self, conf: 'LexerConf', comparator=None) -> None: + terminals = list(conf.terminals) + assert all(isinstance(t, TerminalDef) for t in terminals), terminals + + self.re = conf.re_module + + if not conf.skip_validation: + ## + + terminal_to_regexp = {} + for t in terminals: + regexp = t.pattern.to_regexp() + try: + self.re.compile(regexp, conf.g_regex_flags) + except self.re.error: + raise LexError("Cannot compile token %s: %s" % (t.name, t.pattern)) + + if t.pattern.min_width == 0: + raise LexError("Lexer does not allow zero-width terminals. (%s: %s)" % (t.name, t.pattern)) + if t.pattern.type == "re": + terminal_to_regexp[t] = regexp + + if not (set(conf.ignore) <= {t.name for t in terminals}): + raise LexError("Ignore terminals are not defined: %s" % (set(conf.ignore) - {t.name for t in terminals})) + + if has_interegular: + _check_regex_collisions(terminal_to_regexp, comparator, conf.strict) + elif conf.strict: + raise LexError("interegular must be installed for strict mode. Use `pip install 'lark[interegular]'`.") + + ## + + self.newline_types = frozenset(t.name for t in terminals if _regexp_has_newline(t.pattern.to_regexp())) + self.ignore_types = frozenset(conf.ignore) + + terminals.sort(key=lambda x: (-x.priority, -x.pattern.max_width, -len(x.pattern.value), x.name)) + self.terminals = terminals + self.user_callbacks = conf.callbacks + self.g_regex_flags = conf.g_regex_flags + self.use_bytes = conf.use_bytes + self.terminals_by_name = conf.terminals_by_name + + self._scanner: Optional[Scanner] = None + + def _build_scanner(self) -> Scanner: + terminals, self.callback = _create_unless(self.terminals, self.g_regex_flags, self.re, self.use_bytes) + assert all(self.callback.values()) + + for type_, f in self.user_callbacks.items(): + if type_ in self.callback: + ## + + self.callback[type_] = CallChain(self.callback[type_], f, lambda t: t.type == type_) + else: + self.callback[type_] = f + + return Scanner(terminals, self.g_regex_flags, self.re, self.use_bytes) + + @property + def scanner(self) -> Scanner: + if self._scanner is None: + self._scanner = self._build_scanner() + return self._scanner + + def match(self, text, pos): + return self.scanner.match(text, pos) + + def next_token(self, lex_state: LexerState, parser_state: Any = None) -> Token: + line_ctr = lex_state.line_ctr + while line_ctr.char_pos < lex_state.text.end: + res = self.match(lex_state.text, line_ctr.char_pos) + if not res: + allowed = self.scanner.allowed_types - self.ignore_types + if not allowed: + allowed = {""} + raise UnexpectedCharacters(lex_state.text.text, line_ctr.char_pos, line_ctr.line, line_ctr.column, + allowed=allowed, token_history=lex_state.last_token and [lex_state.last_token], + state=parser_state, terminals_by_name=self.terminals_by_name) + + value, type_ = res + + ignored = type_ in self.ignore_types + t = None + if not ignored or type_ in self.callback: + t = Token(type_, value, line_ctr.char_pos, line_ctr.line, line_ctr.column) + line_ctr.feed(value, type_ in self.newline_types) + if t is not None: + t.end_line = line_ctr.line + t.end_column = line_ctr.column + t.end_pos = line_ctr.char_pos + if t.type in self.callback: + t = self.callback[t.type](t) + if not ignored: + if not isinstance(t, Token): + raise LexError("Callbacks must return a token (returned %r)" % t) + lex_state.last_token = t + return t + + ## + + raise EOFError(self) + + +class ContextualLexer(Lexer): + lexers: Dict[int, AbstractBasicLexer] + root_lexer: AbstractBasicLexer + + BasicLexer: Type[AbstractBasicLexer] = BasicLexer + + def __init__(self, conf: 'LexerConf', states: Dict[int, Collection[str]], always_accept: Collection[str]=()) -> None: + terminals = list(conf.terminals) + terminals_by_name = conf.terminals_by_name + + trad_conf = copy(conf) + trad_conf.terminals = terminals + + if has_interegular and not conf.skip_validation: + comparator = interegular.Comparator.from_regexes({t: t.pattern.to_regexp() for t in terminals}) + else: + comparator = None + lexer_by_tokens: Dict[FrozenSet[str], AbstractBasicLexer] = {} + self.lexers = {} + for state, accepts in states.items(): + key = frozenset(accepts) + try: + lexer = lexer_by_tokens[key] + except KeyError: + accepts = set(accepts) | set(conf.ignore) | set(always_accept) + lexer_conf = copy(trad_conf) + lexer_conf.terminals = [terminals_by_name[n] for n in accepts if n in terminals_by_name] + lexer = self.BasicLexer(lexer_conf, comparator) + lexer_by_tokens[key] = lexer + + self.lexers[state] = lexer + + assert trad_conf.terminals is terminals + trad_conf.skip_validation = True ## + + self.root_lexer = self.BasicLexer(trad_conf, comparator) + + def lex(self, lexer_state: LexerState, parser_state: 'ParserState') -> Iterator[Token]: + try: + while True: + lexer = self.lexers[parser_state.position] + yield lexer.next_token(lexer_state, parser_state) + except EOFError: + pass + except UnexpectedCharacters as e: + ## + + ## + + try: + last_token = lexer_state.last_token ## + + token = self.root_lexer.next_token(lexer_state, parser_state) + raise UnexpectedToken(token, e.allowed, state=parser_state, token_history=[last_token], terminals_by_name=self.root_lexer.terminals_by_name) + except UnexpectedCharacters: + raise e ## + + + + +_ParserArgType: 'TypeAlias' = 'Literal["earley", "lalr", "cyk", "auto"]' +_LexerArgType: 'TypeAlias' = 'Union[Literal["auto", "basic", "contextual", "dynamic", "dynamic_complete"], Type[Lexer]]' +_LexerCallback = Callable[[Token], Token] +ParserCallbacks = Dict[str, Callable] + +class LexerConf(Serialize): + __serialize_fields__ = 'terminals', 'ignore', 'g_regex_flags', 'use_bytes', 'lexer_type' + __serialize_namespace__ = TerminalDef, + + terminals: Collection[TerminalDef] + re_module: ModuleType + ignore: Collection[str] + postlex: 'Optional[PostLex]' + callbacks: Dict[str, _LexerCallback] + g_regex_flags: int + skip_validation: bool + use_bytes: bool + lexer_type: Optional[_LexerArgType] + strict: bool + + def __init__(self, terminals: Collection[TerminalDef], re_module: ModuleType, ignore: Collection[str]=(), postlex: 'Optional[PostLex]'=None, + callbacks: Optional[Dict[str, _LexerCallback]]=None, g_regex_flags: int=0, skip_validation: bool=False, use_bytes: bool=False, strict: bool=False): + self.terminals = terminals + self.terminals_by_name = {t.name: t for t in self.terminals} + assert len(self.terminals) == len(self.terminals_by_name) + self.ignore = ignore + self.postlex = postlex + self.callbacks = callbacks or {} + self.g_regex_flags = g_regex_flags + self.re_module = re_module + self.skip_validation = skip_validation + self.use_bytes = use_bytes + self.strict = strict + self.lexer_type = None + + def _deserialize(self): + self.terminals_by_name = {t.name: t for t in self.terminals} + + def __deepcopy__(self, memo=None): + return type(self)( + deepcopy(self.terminals, memo), + self.re_module, + deepcopy(self.ignore, memo), + deepcopy(self.postlex, memo), + deepcopy(self.callbacks, memo), + deepcopy(self.g_regex_flags, memo), + deepcopy(self.skip_validation, memo), + deepcopy(self.use_bytes, memo), + ) + +class ParserConf(Serialize): + __serialize_fields__ = 'rules', 'start', 'parser_type' + + rules: List['Rule'] + callbacks: ParserCallbacks + start: List[str] + parser_type: _ParserArgType + + def __init__(self, rules: List['Rule'], callbacks: ParserCallbacks, start: List[str]): + assert isinstance(start, list) + self.rules = rules + self.callbacks = callbacks + self.start = start + + +from functools import partial, wraps +from itertools import product + + +class ExpandSingleChild: + def __init__(self, node_builder): + self.node_builder = node_builder + + def __call__(self, children): + if len(children) == 1: + return children[0] + else: + return self.node_builder(children) + + + +class PropagatePositions: + def __init__(self, node_builder, node_filter=None): + self.node_builder = node_builder + self.node_filter = node_filter + + def __call__(self, children): + res = self.node_builder(children) + + if isinstance(res, Tree): + ## + + ## + + ## + + ## + + + res_meta = res.meta + + first_meta = self._pp_get_meta(children) + if first_meta is not None: + if not hasattr(res_meta, 'line'): + ## + + res_meta.line = getattr(first_meta, 'container_line', first_meta.line) + res_meta.column = getattr(first_meta, 'container_column', first_meta.column) + res_meta.start_pos = getattr(first_meta, 'container_start_pos', first_meta.start_pos) + res_meta.empty = False + + res_meta.container_line = getattr(first_meta, 'container_line', first_meta.line) + res_meta.container_column = getattr(first_meta, 'container_column', first_meta.column) + res_meta.container_start_pos = getattr(first_meta, 'container_start_pos', first_meta.start_pos) + + last_meta = self._pp_get_meta(reversed(children)) + if last_meta is not None: + if not hasattr(res_meta, 'end_line'): + res_meta.end_line = getattr(last_meta, 'container_end_line', last_meta.end_line) + res_meta.end_column = getattr(last_meta, 'container_end_column', last_meta.end_column) + res_meta.end_pos = getattr(last_meta, 'container_end_pos', last_meta.end_pos) + res_meta.empty = False + + res_meta.container_end_line = getattr(last_meta, 'container_end_line', last_meta.end_line) + res_meta.container_end_column = getattr(last_meta, 'container_end_column', last_meta.end_column) + res_meta.container_end_pos = getattr(last_meta, 'container_end_pos', last_meta.end_pos) + + return res + + def _pp_get_meta(self, children): + for c in children: + if self.node_filter is not None and not self.node_filter(c): + continue + if isinstance(c, Tree): + if not c.meta.empty: + return c.meta + elif isinstance(c, Token): + return c + elif hasattr(c, '__lark_meta__'): + return c.__lark_meta__() + +def make_propagate_positions(option): + if callable(option): + return partial(PropagatePositions, node_filter=option) + elif option is True: + return PropagatePositions + elif option is False: + return None + + raise ConfigurationError('Invalid option for propagate_positions: %r' % option) + + +class ChildFilter: + def __init__(self, to_include, append_none, node_builder): + self.node_builder = node_builder + self.to_include = to_include + self.append_none = append_none + + def __call__(self, children): + filtered = [] + + for i, to_expand, add_none in self.to_include: + if add_none: + filtered += [None] * add_none + if to_expand: + filtered += children[i].children + else: + filtered.append(children[i]) + + if self.append_none: + filtered += [None] * self.append_none + + return self.node_builder(filtered) + + +class ChildFilterLALR(ChildFilter): + #-- + + def __call__(self, children): + filtered = [] + for i, to_expand, add_none in self.to_include: + if add_none: + filtered += [None] * add_none + if to_expand: + if filtered: + filtered += children[i].children + else: ## + + filtered = children[i].children + else: + filtered.append(children[i]) + + if self.append_none: + filtered += [None] * self.append_none + + return self.node_builder(filtered) + + +class ChildFilterLALR_NoPlaceholders(ChildFilter): + #-- + def __init__(self, to_include, node_builder): + self.node_builder = node_builder + self.to_include = to_include + + def __call__(self, children): + filtered = [] + for i, to_expand in self.to_include: + if to_expand: + if filtered: + filtered += children[i].children + else: ## + + filtered = children[i].children + else: + filtered.append(children[i]) + return self.node_builder(filtered) + + +def _should_expand(sym): + return not sym.is_term and sym.name.startswith('_') + + +def maybe_create_child_filter(expansion, keep_all_tokens, ambiguous, _empty_indices: List[bool]): + ## + + if _empty_indices: + assert _empty_indices.count(False) == len(expansion) + s = ''.join(str(int(b)) for b in _empty_indices) + empty_indices = [len(ones) for ones in s.split('0')] + assert len(empty_indices) == len(expansion)+1, (empty_indices, len(expansion)) + else: + empty_indices = [0] * (len(expansion)+1) + + to_include = [] + nones_to_add = 0 + for i, sym in enumerate(expansion): + nones_to_add += empty_indices[i] + if keep_all_tokens or not (sym.is_term and sym.filter_out): + to_include.append((i, _should_expand(sym), nones_to_add)) + nones_to_add = 0 + + nones_to_add += empty_indices[len(expansion)] + + if _empty_indices or len(to_include) < len(expansion) or any(to_expand for i, to_expand,_ in to_include): + if _empty_indices or ambiguous: + return partial(ChildFilter if ambiguous else ChildFilterLALR, to_include, nones_to_add) + else: + ## + + return partial(ChildFilterLALR_NoPlaceholders, [(i, x) for i,x,_ in to_include]) + + +class AmbiguousExpander: + #-- + def __init__(self, to_expand, tree_class, node_builder): + self.node_builder = node_builder + self.tree_class = tree_class + self.to_expand = to_expand + + def __call__(self, children): + def _is_ambig_tree(t): + return hasattr(t, 'data') and t.data == '_ambig' + + ## + + ## + + ## + + ## + + ambiguous = [] + for i, child in enumerate(children): + if _is_ambig_tree(child): + if i in self.to_expand: + ambiguous.append(i) + + child.expand_kids_by_data('_ambig') + + if not ambiguous: + return self.node_builder(children) + + expand = [child.children if i in ambiguous else (child,) for i, child in enumerate(children)] + return self.tree_class('_ambig', [self.node_builder(list(f)) for f in product(*expand)]) + + +def maybe_create_ambiguous_expander(tree_class, expansion, keep_all_tokens): + to_expand = [i for i, sym in enumerate(expansion) + if keep_all_tokens or ((not (sym.is_term and sym.filter_out)) and _should_expand(sym))] + if to_expand: + return partial(AmbiguousExpander, to_expand, tree_class) + + +class AmbiguousIntermediateExpander: + #-- + + def __init__(self, tree_class, node_builder): + self.node_builder = node_builder + self.tree_class = tree_class + + def __call__(self, children): + def _is_iambig_tree(child): + return hasattr(child, 'data') and child.data == '_iambig' + + def _collapse_iambig(children): + #-- + + ## + + ## + + if children and _is_iambig_tree(children[0]): + iambig_node = children[0] + result = [] + for grandchild in iambig_node.children: + collapsed = _collapse_iambig(grandchild.children) + if collapsed: + for child in collapsed: + child.children += children[1:] + result += collapsed + else: + new_tree = self.tree_class('_inter', grandchild.children + children[1:]) + result.append(new_tree) + return result + + collapsed = _collapse_iambig(children) + if collapsed: + processed_nodes = [self.node_builder(c.children) for c in collapsed] + return self.tree_class('_ambig', processed_nodes) + + return self.node_builder(children) + + + +def inplace_transformer(func): + @wraps(func) + def f(children): + ## + + tree = Tree(func.__name__, children) + return func(tree) + return f + + +def apply_visit_wrapper(func, name, wrapper): + if wrapper is _vargs_meta or wrapper is _vargs_meta_inline: + raise NotImplementedError("Meta args not supported for internal transformer; use YourTransformer().transform(parser.parse()) instead") + + @wraps(func) + def f(children): + return wrapper(func, name, children, None) + return f + + +class ParseTreeBuilder: + def __init__(self, rules, tree_class, propagate_positions=False, ambiguous=False, maybe_placeholders=False): + self.tree_class = tree_class + self.propagate_positions = propagate_positions + self.ambiguous = ambiguous + self.maybe_placeholders = maybe_placeholders + + self.rule_builders = list(self._init_builders(rules)) + + def _init_builders(self, rules): + propagate_positions = make_propagate_positions(self.propagate_positions) + + for rule in rules: + options = rule.options + keep_all_tokens = options.keep_all_tokens + expand_single_child = options.expand1 + + wrapper_chain = list(filter(None, [ + (expand_single_child and not rule.alias) and ExpandSingleChild, + maybe_create_child_filter(rule.expansion, keep_all_tokens, self.ambiguous, options.empty_indices if self.maybe_placeholders else None), + propagate_positions, + self.ambiguous and maybe_create_ambiguous_expander(self.tree_class, rule.expansion, keep_all_tokens), + self.ambiguous and partial(AmbiguousIntermediateExpander, self.tree_class) + ])) + + yield rule, wrapper_chain + + def create_callback(self, transformer=None): + callbacks = {} + + default_handler = getattr(transformer, '__default__', None) + if default_handler: + def default_callback(data, children): + return default_handler(data, children, None) + else: + default_callback = self.tree_class + + for rule, wrapper_chain in self.rule_builders: + + user_callback_name = rule.alias or rule.options.template_source or rule.origin.name + try: + f = getattr(transformer, user_callback_name) + wrapper = getattr(f, 'visit_wrapper', None) + if wrapper is not None: + f = apply_visit_wrapper(f, user_callback_name, wrapper) + elif isinstance(transformer, Transformer_InPlace): + f = inplace_transformer(f) + except AttributeError: + f = partial(default_callback, user_callback_name) + + for w in wrapper_chain: + f = w(f) + + if rule in callbacks: + raise GrammarError("Rule '%s' already exists" % (rule,)) + + callbacks[rule] = f + + return callbacks + + + +class Action: + def __init__(self, name): + self.name = name + def __str__(self): + return self.name + def __repr__(self): + return str(self) + +Shift = Action('Shift') +Reduce = Action('Reduce') + +StateT = TypeVar("StateT") + +class ParseTableBase(Generic[StateT]): + states: Dict[StateT, Dict[str, Tuple]] + start_states: Dict[str, StateT] + end_states: Dict[str, StateT] + + def __init__(self, states, start_states, end_states): + self.states = states + self.start_states = start_states + self.end_states = end_states + + def serialize(self, memo): + tokens = Enumerator() + + states = { + state: {tokens.get(token): ((1, arg.serialize(memo)) if action is Reduce else (0, arg)) + for token, (action, arg) in actions.items()} + for state, actions in self.states.items() + } + + return { + 'tokens': tokens.reversed(), + 'states': states, + 'start_states': self.start_states, + 'end_states': self.end_states, + } + + @classmethod + def deserialize(cls, data, memo): + tokens = data['tokens'] + states = { + state: {tokens[token]: ((Reduce, Rule.deserialize(arg, memo)) if action==1 else (Shift, arg)) + for token, (action, arg) in actions.items()} + for state, actions in data['states'].items() + } + return cls(states, data['start_states'], data['end_states']) + +class ParseTable(ParseTableBase['State']): + #-- + pass + + +class IntParseTable(ParseTableBase[int]): + #-- + + @classmethod + def from_ParseTable(cls, parse_table: ParseTable): + enum = list(parse_table.states) + state_to_idx: Dict['State', int] = {s:i for i,s in enumerate(enum)} + int_states = {} + + for s, la in parse_table.states.items(): + la = {k:(v[0], state_to_idx[v[1]]) if v[0] is Shift else v + for k,v in la.items()} + int_states[ state_to_idx[s] ] = la + + + start_states = {start:state_to_idx[s] for start, s in parse_table.start_states.items()} + end_states = {start:state_to_idx[s] for start, s in parse_table.end_states.items()} + return cls(int_states, start_states, end_states) + + + +class ParseConf(Generic[StateT]): + __slots__ = 'parse_table', 'callbacks', 'start', 'start_state', 'end_state', 'states' + + parse_table: ParseTableBase[StateT] + callbacks: ParserCallbacks + start: str + + start_state: StateT + end_state: StateT + states: Dict[StateT, Dict[str, tuple]] + + def __init__(self, parse_table: ParseTableBase[StateT], callbacks: ParserCallbacks, start: str): + self.parse_table = parse_table + + self.start_state = self.parse_table.start_states[start] + self.end_state = self.parse_table.end_states[start] + self.states = self.parse_table.states + + self.callbacks = callbacks + self.start = start + +class ParserState(Generic[StateT]): + __slots__ = 'parse_conf', 'lexer', 'state_stack', 'value_stack' + + parse_conf: ParseConf[StateT] + lexer: LexerThread + state_stack: List[StateT] + value_stack: list + + def __init__(self, parse_conf: ParseConf[StateT], lexer: LexerThread, state_stack=None, value_stack=None): + self.parse_conf = parse_conf + self.lexer = lexer + self.state_stack = state_stack or [self.parse_conf.start_state] + self.value_stack = value_stack or [] + + @property + def position(self) -> StateT: + return self.state_stack[-1] + + ## + + def __eq__(self, other) -> bool: + if not isinstance(other, ParserState): + return NotImplemented + return len(self.state_stack) == len(other.state_stack) and self.position == other.position + + def __copy__(self): + return self.copy() + + def copy(self, deepcopy_values=True) -> 'ParserState[StateT]': + return type(self)( + self.parse_conf, + self.lexer, ## + + copy(self.state_stack), + deepcopy(self.value_stack) if deepcopy_values else copy(self.value_stack), + ) + + def feed_token(self, token: Token, is_end=False) -> Any: + state_stack = self.state_stack + value_stack = self.value_stack + states = self.parse_conf.states + end_state = self.parse_conf.end_state + callbacks = self.parse_conf.callbacks + + while True: + state = state_stack[-1] + try: + action, arg = states[state][token.type] + except KeyError: + expected = {s for s in states[state].keys() if s.isupper()} + raise UnexpectedToken(token, expected, state=self, interactive_parser=None) + + assert arg != end_state + + if action is Shift: + ## + + assert not is_end + state_stack.append(arg) + value_stack.append(token if token.type not in callbacks else callbacks[token.type](token)) + return + else: + ## + + rule = arg + size = len(rule.expansion) + if size: + s = value_stack[-size:] + del state_stack[-size:] + del value_stack[-size:] + else: + s = [] + + value = callbacks[rule](s) if callbacks else s + + _action, new_state = states[state_stack[-1]][rule.origin.name] + assert _action is Shift + state_stack.append(new_state) + value_stack.append(value) + + if is_end and state_stack[-1] == end_state: + return value_stack[-1] + + +class LALR_Parser(Serialize): + def __init__(self, parser_conf: ParserConf, debug: bool=False, strict: bool=False): + analysis = LALR_Analyzer(parser_conf, debug=debug, strict=strict) + analysis.compute_lalr() + callbacks = parser_conf.callbacks + + self._parse_table = analysis.parse_table + self.parser_conf = parser_conf + self.parser = _Parser(analysis.parse_table, callbacks, debug) + + @classmethod + def deserialize(cls, data, memo, callbacks, debug=False): + inst = cls.__new__(cls) + inst._parse_table = IntParseTable.deserialize(data, memo) + inst.parser = _Parser(inst._parse_table, callbacks, debug) + return inst + + def serialize(self, memo: Any = None) -> Dict[str, Any]: + return self._parse_table.serialize(memo) + + def parse_interactive(self, lexer: LexerThread, start: str): + return self.parser.parse(lexer, start, start_interactive=True) + + def parse(self, lexer, start, on_error=None): + try: + return self.parser.parse(lexer, start) + except UnexpectedInput as e: + if on_error is None: + raise + + while True: + if isinstance(e, UnexpectedCharacters): + s = e.interactive_parser.lexer_thread.state + p = s.line_ctr.char_pos + + if not on_error(e): + raise e + + if isinstance(e, UnexpectedCharacters): + ## + + if p == s.line_ctr.char_pos: + s.line_ctr.feed(s.text.text[p:p+1]) + + try: + return e.interactive_parser.resume_parse() + except UnexpectedToken as e2: + if (isinstance(e, UnexpectedToken) + and e.token.type == e2.token.type == '$END' + and e.interactive_parser == e2.interactive_parser): + ## + + raise e2 + e = e2 + except UnexpectedCharacters as e2: + e = e2 + + +class _Parser: + parse_table: ParseTableBase + callbacks: ParserCallbacks + debug: bool + + def __init__(self, parse_table: ParseTableBase, callbacks: ParserCallbacks, debug: bool=False): + self.parse_table = parse_table + self.callbacks = callbacks + self.debug = debug + + def parse(self, lexer: LexerThread, start: str, value_stack=None, state_stack=None, start_interactive=False): + parse_conf = ParseConf(self.parse_table, self.callbacks, start) + parser_state = ParserState(parse_conf, lexer, state_stack, value_stack) + if start_interactive: + return InteractiveParser(self, parser_state, parser_state.lexer) + return self.parse_from_state(parser_state) + + + def parse_from_state(self, state: ParserState, last_token: Optional[Token]=None): + #-- + try: + token = last_token + for token in state.lexer.lex(state): + assert token is not None + state.feed_token(token) + + end_token = Token.new_borrow_pos('$END', '', token) if token else Token('$END', '', 0, 1, 1) + return state.feed_token(end_token, True) + except UnexpectedInput as e: + try: + e.interactive_parser = InteractiveParser(self, state, state.lexer) + except NameError: + pass + raise e + except Exception as e: + if self.debug: + print("") + print("STATE STACK DUMP") + print("----------------") + for i, s in enumerate(state.state_stack): + print('%d)' % i , s) + print("") + + raise + + +class InteractiveParser: + #-- + def __init__(self, parser, parser_state: ParserState, lexer_thread: LexerThread): + self.parser = parser + self.parser_state = parser_state + self.lexer_thread = lexer_thread + self.result = None + + @property + def lexer_state(self) -> LexerThread: + warnings.warn("lexer_state will be removed in subsequent releases. Use lexer_thread instead.", DeprecationWarning) + return self.lexer_thread + + def feed_token(self, token: Token): + #-- + return self.parser_state.feed_token(token, token.type == '$END') + + def iter_parse(self) -> Iterator[Token]: + #-- + for token in self.lexer_thread.lex(self.parser_state): + yield token + self.result = self.feed_token(token) + + def exhaust_lexer(self) -> List[Token]: + #-- + return list(self.iter_parse()) + + + def feed_eof(self, last_token=None): + #-- + eof = Token.new_borrow_pos('$END', '', last_token) if last_token is not None else self.lexer_thread._Token('$END', '', 0, 1, 1) + return self.feed_token(eof) + + + def __copy__(self): + #-- + return self.copy() + + def copy(self, deepcopy_values=True): + return type(self)( + self.parser, + self.parser_state.copy(deepcopy_values=deepcopy_values), + copy(self.lexer_thread), + ) + + def __eq__(self, other): + if not isinstance(other, InteractiveParser): + return False + + return self.parser_state == other.parser_state and self.lexer_thread == other.lexer_thread + + def as_immutable(self): + #-- + p = copy(self) + return ImmutableInteractiveParser(p.parser, p.parser_state, p.lexer_thread) + + def pretty(self): + #-- + out = ["Parser choices:"] + for k, v in self.choices().items(): + out.append('\t- %s -> %r' % (k, v)) + out.append('stack size: %s' % len(self.parser_state.state_stack)) + return '\n'.join(out) + + def choices(self): + #-- + return self.parser_state.parse_conf.parse_table.states[self.parser_state.position] + + def accepts(self): + #-- + accepts = set() + conf_no_callbacks = copy(self.parser_state.parse_conf) + ## + + ## + + conf_no_callbacks.callbacks = {} + for t in self.choices(): + if t.isupper(): ## + + new_cursor = self.copy(deepcopy_values=False) + new_cursor.parser_state.parse_conf = conf_no_callbacks + try: + new_cursor.feed_token(self.lexer_thread._Token(t, '')) + except UnexpectedToken: + pass + else: + accepts.add(t) + return accepts + + def resume_parse(self): + #-- + return self.parser.parse_from_state(self.parser_state, last_token=self.lexer_thread.state.last_token) + + + +class ImmutableInteractiveParser(InteractiveParser): + #-- + + result = None + + def __hash__(self): + return hash((self.parser_state, self.lexer_thread)) + + def feed_token(self, token): + c = copy(self) + c.result = InteractiveParser.feed_token(c, token) + return c + + def exhaust_lexer(self): + #-- + cursor = self.as_mutable() + cursor.exhaust_lexer() + return cursor.as_immutable() + + def as_mutable(self): + #-- + p = copy(self) + return InteractiveParser(p.parser, p.parser_state, p.lexer_thread) + + + +def _wrap_lexer(lexer_class): + future_interface = getattr(lexer_class, '__future_interface__', 0) + if future_interface == 2: + return lexer_class + elif future_interface == 1: + class CustomLexerWrapper1(Lexer): + def __init__(self, lexer_conf): + self.lexer = lexer_class(lexer_conf) + def lex(self, lexer_state, parser_state): + if isinstance(lexer_state.text, TextSlice) and not lexer_state.text.is_complete_text(): + raise TypeError("Interface=1 Custom Lexer don't support TextSlice") + lexer_state.text = lexer_state.text + return self.lexer.lex(lexer_state, parser_state) + return CustomLexerWrapper1 + elif future_interface == 0: + class CustomLexerWrapper0(Lexer): + def __init__(self, lexer_conf): + self.lexer = lexer_class(lexer_conf) + + def lex(self, lexer_state, parser_state): + if isinstance(lexer_state.text, TextSlice): + if not lexer_state.text.is_complete_text(): + raise TypeError("Interface=0 Custom Lexer don't support TextSlice") + return self.lexer.lex(lexer_state.text.text) + return self.lexer.lex(lexer_state.text) + return CustomLexerWrapper0 + else: + raise ValueError(f"Unknown __future_interface__ value {future_interface}, integer 0-2 expected") + + +def _deserialize_parsing_frontend(data, memo, lexer_conf, callbacks, options): + parser_conf = ParserConf.deserialize(data['parser_conf'], memo) + cls = (options and options._plugins.get('LALR_Parser')) or LALR_Parser + parser = cls.deserialize(data['parser'], memo, callbacks, options.debug) + parser_conf.callbacks = callbacks + return ParsingFrontend(lexer_conf, parser_conf, options, parser=parser) + + +_parser_creators: 'Dict[str, Callable[[LexerConf, Any, Any], Any]]' = {} + + +class ParsingFrontend(Serialize): + __serialize_fields__ = 'lexer_conf', 'parser_conf', 'parser' + + lexer_conf: LexerConf + parser_conf: ParserConf + options: Any + + def __init__(self, lexer_conf: LexerConf, parser_conf: ParserConf, options, parser=None): + self.parser_conf = parser_conf + self.lexer_conf = lexer_conf + self.options = options + + ## + + if parser: ## + + self.parser = parser + else: + create_parser = _parser_creators.get(parser_conf.parser_type) + assert create_parser is not None, "{} is not supported in standalone mode".format( + parser_conf.parser_type + ) + self.parser = create_parser(lexer_conf, parser_conf, options) + + ## + + lexer_type = options.lexer if (options and options.lexer) else lexer_conf.lexer_type + self.skip_lexer = False + if lexer_type in ('dynamic', 'dynamic_complete'): + assert lexer_conf.postlex is None + self.skip_lexer = True + return + + if isinstance(lexer_type, type): + assert issubclass(lexer_type, Lexer) or hasattr(lexer_type, 'lex') + self.lexer = _wrap_lexer(lexer_type)(lexer_conf) + elif isinstance(lexer_type, str): + create_lexer = { + 'basic': create_basic_lexer, + 'contextual': create_contextual_lexer, + }[lexer_type] + self.lexer = create_lexer(lexer_conf, self.parser, lexer_conf.postlex, options) + else: + raise TypeError("Bad value for lexer_type: {lexer_type}") + + if lexer_conf.postlex: + self.lexer = PostLexConnector(self.lexer, lexer_conf.postlex) + + def _verify_start(self, start=None): + if start is None: + start_decls = self.parser_conf.start + if len(start_decls) > 1: + raise ConfigurationError("Lark initialized with more than 1 possible start rule. Must specify which start rule to parse", start_decls) + start ,= start_decls + elif start not in self.parser_conf.start: + raise ConfigurationError("Unknown start rule %s. Must be one of %r" % (start, self.parser_conf.start)) + return start + + def _make_lexer_thread(self, text: Optional[LarkInput]) -> Union[LarkInput, LexerThread, None]: + cls = (self.options and self.options._plugins.get('LexerThread')) or LexerThread + if self.skip_lexer: + return text + if text is None: + return cls(self.lexer, None) + if isinstance(text, (str, bytes, TextSlice)): + return cls.from_text(self.lexer, text) + return cls.from_custom_input(self.lexer, text) + + def parse(self, text: Optional[LarkInput], start=None, on_error=None): + if self.lexer_conf.lexer_type in ("dynamic", "dynamic_complete"): + if isinstance(text, TextSlice) and not text.is_complete_text(): + raise TypeError(f"Lexer {self.lexer_conf.lexer_type} does not support text slices.") + + chosen_start = self._verify_start(start) + kw = {} if on_error is None else {'on_error': on_error} + stream = self._make_lexer_thread(text) + return self.parser.parse(stream, chosen_start, **kw) + + def parse_interactive(self, text: Optional[TextOrSlice]=None, start=None): + ## + + ## + + chosen_start = self._verify_start(start) + if self.parser_conf.parser_type != 'lalr': + raise ConfigurationError("parse_interactive() currently only works with parser='lalr' ") + stream = self._make_lexer_thread(text) + return self.parser.parse_interactive(stream, chosen_start) + + +def _validate_frontend_args(parser, lexer) -> None: + assert_config(parser, ('lalr', 'earley', 'cyk')) + if not isinstance(lexer, type): ## + + expected = { + 'lalr': ('basic', 'contextual'), + 'earley': ('basic', 'dynamic', 'dynamic_complete'), + 'cyk': ('basic', ), + }[parser] + assert_config(lexer, expected, 'Parser %r does not support lexer %%r, expected one of %%s' % parser) + + +def _get_lexer_callbacks(transformer, terminals): + result = {} + for terminal in terminals: + callback = getattr(transformer, terminal.name, None) + if callback is not None: + result[terminal.name] = callback + return result + +class PostLexConnector: + def __init__(self, lexer, postlexer): + self.lexer = lexer + self.postlexer = postlexer + + def lex(self, lexer_state, parser_state): + i = self.lexer.lex(lexer_state, parser_state) + return self.postlexer.process(i) + + + +def create_basic_lexer(lexer_conf, parser, postlex, options) -> BasicLexer: + cls = (options and options._plugins.get('BasicLexer')) or BasicLexer + return cls(lexer_conf) + +def create_contextual_lexer(lexer_conf: LexerConf, parser, postlex, options) -> ContextualLexer: + cls = (options and options._plugins.get('ContextualLexer')) or ContextualLexer + parse_table: ParseTableBase[int] = parser._parse_table + states: Dict[int, Collection[str]] = {idx:list(t.keys()) for idx, t in parse_table.states.items()} + always_accept: Collection[str] = postlex.always_accept if postlex else () + return cls(lexer_conf, states, always_accept=always_accept) + +def create_lalr_parser(lexer_conf: LexerConf, parser_conf: ParserConf, options=None) -> LALR_Parser: + debug = options.debug if options else False + strict = options.strict if options else False + cls = (options and options._plugins.get('LALR_Parser')) or LALR_Parser + return cls(parser_conf, debug=debug, strict=strict) + +_parser_creators['lalr'] = create_lalr_parser + + + + +class PostLex(ABC): + @abstractmethod + def process(self, stream: Iterator[Token]) -> Iterator[Token]: + return stream + + always_accept: Iterable[str] = () + +class LarkOptions(Serialize): + #-- + + start: List[str] + debug: bool + strict: bool + transformer: 'Optional[Transformer]' + propagate_positions: Union[bool, str] + maybe_placeholders: bool + cache: Union[bool, str] + cache_grammar: bool + regex: bool + g_regex_flags: int + keep_all_tokens: bool + tree_class: Optional[Callable[[str, List], Any]] + parser: _ParserArgType + lexer: _LexerArgType + ambiguity: 'Literal["auto", "resolve", "explicit", "forest"]' + postlex: Optional[PostLex] + priority: 'Optional[Literal["auto", "normal", "invert"]]' + lexer_callbacks: Dict[str, Callable[[Token], Token]] + use_bytes: bool + ordered_sets: bool + edit_terminals: Optional[Callable[[TerminalDef], TerminalDef]] + import_paths: 'List[Union[str, Callable[[Union[None, str, PackageResource], str], Tuple[str, str]]]]' + source_path: Optional[str] + + OPTIONS_DOC = r""" + **=== General Options ===** + + start + The start symbol. Either a string, or a list of strings for multiple possible starts (Default: "start") + debug + Display debug information and extra warnings. Use only when debugging (Default: ``False``) + When used with Earley, it generates a forest graph as "sppf.png", if 'dot' is installed. + strict + Throw an exception on any potential ambiguity, including shift/reduce conflicts, and regex collisions. + transformer + Applies the transformer to every parse tree (equivalent to applying it after the parse, but faster) + propagate_positions + Propagates positional attributes into the 'meta' attribute of all tree branches. + Sets attributes: (line, column, end_line, end_column, start_pos, end_pos, + container_line, container_column, container_end_line, container_end_column) + Accepts ``False``, ``True``, or a callable, which will filter which nodes to ignore when propagating. + maybe_placeholders + When ``True``, the ``[]`` operator returns ``None`` when not matched. + When ``False``, ``[]`` behaves like the ``?`` operator, and returns no value at all. + (default= ``True``) + cache + Cache the results of the Lark grammar analysis, for x2 to x3 faster loading. LALR only for now. + + - When ``False``, does nothing (default) + - When ``True``, caches to a temporary file in the local directory + - When given a string, caches to the path pointed by the string + cache_grammar + For use with ``cache`` option. When ``True``, the unanalyzed grammar is also included in the cache. + Useful for classes that require the ``Lark.grammar`` to be present (e.g. Reconstructor). + (default= ``False``) + regex + When True, uses the ``regex`` module instead of the stdlib ``re``. + g_regex_flags + Flags that are applied to all terminals (both regex and strings) + keep_all_tokens + Prevent the tree builder from automagically removing "punctuation" tokens (Default: ``False``) + tree_class + Lark will produce trees comprised of instances of this class instead of the default ``lark.Tree``. + + **=== Algorithm Options ===** + + parser + Decides which parser engine to use. Accepts "earley" or "lalr". (Default: "earley"). + (there is also a "cyk" option for legacy) + lexer + Decides whether or not to use a lexer stage + + - "auto" (default): Choose for me based on the parser + - "basic": Use a basic lexer + - "contextual": Stronger lexer (only works with parser="lalr") + - "dynamic": Flexible and powerful (only with parser="earley") + - "dynamic_complete": Same as dynamic, but tries *every* variation of tokenizing possible. + ambiguity + Decides how to handle ambiguity in the parse. Only relevant if parser="earley" + + - "resolve": The parser will automatically choose the simplest derivation + (it chooses consistently: greedy for tokens, non-greedy for rules) + - "explicit": The parser will return all derivations wrapped in "_ambig" tree nodes (i.e. a forest). + - "forest": The parser will return the root of the shared packed parse forest. + + **=== Misc. / Domain Specific Options ===** + + postlex + Lexer post-processing (Default: ``None``) Only works with the basic and contextual lexers. + priority + How priorities should be evaluated - "auto", ``None``, "normal", "invert" (Default: "auto") + lexer_callbacks + Dictionary of callbacks for the lexer. May alter tokens during lexing. Use with caution. + use_bytes + Accept an input of type ``bytes`` instead of ``str``. + ordered_sets + Should Earley use ordered-sets to achieve stable output (~10% slower than regular sets. Default: True) + edit_terminals + A callback for editing the terminals before parse. + import_paths + A List of either paths or loader functions to specify from where grammars are imported + source_path + Override the source of from where the grammar was loaded. Useful for relative imports and unconventional grammar loading + **=== End of Options ===** + """ + if __doc__: + __doc__ += OPTIONS_DOC + + + ## + + ## + + ## + + ## + + ## + + ## + + _defaults: Dict[str, Any] = { + 'debug': False, + 'strict': False, + 'keep_all_tokens': False, + 'tree_class': None, + 'cache': False, + 'cache_grammar': False, + 'postlex': None, + 'parser': 'earley', + 'lexer': 'auto', + 'transformer': None, + 'start': 'start', + 'priority': 'auto', + 'ambiguity': 'auto', + 'regex': False, + 'propagate_positions': False, + 'lexer_callbacks': {}, + 'maybe_placeholders': True, + 'edit_terminals': None, + 'g_regex_flags': 0, + 'use_bytes': False, + 'ordered_sets': True, + 'import_paths': [], + 'source_path': None, + '_plugins': {}, + } + + def __init__(self, options_dict: Dict[str, Any]) -> None: + o = dict(options_dict) + + options = {} + for name, default in self._defaults.items(): + if name in o: + value = o.pop(name) + if isinstance(default, bool) and name not in ('cache', 'use_bytes', 'propagate_positions'): + value = bool(value) + else: + value = default + + options[name] = value + + if isinstance(options['start'], str): + options['start'] = [options['start']] + + self.__dict__['options'] = options + + + assert_config(self.parser, ('earley', 'lalr', 'cyk', None)) + + if self.parser == 'earley' and self.transformer: + raise ConfigurationError('Cannot specify an embedded transformer when using the Earley algorithm. ' + 'Please use your transformer on the resulting parse tree, or use a different algorithm (i.e. LALR)') + + if self.cache_grammar and not self.cache: + raise ConfigurationError('cache_grammar cannot be set when cache is disabled') + + if o: + raise ConfigurationError("Unknown options: %s" % o.keys()) + + def __getattr__(self, name: str) -> Any: + try: + return self.__dict__['options'][name] + except KeyError as e: + raise AttributeError(e) + + def __setattr__(self, name: str, value: str) -> None: + assert_config(name, self.options.keys(), "%r isn't a valid option. Expected one of: %s") + self.options[name] = value + + def serialize(self, memo = None) -> Dict[str, Any]: + return self.options + + @classmethod + def deserialize(cls, data: Dict[str, Any], memo: Dict[int, Union[TerminalDef, Rule]]) -> "LarkOptions": + return cls(data) + + +## + +## + +_LOAD_ALLOWED_OPTIONS = {'lexer', 'postlex', 'transformer', 'lexer_callbacks', 'use_bytes', 'debug', 'g_regex_flags', 'regex', 'propagate_positions', 'tree_class', '_plugins'} + +_VALID_PRIORITY_OPTIONS = ('auto', 'normal', 'invert', None) +_VALID_AMBIGUITY_OPTIONS = ('auto', 'resolve', 'explicit', 'forest') + + +_T = TypeVar('_T', bound="Lark") + +class Lark(Serialize): + #-- + + source_path: str + source_grammar: str + grammar: 'Grammar' + options: LarkOptions + lexer: Lexer + parser: 'ParsingFrontend' + terminals: Collection[TerminalDef] + + __serialize_fields__ = ['parser', 'rules', 'options'] + + def __init__(self, grammar: 'Union[Grammar, str, IO[str]]', **options) -> None: + self.options = LarkOptions(options) + re_module: types.ModuleType + + ## + + if self.options.cache_grammar: + self.__serialize_fields__ = self.__serialize_fields__ + ['grammar'] + + ## + + use_regex = self.options.regex + if use_regex: + if _has_regex: + re_module = regex + else: + raise ImportError('`regex` module must be installed if calling `Lark(regex=True)`.') + else: + re_module = re + + ## + + if self.options.source_path is None: + try: + self.source_path = grammar.name ## + + except AttributeError: + self.source_path = '' + else: + self.source_path = self.options.source_path + + ## + + try: + read = grammar.read ## + + except AttributeError: + pass + else: + grammar = read() + + cache_fn = None + cache_sha256 = None + if isinstance(grammar, str): + self.source_grammar = grammar + if self.options.use_bytes: + if not grammar.isascii(): + raise ConfigurationError("Grammar must be ascii only, when use_bytes=True") + + if self.options.cache: + if self.options.parser != 'lalr': + raise ConfigurationError("cache only works with parser='lalr' for now") + + unhashable = ('transformer', 'postlex', 'lexer_callbacks', 'edit_terminals', '_plugins') + options_str = ''.join(k+str(v) for k, v in options.items() if k not in unhashable) + from . import __version__ + s = grammar + options_str + __version__ + str(sys.version_info[:2]) + cache_sha256 = sha256_digest(s) + + if isinstance(self.options.cache, str): + cache_fn = self.options.cache + else: + if self.options.cache is not True: + raise ConfigurationError("cache argument must be bool or str") + + try: + username = getpass.getuser() + except Exception: + ## + + ## + + ## + + username = "unknown" + + + cache_fn = tempfile.gettempdir() + "/.lark_%s_%s_%s_%s_%s.tmp" % ( + "cache_grammar" if self.options.cache_grammar else "cache", username, cache_sha256, *sys.version_info[:2]) + + old_options = self.options + try: + with FS.open(cache_fn, 'rb') as f: + logger.debug('Loading grammar from cache: %s', cache_fn) + ## + + for name in (set(options) - _LOAD_ALLOWED_OPTIONS): + del options[name] + file_sha256 = f.readline().rstrip(b'\n') + cached_used_files = pickle.load(f) + if file_sha256 == cache_sha256.encode('utf8') and verify_used_files(cached_used_files): + cached_parser_data = pickle.load(f) + self._load(cached_parser_data, **options) + return + except FileNotFoundError: + ## + + pass + except Exception: ## + + logger.exception("Failed to load Lark from cache: %r. We will try to carry on.", cache_fn) + + ## + + ## + + self.options = old_options + + + ## + + self.grammar, used_files = load_grammar(grammar, self.source_path, self.options.import_paths, self.options.keep_all_tokens) + else: + assert isinstance(grammar, Grammar) + self.grammar = grammar + + + if self.options.lexer == 'auto': + if self.options.parser == 'lalr': + self.options.lexer = 'contextual' + elif self.options.parser == 'earley': + if self.options.postlex is not None: + logger.info("postlex can't be used with the dynamic lexer, so we use 'basic' instead. " + "Consider using lalr with contextual instead of earley") + self.options.lexer = 'basic' + else: + self.options.lexer = 'dynamic' + elif self.options.parser == 'cyk': + self.options.lexer = 'basic' + else: + assert False, self.options.parser + lexer = self.options.lexer + if isinstance(lexer, type): + assert issubclass(lexer, Lexer) ## + + else: + assert_config(lexer, ('basic', 'contextual', 'dynamic', 'dynamic_complete')) + if self.options.postlex is not None and 'dynamic' in lexer: + raise ConfigurationError("Can't use postlex with a dynamic lexer. Use basic or contextual instead") + + if self.options.ambiguity == 'auto': + if self.options.parser == 'earley': + self.options.ambiguity = 'resolve' + else: + assert_config(self.options.parser, ('earley', 'cyk'), "%r doesn't support disambiguation. Use one of these parsers instead: %s") + + if self.options.priority == 'auto': + self.options.priority = 'normal' + + if self.options.priority not in _VALID_PRIORITY_OPTIONS: + raise ConfigurationError("invalid priority option: %r. Must be one of %r" % (self.options.priority, _VALID_PRIORITY_OPTIONS)) + if self.options.ambiguity not in _VALID_AMBIGUITY_OPTIONS: + raise ConfigurationError("invalid ambiguity option: %r. Must be one of %r" % (self.options.ambiguity, _VALID_AMBIGUITY_OPTIONS)) + + if self.options.parser is None: + terminals_to_keep = '*' ## + + elif self.options.postlex is not None: + terminals_to_keep = set(self.options.postlex.always_accept) + else: + terminals_to_keep = set() + + ## + + self.terminals, self.rules, self.ignore_tokens = self.grammar.compile(self.options.start, terminals_to_keep) + + if self.options.edit_terminals: + for t in self.terminals: + self.options.edit_terminals(t) + + self._terminals_dict = {t.name: t for t in self.terminals} + + ## + + if self.options.priority == 'invert': + for rule in self.rules: + if rule.options.priority is not None: + rule.options.priority = -rule.options.priority + for term in self.terminals: + term.priority = -term.priority + ## + + ## + + ## + + elif self.options.priority is None: + for rule in self.rules: + if rule.options.priority is not None: + rule.options.priority = None + for term in self.terminals: + term.priority = 0 + + ## + + self.lexer_conf = LexerConf( + self.terminals, re_module, self.ignore_tokens, self.options.postlex, + self.options.lexer_callbacks, self.options.g_regex_flags, use_bytes=self.options.use_bytes, strict=self.options.strict + ) + + if self.options.parser: + self.parser = self._build_parser() + elif lexer: + self.lexer = self._build_lexer() + + if cache_fn: + logger.debug('Saving grammar to cache: %s', cache_fn) + try: + with FS.open(cache_fn, 'wb') as f: + assert cache_sha256 is not None + f.write(cache_sha256.encode('utf8') + b'\n') + pickle.dump(used_files, f) + self.save(f, _LOAD_ALLOWED_OPTIONS) + except IOError as e: + logger.exception("Failed to save Lark to cache: %r.", cache_fn, e) + + if __doc__: + __doc__ += "\n\n" + LarkOptions.OPTIONS_DOC + + def _build_lexer(self, dont_ignore: bool=False) -> BasicLexer: + lexer_conf = self.lexer_conf + if dont_ignore: + from copy import copy + lexer_conf = copy(lexer_conf) + lexer_conf.ignore = () + return BasicLexer(lexer_conf) + + def _prepare_callbacks(self) -> None: + self._callbacks = {} + ## + + if self.options.ambiguity != 'forest': + self._parse_tree_builder = ParseTreeBuilder( + self.rules, + self.options.tree_class or Tree, + self.options.propagate_positions, + self.options.parser != 'lalr' and self.options.ambiguity == 'explicit', + self.options.maybe_placeholders + ) + self._callbacks = self._parse_tree_builder.create_callback(self.options.transformer) + self._callbacks.update(_get_lexer_callbacks(self.options.transformer, self.terminals)) + + def _build_parser(self) -> "ParsingFrontend": + self._prepare_callbacks() + _validate_frontend_args(self.options.parser, self.options.lexer) + parser_conf = ParserConf(self.rules, self._callbacks, self.options.start) + return _construct_parsing_frontend( + self.options.parser, + self.options.lexer, + self.lexer_conf, + parser_conf, + options=self.options + ) + + def save(self, f, exclude_options: Collection[str] = ()) -> None: + #-- + if self.options.parser != 'lalr': + raise NotImplementedError("Lark.save() is only implemented for the LALR(1) parser.") + data, m = self.memo_serialize([TerminalDef, Rule]) + if exclude_options: + data["options"] = {n: v for n, v in data["options"].items() if n not in exclude_options} + pickle.dump({'data': data, 'memo': m}, f, protocol=pickle.HIGHEST_PROTOCOL) + + @classmethod + def load(cls: Type[_T], f) -> _T: + #-- + inst = cls.__new__(cls) + return inst._load(f) + + def _deserialize_lexer_conf(self, data: Dict[str, Any], memo: Dict[int, Union[TerminalDef, Rule]], options: LarkOptions) -> LexerConf: + lexer_conf = LexerConf.deserialize(data['lexer_conf'], memo) + lexer_conf.callbacks = options.lexer_callbacks or {} + lexer_conf.re_module = regex if options.regex else re + lexer_conf.use_bytes = options.use_bytes + lexer_conf.g_regex_flags = options.g_regex_flags + lexer_conf.skip_validation = True + lexer_conf.postlex = options.postlex + return lexer_conf + + def _load(self: _T, f: Any, **kwargs) -> _T: + if isinstance(f, dict): + d = f + else: + d = pickle.load(f) + memo_json = d['memo'] + data = d['data'] + + assert memo_json + memo = SerializeMemoizer.deserialize(memo_json, {'Rule': Rule, 'TerminalDef': TerminalDef}, {}) + if 'grammar' in data: + self.grammar = Grammar.deserialize(data['grammar'], memo) + options = dict(data['options']) + if (set(kwargs) - _LOAD_ALLOWED_OPTIONS) & set(LarkOptions._defaults): + raise ConfigurationError("Some options are not allowed when loading a Parser: {}" + .format(set(kwargs) - _LOAD_ALLOWED_OPTIONS)) + options.update(kwargs) + self.options = LarkOptions.deserialize(options, memo) + self.rules = [Rule.deserialize(r, memo) for r in data['rules']] + self.source_path = '' + _validate_frontend_args(self.options.parser, self.options.lexer) + self.lexer_conf = self._deserialize_lexer_conf(data['parser'], memo, self.options) + self.terminals = self.lexer_conf.terminals + self._prepare_callbacks() + self._terminals_dict = {t.name: t for t in self.terminals} + self.parser = _deserialize_parsing_frontend( + data['parser'], + memo, + self.lexer_conf, + self._callbacks, + self.options, ## + + ) + return self + + @classmethod + def _load_from_dict(cls, data, memo, **kwargs): + inst = cls.__new__(cls) + return inst._load({'data': data, 'memo': memo}, **kwargs) + + @classmethod + def open(cls: Type[_T], grammar_filename: str, rel_to: Optional[str]=None, **options) -> _T: + #-- + if rel_to: + basepath = os.path.dirname(rel_to) + grammar_filename = os.path.join(basepath, grammar_filename) + with open(grammar_filename, encoding='utf8') as f: + return cls(f, **options) + + @classmethod + def open_from_package(cls: Type[_T], package: str, grammar_path: str, search_paths: 'Sequence[str]'=[""], **options) -> _T: + #-- + package_loader = FromPackageLoader(package, search_paths) + full_path, text = package_loader(None, grammar_path) + options.setdefault('source_path', full_path) + options.setdefault('import_paths', []) + options['import_paths'].append(package_loader) + return cls(text, **options) + + def __repr__(self): + return 'Lark(open(%r), parser=%r, lexer=%r, ...)' % (self.source_path, self.options.parser, self.options.lexer) + + + def lex(self, text: TextOrSlice, dont_ignore: bool=False) -> Iterator[Token]: + #-- + lexer: Lexer + if not hasattr(self, 'lexer') or dont_ignore: + lexer = self._build_lexer(dont_ignore) + else: + lexer = self.lexer + lexer_thread = LexerThread.from_text(lexer, text) + stream = lexer_thread.lex(None) + if self.options.postlex: + return self.options.postlex.process(stream) + return stream + + def get_terminal(self, name: str) -> TerminalDef: + #-- + return self._terminals_dict[name] + + def parse_interactive(self, text: Optional[LarkInput]=None, start: Optional[str]=None) -> 'InteractiveParser': + #-- + return self.parser.parse_interactive(text, start=start) + + def parse(self, text: LarkInput, start: Optional[str]=None, on_error: 'Optional[Callable[[UnexpectedInput], bool]]'=None) -> 'ParseTree': + #-- + if on_error is not None and self.options.parser != 'lalr': + raise NotImplementedError("The on_error option is only implemented for the LALR(1) parser.") + return self.parser.parse(text, start=start, on_error=on_error) + + + + +class DedentError(LarkError): + pass + +class Indenter(PostLex, ABC): + #-- + paren_level: int + indent_level: List[int] + + def __init__(self) -> None: + self.paren_level = 0 + self.indent_level = [0] + assert self.tab_len > 0 + + def handle_NL(self, token: Token) -> Iterator[Token]: + if self.paren_level > 0: + return + + yield token + + indent_str = token.rsplit('\n', 1)[1] ## + + indent = indent_str.count(' ') + indent_str.count('\t') * self.tab_len + + if indent > self.indent_level[-1]: + self.indent_level.append(indent) + yield Token.new_borrow_pos(self.INDENT_type, indent_str, token) + else: + while indent < self.indent_level[-1]: + self.indent_level.pop() + yield Token.new_borrow_pos(self.DEDENT_type, indent_str, token) + + if indent != self.indent_level[-1]: + raise DedentError('Unexpected dedent to column %s. Expected dedent to %s' % (indent, self.indent_level[-1])) + + def _process(self, stream): + token = None + for token in stream: + if token.type == self.NL_type: + yield from self.handle_NL(token) + else: + yield token + + if token.type in self.OPEN_PAREN_types: + self.paren_level += 1 + elif token.type in self.CLOSE_PAREN_types: + self.paren_level -= 1 + assert self.paren_level >= 0 + + while len(self.indent_level) > 1: + self.indent_level.pop() + yield Token.new_borrow_pos(self.DEDENT_type, '', token) if token else Token(self.DEDENT_type, '', 0, 0, 0, 0, 0, 0) + + assert self.indent_level == [0], self.indent_level + + def process(self, stream): + self.paren_level = 0 + self.indent_level = [0] + return self._process(stream) + + ## + + @property + def always_accept(self): + return (self.NL_type,) + + @property + @abstractmethod + def NL_type(self) -> str: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def OPEN_PAREN_types(self) -> List[str]: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def CLOSE_PAREN_types(self) -> List[str]: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def INDENT_type(self) -> str: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def DEDENT_type(self) -> str: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def tab_len(self) -> int: + #-- + raise NotImplementedError() + + +class PythonIndenter(Indenter): + #-- + + NL_type = '_NEWLINE' + OPEN_PAREN_types = ['LPAR', 'LSQB', 'LBRACE'] + CLOSE_PAREN_types = ['RPAR', 'RSQB', 'RBRACE'] + INDENT_type = '_INDENT' + DEDENT_type = '_DEDENT' + tab_len = 8 + + +import pickle, zlib, base64 +DATA = ( +{'parser': {'lexer_conf': {'terminals': [], 'ignore': [], 'g_regex_flags': 0, 'use_bytes': False, 'lexer_type': 'contextual', '__type__': 'LexerConf'}, 'parser_conf': {'rules': [{'@': 0}, {'@': 1}, {'@': 2}, {'@': 3}, {'@': 4}, {'@': 5}, {'@': 6}, {'@': 7}, {'@': 8}, {'@': 9}, {'@': 10}, {'@': 11}, {'@': 12}, {'@': 13}, {'@': 14}, {'@': 15}, {'@': 16}, {'@': 17}, {'@': 18}, {'@': 19}, {'@': 20}, {'@': 21}, {'@': 22}, {'@': 23}, {'@': 24}, {'@': 25}, {'@': 26}, {'@': 27}, {'@': 28}, {'@': 29}, {'@': 30}, {'@': 31}, {'@': 32}, {'@': 33}, {'@': 34}, {'@': 35}, {'@': 36}, {'@': 37}, {'@': 38}, {'@': 39}, {'@': 40}, {'@': 41}, {'@': 42}, {'@': 43}, {'@': 44}, {'@': 45}, {'@': 46}, {'@': 47}, {'@': 48}, {'@': 49}, {'@': 50}, {'@': 51}, {'@': 52}, {'@': 53}, {'@': 54}, {'@': 55}, {'@': 56}, {'@': 57}, {'@': 58}, {'@': 59}, {'@': 60}, {'@': 61}, {'@': 62}, {'@': 63}, {'@': 64}, {'@': 65}, {'@': 66}, {'@': 67}, {'@': 68}, {'@': 69}, {'@': 70}, {'@': 71}, {'@': 72}, {'@': 73}, {'@': 74}, {'@': 75}, {'@': 76}, {'@': 77}, {'@': 78}, {'@': 79}, {'@': 80}, {'@': 81}, {'@': 82}, {'@': 83}, {'@': 84}, {'@': 85}, {'@': 86}, {'@': 87}, {'@': 88}, {'@': 89}, {'@': 90}, {'@': 91}, {'@': 92}, {'@': 93}, {'@': 94}, {'@': 95}, {'@': 96}, {'@': 97}, {'@': 98}, {'@': 99}, {'@': 100}, {'@': 101}, {'@': 102}, {'@': 103}, {'@': 104}, {'@': 105}, {'@': 106}, {'@': 107}, {'@': 108}, {'@': 109}, {'@': 110}, {'@': 111}, {'@': 112}, {'@': 113}, {'@': 114}, {'@': 115}, {'@': 116}, {'@': 117}, {'@': 118}, {'@': 119}, {'@': 120}, {'@': 121}, {'@': 122}, {'@': 123}, {'@': 124}, {'@': 125}, {'@': 126}, {'@': 127}, {'@': 128}, {'@': 129}, {'@': 130}, {'@': 131}, {'@': 132}, {'@': 133}, {'@': 134}, {'@': 135}, {'@': 136}, {'@': 137}, {'@': 138}, {'@': 139}, {'@': 140}, {'@': 141}, {'@': 142}, {'@': 143}, {'@': 144}, {'@': 145}, {'@': 146}, {'@': 147}, {'@': 148}, {'@': 149}, {'@': 150}, {'@': 151}, {'@': 152}, {'@': 153}, {'@': 154}, {'@': 155}, {'@': 156}, {'@': 157}, {'@': 158}, {'@': 159}, {'@': 160}, {'@': 161}, {'@': 162}, {'@': 163}, {'@': 164}, {'@': 165}, {'@': 166}, {'@': 167}, {'@': 168}, {'@': 169}, {'@': 170}, {'@': 171}, {'@': 172}, {'@': 173}, {'@': 174}, {'@': 175}, {'@': 176}, {'@': 177}, {'@': 178}, {'@': 179}, {'@': 180}, {'@': 181}, {'@': 182}, {'@': 183}, {'@': 184}, {'@': 185}, {'@': 186}, {'@': 187}, {'@': 188}, {'@': 189}, {'@': 190}, {'@': 191}, {'@': 192}, {'@': 193}, {'@': 194}, {'@': 195}, {'@': 196}, {'@': 197}, {'@': 198}, {'@': 199}, {'@': 200}, {'@': 201}, {'@': 202}, {'@': 203}, {'@': 204}, {'@': 205}, {'@': 206}, {'@': 207}, {'@': 208}, {'@': 209}, {'@': 210}, {'@': 211}, {'@': 212}, {'@': 213}, {'@': 214}, {'@': 215}, {'@': 216}, {'@': 217}, {'@': 218}, {'@': 219}, {'@': 220}, {'@': 221}, {'@': 222}, {'@': 223}, {'@': 224}, {'@': 225}, {'@': 226}, {'@': 227}, {'@': 228}, {'@': 229}, {'@': 230}, {'@': 231}, {'@': 232}, {'@': 233}, {'@': 234}, {'@': 235}, {'@': 236}, {'@': 237}, {'@': 238}, {'@': 239}, {'@': 240}, {'@': 241}, {'@': 242}, {'@': 243}, {'@': 244}, {'@': 245}, {'@': 246}, {'@': 247}, {'@': 248}, {'@': 249}, {'@': 250}, {'@': 251}, {'@': 252}, {'@': 253}, {'@': 254}, {'@': 255}, {'@': 256}, {'@': 257}, {'@': 258}, {'@': 259}, {'@': 260}, {'@': 261}, {'@': 262}, {'@': 263}, {'@': 264}, {'@': 265}, {'@': 266}, {'@': 267}, {'@': 268}, {'@': 269}, {'@': 270}, {'@': 271}, {'@': 272}, {'@': 273}, {'@': 274}, {'@': 275}, {'@': 276}, {'@': 277}, {'@': 278}, {'@': 279}, {'@': 280}, {'@': 281}, {'@': 282}, {'@': 283}, {'@': 284}, {'@': 285}, {'@': 286}, {'@': 287}, {'@': 288}, {'@': 289}, {'@': 290}, {'@': 291}, {'@': 292}, {'@': 293}, {'@': 294}, {'@': 295}, {'@': 296}, {'@': 297}, {'@': 298}, {'@': 299}, {'@': 300}, {'@': 301}, {'@': 302}, {'@': 303}, {'@': 304}, {'@': 305}, {'@': 306}, {'@': 307}, {'@': 308}, {'@': 309}, {'@': 310}, {'@': 311}, {'@': 312}, {'@': 313}, {'@': 314}, {'@': 315}, {'@': 316}, {'@': 317}, {'@': 318}, {'@': 319}, {'@': 320}, {'@': 321}, {'@': 322}, {'@': 323}, {'@': 324}, {'@': 325}, {'@': 326}, {'@': 327}, {'@': 328}, {'@': 329}, {'@': 330}, {'@': 331}, {'@': 332}, {'@': 333}, {'@': 334}, {'@': 335}, {'@': 336}, {'@': 337}, {'@': 338}, {'@': 339}, {'@': 340}, {'@': 341}, {'@': 342}, {'@': 343}, {'@': 344}, {'@': 345}, {'@': 346}, {'@': 347}, {'@': 348}, {'@': 349}, {'@': 350}, {'@': 351}, {'@': 352}, {'@': 353}, {'@': 354}, {'@': 355}, {'@': 356}, {'@': 357}, {'@': 358}, {'@': 359}, {'@': 360}, {'@': 361}, {'@': 362}, {'@': 363}, {'@': 364}, {'@': 365}, {'@': 366}, {'@': 367}, {'@': 368}, {'@': 369}, {'@': 370}, {'@': 371}, {'@': 372}, {'@': 373}, {'@': 374}, {'@': 375}, {'@': 376}, {'@': 377}, {'@': 378}, {'@': 379}, {'@': 380}, {'@': 381}, {'@': 382}, {'@': 383}, {'@': 384}, {'@': 385}, {'@': 386}, {'@': 387}, {'@': 388}, {'@': 389}, {'@': 390}, {'@': 391}, {'@': 392}, {'@': 393}, {'@': 394}, {'@': 395}, {'@': 396}, {'@': 397}, {'@': 398}, {'@': 399}, {'@': 400}, {'@': 401}, {'@': 402}, {'@': 403}, {'@': 404}, {'@': 405}, {'@': 406}, {'@': 407}, {'@': 408}, {'@': 409}, {'@': 410}, {'@': 411}, {'@': 412}, {'@': 413}, {'@': 414}, {'@': 415}, {'@': 416}, {'@': 417}], 'start': ['start'], 'parser_type': 'lalr', '__type__': 'ParserConf'}, 'parser': {'tokens': {0: 'param_def', 1: 'ARRAY_ID', 2: 'singleid', 3: 'ID', 4: 'EQ', 5: 'COMMA', 6: 'RBRACE', 7: 'AS', 8: 'typedef', 9: 'LP', 10: 'RP', 11: 'CO', 12: 'ELSE', 13: 'NEWLINE', 14: 'SIZEOF', 15: 'EXP', 16: 'PEEK', 17: 'INKEY', 18: 'IN', 19: 'LBOUND', 20: 'SQR', 21: 'STR', 22: 'LEN', 23: 'LN', 24: 'ATN', 25: 'USR', 26: 'ABS', 27: 'UBOUND', 28: 'VAL', 29: 'RND', 30: 'CHR', 31: 'ADDRESSOF', 32: 'CODE', 33: 'NUMBER', 34: 'TAN', 35: 'INT', 36: 'PI', 37: 'COS', 38: 'SIN', 39: 'SGN', 40: 'ASN', 41: 'STRC', 42: 'ACS', 43: 'SC', 44: 'END_WHILE', 45: 'WEND', 46: 'BNOT', 47: 'MINUS', 48: 'NOT', 49: 'CAST', 50: 'string', 51: 'math_fn', 52: 'func_call', 53: 'bexpr', 54: 'TO', 55: 'BXOR', 56: 'AND', 57: 'XOR', 58: 'LE', 59: 'NE', 60: 'SHL', 61: 'DIV', 62: 'LT', 63: 'OR', 64: 'GT', 65: 'BAND', 66: 'PLUS', 67: 'BOR', 68: 'SHR', 69: 'GE', 70: 'MUL', 71: 'MOD', 72: 'RESTORE', 73: 'THEN', 74: '_PRAGMA', 75: '_REQUIRE', 76: 'VERIFY', 77: 'DECLARE', 78: 'FUNCTION', 79: 'GOSUB', 80: 'END', 81: 'DATA', 82: 'OVER', 83: 'PRINT', 84: 'ON', 85: 'CONST', 86: 'POKE', 87: 'ITALIC', 88: 'GO', 89: 'CLS', 90: 'STEP', 91: 'CONTINUE', 92: 'DIM', 93: 'DRAW', 94: 'RETURN', 95: 'INVERSE', 96: 'GOTO', 97: 'CIRCLE', 98: 'EXIT', 99: 'BOLD', 100: 'FLASH', 101: 'INK', 102: 'PAUSE', 103: 'DO', 104: 'SAVE', 105: 'BEEP', 106: 'BORDER', 107: 'IF', 108: 'BRIGHT', 109: 'LET', 110: 'PAPER', 111: 'STOP', 112: 'LOAD', 113: '_INIT', 114: 'RANDOMIZE', 115: 'ASM', 116: 'FOR', 117: 'ERROR', 118: 'PLOT', 119: 'READ', 120: 'LABEL', 121: 'WHILE', 122: 'LOOP', 123: 'OUT', 124: 'SUB', 125: 'POW', 126: 'expr_bxor', 127: 'expr_xor', 128: 'expr_and', 129: 'expr_mul', 130: 'expr_mod', 131: 'expr', 132: 'expr_cmp', 133: 'expr_add', 134: 'expr_or', 135: 'expr_unary', 136: 'expr_pow', 137: 'expr_cast', 138: 'expr_bor', 139: 'expr_not', 140: 'param_decl_list', 141: 'param_definition', 142: 'BYVAL', 143: 'BYREF', 144: 'bound', 145: 'load_or_verify', 146: 'var_decl', 147: 'do_start', 148: 'if_then_part', 149: 'if_inline', 150: 'statement', 151: 'do_while_start', 152: 'lexpr', 153: 'while_start', 154: 'var_arr_decl_addr', 155: 'function_def', 156: 'function_header_pre', 157: 'do_until_start', 158: 'goto', 159: 'var_arr_decl', 160: 'function_header', 161: 'for_start', 162: 'function_declaration', 163: 'END_SUB', 164: 'END_IF', 165: 'ENDIF', 166: 'ELSEIF', 167: 'END_FUNCTION', 168: 'NEXT', 169: 'RIGHTARROW', 170: 'AT', 171: 'arg_list', 172: 'substr', 173: 'UNTIL', 174: 'label_loop', 175: 'label', 176: 'INTEGER', 177: 'numbertype', 178: 'FLOAT', 179: 'UINTEGER', 180: 'BYTE', 181: 'ULONG', 182: 'LONG', 183: 'UBYTE', 184: 'FIXED', 185: 'endif', 186: 'then', 187: 'else_part_inline', 188: 'elseif_expr', 189: 'elseiflist', 190: 'else_part', 191: 'statements_co', 192: 'co_statements_co', 193: 'co_statements', 194: 'statements', 195: '$END', 196: 'arguments', 197: 'argument', 198: 'default_arg_value', 199: 'preproc_line', 200: 'program_line', 201: 'label_line', 202: 'label_line_co', 203: 'step', 204: 'bound_list', 205: 'LBRACE', 206: 'const_vector', 207: 'start', 208: 'program', 209: 'STRING', 210: 'type', 211: 'const_vector_list', 212: 'const_number_list', 213: 'program_co', 214: 'FASTCALL', 215: 'STDCALL', 216: 'convention', 217: 'WEQ', 218: 'attr_list', 219: 'attr', 220: 'label_end_while', 221: 'label_list', 222: 'print_elem', 223: 'print_at', 224: 'TAB', 225: 'print_tab', 226: '_PUSH', 227: '_POP', 228: 'idlist', 229: 'label_next', 230: 'param_decl', 231: 'print_list', 232: 'function_body'}, 'states': {0: {0: (0, 236), 1: (0, 185), 2: (0, 4), 3: (0, 264)}, 1: {4: (0, 187)}, 2: {5: (1, {'@': 174}), 6: (1, {'@': 174})}, 3: {2: (0, 576), 1: (0, 132), 3: (0, 749)}, 4: {7: (0, 377), 8: (0, 207), 9: (0, 321), 5: (1, {'@': 352}), 10: (1, {'@': 352}), 4: (1, {'@': 352})}, 5: {11: (1, {'@': 217}), 12: (1, {'@': 217}), 13: (1, {'@': 217})}, 6: {14: (1, {'@': 373}), 15: (1, {'@': 373}), 16: (1, {'@': 373}), 17: (1, {'@': 373}), 18: (1, {'@': 373}), 19: (1, {'@': 373}), 20: (1, {'@': 373}), 21: (1, {'@': 373}), 22: (1, {'@': 373}), 23: (1, {'@': 373}), 24: (1, {'@': 373}), 25: (1, {'@': 373}), 1: (1, {'@': 373}), 26: (1, {'@': 373}), 27: (1, {'@': 373}), 28: (1, {'@': 373}), 29: (1, {'@': 373}), 30: (1, {'@': 373}), 3: (1, {'@': 373}), 31: (1, {'@': 373}), 9: (1, {'@': 373}), 32: (1, {'@': 373}), 33: (1, {'@': 373}), 34: (1, {'@': 373}), 35: (1, {'@': 373}), 36: (1, {'@': 373}), 37: (1, {'@': 373}), 38: (1, {'@': 373}), 39: (1, {'@': 373}), 40: (1, {'@': 373}), 41: (1, {'@': 373}), 42: (1, {'@': 373})}, 7: {5: (1, {'@': 233}), 11: (1, {'@': 233}), 12: (1, {'@': 233}), 43: (1, {'@': 233}), 13: (1, {'@': 233})}, 8: {44: (0, 245), 45: (0, 102)}, 9: {14: (1, {'@': 262}), 15: (1, {'@': 262}), 16: (1, {'@': 262}), 17: (1, {'@': 262}), 18: (1, {'@': 262}), 19: (1, {'@': 262}), 20: (1, {'@': 262}), 21: (1, {'@': 262}), 22: (1, {'@': 262}), 23: (1, {'@': 262}), 24: (1, {'@': 262}), 25: (1, {'@': 262}), 1: (1, {'@': 262}), 26: (1, {'@': 262}), 27: (1, {'@': 262}), 28: (1, {'@': 262}), 46: (1, {'@': 262}), 29: (1, {'@': 262}), 30: (1, {'@': 262}), 3: (1, {'@': 262}), 31: (1, {'@': 262}), 9: (1, {'@': 262}), 32: (1, {'@': 262}), 34: (1, {'@': 262}), 33: (1, {'@': 262}), 5: (1, {'@': 262}), 35: (1, {'@': 262}), 36: (1, {'@': 262}), 37: (1, {'@': 262}), 47: (1, {'@': 262}), 38: (1, {'@': 262}), 48: (1, {'@': 262}), 39: (1, {'@': 262}), 49: (1, {'@': 262}), 40: (1, {'@': 262}), 41: (1, {'@': 262}), 42: (1, {'@': 262})}, 10: {11: (1, {'@': 88}), 13: (1, {'@': 88}), 12: (1, {'@': 88})}, 11: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 23: (0, 219), 29: (0, 474), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 24: (0, 408), 40: (0, 503), 26: (0, 405), 53: (0, 709), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 12: {11: (1, {'@': 231}), 12: (1, {'@': 231}), 13: (1, {'@': 231})}, 13: {10: (0, 181), 54: (0, 88)}, 14: {55: (1, {'@': 409}), 56: (1, {'@': 409}), 57: (1, {'@': 409}), 58: (1, {'@': 409}), 59: (1, {'@': 409}), 60: (1, {'@': 409}), 61: (1, {'@': 409}), 47: (1, {'@': 409}), 62: (1, {'@': 409}), 63: (1, {'@': 409}), 64: (1, {'@': 409}), 65: (1, {'@': 409}), 66: (1, {'@': 409}), 67: (1, {'@': 409}), 68: (1, {'@': 409}), 54: (1, {'@': 409}), 69: (1, {'@': 409}), 70: (1, {'@': 409}), 4: (1, {'@': 409}), 71: (1, {'@': 409}), 12: (1, {'@': 409}), 13: (1, {'@': 409}), 11: (1, {'@': 409}), 72: (1, {'@': 409}), 73: (1, {'@': 409}), 74: (1, {'@': 409}), 75: (1, {'@': 409}), 76: (1, {'@': 409}), 77: (1, {'@': 409}), 78: (1, {'@': 409}), 79: (1, {'@': 409}), 80: (1, {'@': 409}), 81: (1, {'@': 409}), 10: (1, {'@': 409}), 82: (1, {'@': 409}), 83: (1, {'@': 409}), 84: (1, {'@': 409}), 85: (1, {'@': 409}), 86: (1, {'@': 409}), 87: (1, {'@': 409}), 88: (1, {'@': 409}), 89: (1, {'@': 409}), 90: (1, {'@': 409}), 91: (1, {'@': 409}), 92: (1, {'@': 409}), 93: (1, {'@': 409}), 94: (1, {'@': 409}), 95: (1, {'@': 409}), 96: (1, {'@': 409}), 97: (1, {'@': 409}), 98: (1, {'@': 409}), 99: (1, {'@': 409}), 100: (1, {'@': 409}), 101: (1, {'@': 409}), 102: (1, {'@': 409}), 43: (1, {'@': 409}), 103: (1, {'@': 409}), 104: (1, {'@': 409}), 105: (1, {'@': 409}), 106: (1, {'@': 409}), 107: (1, {'@': 409}), 108: (1, {'@': 409}), 109: (1, {'@': 409}), 1: (1, {'@': 409}), 110: (1, {'@': 409}), 111: (1, {'@': 409}), 112: (1, {'@': 409}), 113: (1, {'@': 409}), 114: (1, {'@': 409}), 3: (1, {'@': 409}), 32: (1, {'@': 409}), 115: (1, {'@': 409}), 5: (1, {'@': 409}), 116: (1, {'@': 409}), 117: (1, {'@': 409}), 118: (1, {'@': 409}), 119: (1, {'@': 409}), 120: (1, {'@': 409}), 121: (1, {'@': 409}), 6: (1, {'@': 409}), 122: (1, {'@': 409}), 123: (1, {'@': 409}), 124: (1, {'@': 409})}, 15: {72: (1, {'@': 269}), 56: (1, {'@': 269}), 78: (1, {'@': 269}), 80: (1, {'@': 269}), 81: (1, {'@': 269}), 68: (1, {'@': 269}), 84: (1, {'@': 269}), 69: (1, {'@': 269}), 86: (1, {'@': 269}), 71: (1, {'@': 269}), 87: (1, {'@': 269}), 89: (1, {'@': 269}), 12: (1, {'@': 269}), 93: (1, {'@': 269}), 60: (1, {'@': 269}), 63: (1, {'@': 269}), 62: (1, {'@': 269}), 97: (1, {'@': 269}), 99: (1, {'@': 269}), 100: (1, {'@': 269}), 102: (1, {'@': 269}), 43: (1, {'@': 269}), 103: (1, {'@': 269}), 105: (1, {'@': 269}), 107: (1, {'@': 269}), 108: (1, {'@': 269}), 112: (1, {'@': 269}), 70: (1, {'@': 269}), 64: (1, {'@': 269}), 67: (1, {'@': 269}), 3: (1, {'@': 269}), 9: (1, {'@': 269}), 115: (1, {'@': 269}), 116: (1, {'@': 269}), 5: (1, {'@': 269}), 125: (1, {'@': 269}), 59: (1, {'@': 269}), 120: (1, {'@': 269}), 65: (1, {'@': 269}), 6: (1, {'@': 269}), 54: (1, {'@': 269}), 123: (1, {'@': 269}), 73: (1, {'@': 269}), 74: (1, {'@': 269}), 75: (1, {'@': 269}), 76: (1, {'@': 269}), 77: (1, {'@': 269}), 79: (1, {'@': 269}), 61: (1, {'@': 269}), 82: (1, {'@': 269}), 10: (1, {'@': 269}), 66: (1, {'@': 269}), 83: (1, {'@': 269}), 85: (1, {'@': 269}), 11: (1, {'@': 269}), 88: (1, {'@': 269}), 90: (1, {'@': 269}), 91: (1, {'@': 269}), 92: (1, {'@': 269}), 94: (1, {'@': 269}), 47: (1, {'@': 269}), 95: (1, {'@': 269}), 96: (1, {'@': 269}), 4: (1, {'@': 269}), 55: (1, {'@': 269}), 98: (1, {'@': 269}), 101: (1, {'@': 269}), 13: (1, {'@': 269}), 104: (1, {'@': 269}), 106: (1, {'@': 269}), 58: (1, {'@': 269}), 109: (1, {'@': 269}), 1: (1, {'@': 269}), 110: (1, {'@': 269}), 111: (1, {'@': 269}), 113: (1, {'@': 269}), 114: (1, {'@': 269}), 32: (1, {'@': 269}), 57: (1, {'@': 269}), 117: (1, {'@': 269}), 118: (1, {'@': 269}), 119: (1, {'@': 269}), 121: (1, {'@': 269}), 122: (1, {'@': 269}), 124: (1, {'@': 269})}, 16: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 131: (0, 327), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 17: {11: (1, {'@': 41}), 13: (1, {'@': 41}), 12: (1, {'@': 41})}, 18: {11: (1, {'@': 45}), 13: (1, {'@': 45}), 12: (1, {'@': 45})}, 19: {1: (0, 185), 2: (0, 4), 10: (0, 387), 140: (0, 435), 141: (0, 425), 142: (0, 0), 0: (0, 355), 3: (0, 264), 143: (0, 374)}, 20: {72: (1, {'@': 284}), 56: (1, {'@': 284}), 78: (1, {'@': 284}), 80: (1, {'@': 284}), 81: (1, {'@': 284}), 68: (1, {'@': 284}), 84: (1, {'@': 284}), 69: (1, {'@': 284}), 86: (1, {'@': 284}), 71: (1, {'@': 284}), 87: (1, {'@': 284}), 89: (1, {'@': 284}), 12: (1, {'@': 284}), 93: (1, {'@': 284}), 60: (1, {'@': 284}), 63: (1, {'@': 284}), 62: (1, {'@': 284}), 97: (1, {'@': 284}), 99: (1, {'@': 284}), 100: (1, {'@': 284}), 102: (1, {'@': 284}), 43: (1, {'@': 284}), 103: (1, {'@': 284}), 105: (1, {'@': 284}), 107: (1, {'@': 284}), 108: (1, {'@': 284}), 112: (1, {'@': 284}), 70: (1, {'@': 284}), 64: (1, {'@': 284}), 67: (1, {'@': 284}), 3: (1, {'@': 284}), 9: (1, {'@': 284}), 115: (1, {'@': 284}), 116: (1, {'@': 284}), 5: (1, {'@': 284}), 125: (1, {'@': 284}), 59: (1, {'@': 284}), 120: (1, {'@': 284}), 65: (1, {'@': 284}), 6: (1, {'@': 284}), 54: (1, {'@': 284}), 123: (1, {'@': 284}), 73: (1, {'@': 284}), 74: (1, {'@': 284}), 75: (1, {'@': 284}), 76: (1, {'@': 284}), 77: (1, {'@': 284}), 79: (1, {'@': 284}), 61: (1, {'@': 284}), 82: (1, {'@': 284}), 10: (1, {'@': 284}), 66: (1, {'@': 284}), 83: (1, {'@': 284}), 85: (1, {'@': 284}), 11: (1, {'@': 284}), 88: (1, {'@': 284}), 90: (1, {'@': 284}), 91: (1, {'@': 284}), 92: (1, {'@': 284}), 94: (1, {'@': 284}), 47: (1, {'@': 284}), 95: (1, {'@': 284}), 96: (1, {'@': 284}), 4: (1, {'@': 284}), 55: (1, {'@': 284}), 98: (1, {'@': 284}), 101: (1, {'@': 284}), 13: (1, {'@': 284}), 104: (1, {'@': 284}), 106: (1, {'@': 284}), 58: (1, {'@': 284}), 109: (1, {'@': 284}), 1: (1, {'@': 284}), 110: (1, {'@': 284}), 111: (1, {'@': 284}), 113: (1, {'@': 284}), 114: (1, {'@': 284}), 32: (1, {'@': 284}), 57: (1, {'@': 284}), 117: (1, {'@': 284}), 118: (1, {'@': 284}), 119: (1, {'@': 284}), 121: (1, {'@': 284}), 122: (1, {'@': 284}), 124: (1, {'@': 284})}, 21: {11: (1, {'@': 43}), 13: (1, {'@': 43}), 12: (1, {'@': 43})}, 22: {11: (1, {'@': 57}), 13: (1, {'@': 57}), 12: (1, {'@': 57})}, 23: {7: (0, 377), 8: (0, 224), 5: (1, {'@': 352}), 10: (1, {'@': 352})}, 24: {14: (1, {'@': 178}), 15: (1, {'@': 178}), 16: (1, {'@': 178}), 17: (1, {'@': 178}), 18: (1, {'@': 178}), 19: (1, {'@': 178}), 20: (1, {'@': 178}), 21: (1, {'@': 178}), 22: (1, {'@': 178}), 23: (1, {'@': 178}), 24: (1, {'@': 178}), 25: (1, {'@': 178}), 1: (1, {'@': 178}), 26: (1, {'@': 178}), 27: (1, {'@': 178}), 28: (1, {'@': 178}), 46: (1, {'@': 178}), 29: (1, {'@': 178}), 30: (1, {'@': 178}), 3: (1, {'@': 178}), 31: (1, {'@': 178}), 9: (1, {'@': 178}), 32: (1, {'@': 178}), 34: (1, {'@': 178}), 33: (1, {'@': 178}), 35: (1, {'@': 178}), 36: (1, {'@': 178}), 37: (1, {'@': 178}), 47: (1, {'@': 178}), 38: (1, {'@': 178}), 48: (1, {'@': 178}), 39: (1, {'@': 178}), 49: (1, {'@': 178}), 40: (1, {'@': 178}), 41: (1, {'@': 178}), 42: (1, {'@': 178})}, 25: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 23: (0, 219), 29: (0, 474), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 24: (0, 408), 40: (0, 503), 26: (0, 405), 53: (0, 738), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 26: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 131: (0, 263), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 27: {72: (1, {'@': 300}), 56: (1, {'@': 300}), 78: (1, {'@': 300}), 80: (1, {'@': 300}), 81: (1, {'@': 300}), 68: (1, {'@': 300}), 84: (1, {'@': 300}), 69: (1, {'@': 300}), 86: (1, {'@': 300}), 71: (1, {'@': 300}), 87: (1, {'@': 300}), 89: (1, {'@': 300}), 12: (1, {'@': 300}), 93: (1, {'@': 300}), 60: (1, {'@': 300}), 63: (1, {'@': 300}), 62: (1, {'@': 300}), 97: (1, {'@': 300}), 99: (1, {'@': 300}), 100: (1, {'@': 300}), 102: (1, {'@': 300}), 43: (1, {'@': 300}), 103: (1, {'@': 300}), 105: (1, {'@': 300}), 107: (1, {'@': 300}), 108: (1, {'@': 300}), 112: (1, {'@': 300}), 70: (1, {'@': 300}), 64: (1, {'@': 300}), 67: (1, {'@': 300}), 3: (1, {'@': 300}), 9: (1, {'@': 300}), 115: (1, {'@': 300}), 116: (1, {'@': 300}), 5: (1, {'@': 300}), 125: (1, {'@': 300}), 59: (1, {'@': 300}), 120: (1, {'@': 300}), 65: (1, {'@': 300}), 6: (1, {'@': 300}), 54: (1, {'@': 300}), 123: (1, {'@': 300}), 73: (1, {'@': 300}), 74: (1, {'@': 300}), 75: (1, {'@': 300}), 76: (1, {'@': 300}), 77: (1, {'@': 300}), 79: (1, {'@': 300}), 61: (1, {'@': 300}), 82: (1, {'@': 300}), 10: (1, {'@': 300}), 66: (1, {'@': 300}), 83: (1, {'@': 300}), 85: (1, {'@': 300}), 11: (1, {'@': 300}), 88: (1, {'@': 300}), 90: (1, {'@': 300}), 91: (1, {'@': 300}), 92: (1, {'@': 300}), 94: (1, {'@': 300}), 47: (1, {'@': 300}), 95: (1, {'@': 300}), 96: (1, {'@': 300}), 4: (1, {'@': 300}), 55: (1, {'@': 300}), 98: (1, {'@': 300}), 101: (1, {'@': 300}), 13: (1, {'@': 300}), 104: (1, {'@': 300}), 106: (1, {'@': 300}), 58: (1, {'@': 300}), 109: (1, {'@': 300}), 1: (1, {'@': 300}), 110: (1, {'@': 300}), 111: (1, {'@': 300}), 113: (1, {'@': 300}), 114: (1, {'@': 300}), 32: (1, {'@': 300}), 57: (1, {'@': 300}), 117: (1, {'@': 300}), 118: (1, {'@': 300}), 119: (1, {'@': 300}), 121: (1, {'@': 300}), 122: (1, {'@': 300}), 124: (1, {'@': 300})}, 28: {13: (1, {'@': 370})}, 29: {9: (0, 270), 11: (1, {'@': 127}), 13: (1, {'@': 127}), 12: (1, {'@': 127})}, 30: {10: (0, 171)}, 31: {14: (1, {'@': 266}), 15: (1, {'@': 266}), 16: (1, {'@': 266}), 17: (1, {'@': 266}), 18: (1, {'@': 266}), 19: (1, {'@': 266}), 20: (1, {'@': 266}), 21: (1, {'@': 266}), 22: (1, {'@': 266}), 23: (1, {'@': 266}), 24: (1, {'@': 266}), 25: (1, {'@': 266}), 1: (1, {'@': 266}), 26: (1, {'@': 266}), 27: (1, {'@': 266}), 28: (1, {'@': 266}), 46: (1, {'@': 266}), 29: (1, {'@': 266}), 30: (1, {'@': 266}), 3: (1, {'@': 266}), 31: (1, {'@': 266}), 9: (1, {'@': 266}), 32: (1, {'@': 266}), 34: (1, {'@': 266}), 33: (1, {'@': 266}), 5: (1, {'@': 266}), 35: (1, {'@': 266}), 36: (1, {'@': 266}), 37: (1, {'@': 266}), 47: (1, {'@': 266}), 38: (1, {'@': 266}), 48: (1, {'@': 266}), 39: (1, {'@': 266}), 49: (1, {'@': 266}), 40: (1, {'@': 266}), 41: (1, {'@': 266}), 42: (1, {'@': 266})}, 32: {11: (1, {'@': 230}), 12: (1, {'@': 230}), 13: (1, {'@': 230})}, 33: {72: (1, {'@': 311}), 56: (1, {'@': 311}), 78: (1, {'@': 311}), 80: (1, {'@': 311}), 81: (1, {'@': 311}), 68: (1, {'@': 311}), 84: (1, {'@': 311}), 69: (1, {'@': 311}), 86: (1, {'@': 311}), 71: (1, {'@': 311}), 87: (1, {'@': 311}), 89: (1, {'@': 311}), 12: (1, {'@': 311}), 93: (1, {'@': 311}), 60: (1, {'@': 311}), 63: (1, {'@': 311}), 62: (1, {'@': 311}), 97: (1, {'@': 311}), 99: (1, {'@': 311}), 100: (1, {'@': 311}), 102: (1, {'@': 311}), 43: (1, {'@': 311}), 103: (1, {'@': 311}), 105: (1, {'@': 311}), 107: (1, {'@': 311}), 108: (1, {'@': 311}), 112: (1, {'@': 311}), 70: (1, {'@': 311}), 67: (1, {'@': 311}), 64: (1, {'@': 311}), 3: (1, {'@': 311}), 9: (1, {'@': 311}), 115: (1, {'@': 311}), 5: (1, {'@': 311}), 116: (1, {'@': 311}), 125: (1, {'@': 311}), 59: (1, {'@': 311}), 120: (1, {'@': 311}), 65: (1, {'@': 311}), 6: (1, {'@': 311}), 54: (1, {'@': 311}), 123: (1, {'@': 311}), 73: (1, {'@': 311}), 74: (1, {'@': 311}), 75: (1, {'@': 311}), 76: (1, {'@': 311}), 77: (1, {'@': 311}), 79: (1, {'@': 311}), 61: (1, {'@': 311}), 10: (1, {'@': 311}), 82: (1, {'@': 311}), 66: (1, {'@': 311}), 83: (1, {'@': 311}), 85: (1, {'@': 311}), 11: (1, {'@': 311}), 88: (1, {'@': 311}), 90: (1, {'@': 311}), 91: (1, {'@': 311}), 92: (1, {'@': 311}), 94: (1, {'@': 311}), 47: (1, {'@': 311}), 95: (1, {'@': 311}), 96: (1, {'@': 311}), 4: (1, {'@': 311}), 55: (1, {'@': 311}), 98: (1, {'@': 311}), 101: (1, {'@': 311}), 13: (1, {'@': 311}), 104: (1, {'@': 311}), 106: (1, {'@': 311}), 58: (1, {'@': 311}), 109: (1, {'@': 311}), 1: (1, {'@': 311}), 110: (1, {'@': 311}), 111: (1, {'@': 311}), 113: (1, {'@': 311}), 114: (1, {'@': 311}), 32: (1, {'@': 311}), 57: (1, {'@': 311}), 117: (1, {'@': 311}), 118: (1, {'@': 311}), 119: (1, {'@': 311}), 121: (1, {'@': 311}), 122: (1, {'@': 311}), 124: (1, {'@': 311})}, 34: {5: (0, 285)}, 35: {10: (0, 490)}, 36: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 139: (0, 158), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 37: {11: (1, {'@': 59}), 13: (1, {'@': 59}), 12: (1, {'@': 59})}, 38: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 131: (0, 281), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 39: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 131: (0, 280), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 40: {72: (1, {'@': 275}), 56: (1, {'@': 275}), 78: (1, {'@': 275}), 80: (1, {'@': 275}), 81: (1, {'@': 275}), 68: (1, {'@': 275}), 84: (1, {'@': 275}), 69: (1, {'@': 275}), 86: (1, {'@': 275}), 71: (1, {'@': 275}), 87: (1, {'@': 275}), 89: (1, {'@': 275}), 12: (1, {'@': 275}), 93: (1, {'@': 275}), 60: (1, {'@': 275}), 63: (1, {'@': 275}), 62: (1, {'@': 275}), 97: (1, {'@': 275}), 99: (1, {'@': 275}), 100: (1, {'@': 275}), 102: (1, {'@': 275}), 43: (1, {'@': 275}), 103: (1, {'@': 275}), 105: (1, {'@': 275}), 107: (1, {'@': 275}), 108: (1, {'@': 275}), 112: (1, {'@': 275}), 70: (1, {'@': 275}), 64: (1, {'@': 275}), 67: (1, {'@': 275}), 3: (1, {'@': 275}), 9: (1, {'@': 275}), 115: (1, {'@': 275}), 116: (1, {'@': 275}), 5: (1, {'@': 275}), 125: (1, {'@': 275}), 59: (1, {'@': 275}), 120: (1, {'@': 275}), 65: (1, {'@': 275}), 6: (1, {'@': 275}), 54: (1, {'@': 275}), 123: (1, {'@': 275}), 73: (1, {'@': 275}), 74: (1, {'@': 275}), 75: (1, {'@': 275}), 76: (1, {'@': 275}), 77: (1, {'@': 275}), 79: (1, {'@': 275}), 61: (1, {'@': 275}), 82: (1, {'@': 275}), 10: (1, {'@': 275}), 66: (1, {'@': 275}), 83: (1, {'@': 275}), 85: (1, {'@': 275}), 11: (1, {'@': 275}), 88: (1, {'@': 275}), 90: (1, {'@': 275}), 91: (1, {'@': 275}), 92: (1, {'@': 275}), 94: (1, {'@': 275}), 47: (1, {'@': 275}), 95: (1, {'@': 275}), 96: (1, {'@': 275}), 4: (1, {'@': 275}), 55: (1, {'@': 275}), 98: (1, {'@': 275}), 101: (1, {'@': 275}), 13: (1, {'@': 275}), 104: (1, {'@': 275}), 106: (1, {'@': 275}), 58: (1, {'@': 275}), 109: (1, {'@': 275}), 1: (1, {'@': 275}), 110: (1, {'@': 275}), 111: (1, {'@': 275}), 113: (1, {'@': 275}), 114: (1, {'@': 275}), 32: (1, {'@': 275}), 57: (1, {'@': 275}), 117: (1, {'@': 275}), 118: (1, {'@': 275}), 119: (1, {'@': 275}), 121: (1, {'@': 275}), 122: (1, {'@': 275}), 124: (1, {'@': 275})}, 41: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 10: (0, 165), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 131: (0, 84), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 42: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 53: (0, 768), 33: (0, 356), 23: (0, 219), 29: (0, 474), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 24: (0, 408), 40: (0, 503), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 43: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 131: (0, 378), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 44: {11: (1, {'@': 208}), 12: (1, {'@': 208}), 13: (1, {'@': 208})}, 45: {11: (1, {'@': 131}), 13: (1, {'@': 131}), 12: (1, {'@': 131})}, 46: {10: (0, 246)}, 47: {57: (0, 36), 56: (1, {'@': 383}), 54: (1, {'@': 383}), 63: (1, {'@': 383}), 11: (1, {'@': 383}), 12: (1, {'@': 383}), 13: (1, {'@': 383}), 72: (1, {'@': 383}), 74: (1, {'@': 383}), 98: (1, {'@': 383}), 75: (1, {'@': 383}), 99: (1, {'@': 383}), 100: (1, {'@': 383}), 76: (1, {'@': 383}), 101: (1, {'@': 383}), 102: (1, {'@': 383}), 103: (1, {'@': 383}), 77: (1, {'@': 383}), 78: (1, {'@': 383}), 104: (1, {'@': 383}), 105: (1, {'@': 383}), 106: (1, {'@': 383}), 107: (1, {'@': 383}), 79: (1, {'@': 383}), 108: (1, {'@': 383}), 80: (1, {'@': 383}), 81: (1, {'@': 383}), 82: (1, {'@': 383}), 109: (1, {'@': 383}), 83: (1, {'@': 383}), 1: (1, {'@': 383}), 110: (1, {'@': 383}), 111: (1, {'@': 383}), 84: (1, {'@': 383}), 112: (1, {'@': 383}), 85: (1, {'@': 383}), 86: (1, {'@': 383}), 113: (1, {'@': 383}), 114: (1, {'@': 383}), 87: (1, {'@': 383}), 3: (1, {'@': 383}), 88: (1, {'@': 383}), 89: (1, {'@': 383}), 115: (1, {'@': 383}), 90: (1, {'@': 383}), 116: (1, {'@': 383}), 117: (1, {'@': 383}), 118: (1, {'@': 383}), 91: (1, {'@': 383}), 119: (1, {'@': 383}), 92: (1, {'@': 383}), 93: (1, {'@': 383}), 94: (1, {'@': 383}), 120: (1, {'@': 383}), 95: (1, {'@': 383}), 96: (1, {'@': 383}), 121: (1, {'@': 383}), 123: (1, {'@': 383}), 97: (1, {'@': 383}), 124: (1, {'@': 383}), 5: (1, {'@': 383}), 10: (1, {'@': 383}), 43: (1, {'@': 383}), 73: (1, {'@': 383}), 6: (1, {'@': 383}), 32: (1, {'@': 383}), 122: (1, {'@': 383})}, 48: {5: (0, 153)}, 49: {5: (0, 353), 10: (0, 695), 54: (0, 229)}, 50: {14: (1, {'@': 371}), 15: (1, {'@': 371}), 16: (1, {'@': 371}), 17: (1, {'@': 371}), 18: (1, {'@': 371}), 19: (1, {'@': 371}), 20: (1, {'@': 371}), 21: (1, {'@': 371}), 22: (1, {'@': 371}), 23: (1, {'@': 371}), 24: (1, {'@': 371}), 25: (1, {'@': 371}), 1: (1, {'@': 371}), 26: (1, {'@': 371}), 27: (1, {'@': 371}), 28: (1, {'@': 371}), 29: (1, {'@': 371}), 30: (1, {'@': 371}), 3: (1, {'@': 371}), 31: (1, {'@': 371}), 9: (1, {'@': 371}), 32: (1, {'@': 371}), 33: (1, {'@': 371}), 34: (1, {'@': 371}), 35: (1, {'@': 371}), 36: (1, {'@': 371}), 37: (1, {'@': 371}), 38: (1, {'@': 371}), 39: (1, {'@': 371}), 40: (1, {'@': 371}), 41: (1, {'@': 371}), 42: (1, {'@': 371})}, 51: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 131: (0, 258), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 54: (0, 222), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 52: {11: (1, {'@': 103}), 13: (1, {'@': 103}), 12: (1, {'@': 103})}, 53: {33: (1, {'@': 180}), 3: (1, {'@': 180})}, 54: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 131: (0, 310), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 144: (0, 276), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 55: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 131: (0, 421), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 56: {3: (0, 765)}, 57: {11: (1, {'@': 165}), 13: (1, {'@': 165}), 12: (1, {'@': 165})}, 58: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 131: (0, 113), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 59: {4: (0, 737)}, 60: {4: (0, 427)}, 61: {11: (1, {'@': 154}), 13: (1, {'@': 154}), 12: (1, {'@': 154})}, 62: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 150: (0, 81), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 156: (0, 747), 11: (0, 73), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 163: (1, {'@': 146}), 120: (1, {'@': 146}), 45: (1, {'@': 146}), 44: (1, {'@': 146}), 12: (1, {'@': 146}), 13: (1, {'@': 146}), 164: (1, {'@': 146}), 122: (1, {'@': 146}), 165: (1, {'@': 146}), 166: (1, {'@': 146}), 167: (1, {'@': 146}), 168: (1, {'@': 146})}, 63: {11: (1, {'@': 61}), 13: (1, {'@': 61}), 12: (1, {'@': 61})}, 64: {11: (1, {'@': 155}), 13: (1, {'@': 155}), 12: (1, {'@': 155})}, 65: {5: (0, 349)}, 66: {5: (1, {'@': 319}), 10: (1, {'@': 319}), 54: (1, {'@': 319}), 12: (1, {'@': 319}), 13: (1, {'@': 319}), 11: (1, {'@': 319})}, 67: {10: (1, {'@': 355}), 12: (1, {'@': 355}), 13: (1, {'@': 355}), 5: (1, {'@': 355}), 4: (1, {'@': 355}), 11: (1, {'@': 355}), 169: (1, {'@': 355}), 170: (1, {'@': 355})}, 68: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 171: (0, 398), 9: (0, 183), 172: (0, 533), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 23: (0, 219), 29: (0, 474), 14: (0, 493), 15: (0, 205), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 24: (0, 408), 40: (0, 503), 53: (0, 643), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247), 72: (1, {'@': 271}), 56: (1, {'@': 271}), 78: (1, {'@': 271}), 80: (1, {'@': 271}), 81: (1, {'@': 271}), 68: (1, {'@': 271}), 84: (1, {'@': 271}), 69: (1, {'@': 271}), 86: (1, {'@': 271}), 71: (1, {'@': 271}), 87: (1, {'@': 271}), 89: (1, {'@': 271}), 12: (1, {'@': 271}), 93: (1, {'@': 271}), 60: (1, {'@': 271}), 63: (1, {'@': 271}), 62: (1, {'@': 271}), 97: (1, {'@': 271}), 99: (1, {'@': 271}), 100: (1, {'@': 271}), 102: (1, {'@': 271}), 43: (1, {'@': 271}), 103: (1, {'@': 271}), 105: (1, {'@': 271}), 107: (1, {'@': 271}), 108: (1, {'@': 271}), 112: (1, {'@': 271}), 70: (1, {'@': 271}), 64: (1, {'@': 271}), 67: (1, {'@': 271}), 115: (1, {'@': 271}), 116: (1, {'@': 271}), 5: (1, {'@': 271}), 125: (1, {'@': 271}), 59: (1, {'@': 271}), 120: (1, {'@': 271}), 65: (1, {'@': 271}), 6: (1, {'@': 271}), 54: (1, {'@': 271}), 123: (1, {'@': 271}), 73: (1, {'@': 271}), 74: (1, {'@': 271}), 75: (1, {'@': 271}), 76: (1, {'@': 271}), 77: (1, {'@': 271}), 79: (1, {'@': 271}), 61: (1, {'@': 271}), 82: (1, {'@': 271}), 10: (1, {'@': 271}), 66: (1, {'@': 271}), 83: (1, {'@': 271}), 85: (1, {'@': 271}), 11: (1, {'@': 271}), 88: (1, {'@': 271}), 90: (1, {'@': 271}), 91: (1, {'@': 271}), 92: (1, {'@': 271}), 94: (1, {'@': 271}), 47: (1, {'@': 271}), 95: (1, {'@': 271}), 96: (1, {'@': 271}), 4: (1, {'@': 271}), 55: (1, {'@': 271}), 98: (1, {'@': 271}), 101: (1, {'@': 271}), 13: (1, {'@': 271}), 104: (1, {'@': 271}), 106: (1, {'@': 271}), 58: (1, {'@': 271}), 109: (1, {'@': 271}), 110: (1, {'@': 271}), 111: (1, {'@': 271}), 113: (1, {'@': 271}), 114: (1, {'@': 271}), 57: (1, {'@': 271}), 117: (1, {'@': 271}), 118: (1, {'@': 271}), 119: (1, {'@': 271}), 121: (1, {'@': 271}), 122: (1, {'@': 271}), 124: (1, {'@': 271})}, 69: {10: (0, 383), 54: (0, 88)}, 70: {5: (0, 394), 10: (0, 133)}, 71: {11: (1, {'@': 46}), 13: (1, {'@': 46}), 12: (1, {'@': 46})}, 72: {5: (0, 170)}, 73: {72: (1, {'@': 15}), 168: (1, {'@': 15}), 98: (1, {'@': 15}), 99: (1, {'@': 15}), 100: (1, {'@': 15}), 101: (1, {'@': 15}), 102: (1, {'@': 15}), 76: (1, {'@': 15}), 13: (1, {'@': 15}), 103: (1, {'@': 15}), 105: (1, {'@': 15}), 104: (1, {'@': 15}), 78: (1, {'@': 15}), 77: (1, {'@': 15}), 106: (1, {'@': 15}), 107: (1, {'@': 15}), 79: (1, {'@': 15}), 108: (1, {'@': 15}), 80: (1, {'@': 15}), 81: (1, {'@': 15}), 82: (1, {'@': 15}), 109: (1, {'@': 15}), 83: (1, {'@': 15}), 1: (1, {'@': 15}), 110: (1, {'@': 15}), 111: (1, {'@': 15}), 84: (1, {'@': 15}), 112: (1, {'@': 15}), 85: (1, {'@': 15}), 11: (1, {'@': 15}), 86: (1, {'@': 15}), 114: (1, {'@': 15}), 87: (1, {'@': 15}), 3: (1, {'@': 15}), 88: (1, {'@': 15}), 89: (1, {'@': 15}), 115: (1, {'@': 15}), 116: (1, {'@': 15}), 117: (1, {'@': 15}), 118: (1, {'@': 15}), 91: (1, {'@': 15}), 119: (1, {'@': 15}), 92: (1, {'@': 15}), 93: (1, {'@': 15}), 94: (1, {'@': 15}), 120: (1, {'@': 15}), 95: (1, {'@': 15}), 96: (1, {'@': 15}), 121: (1, {'@': 15}), 123: (1, {'@': 15}), 97: (1, {'@': 15}), 124: (1, {'@': 15}), 166: (1, {'@': 15}), 44: (1, {'@': 15}), 163: (1, {'@': 15}), 45: (1, {'@': 15}), 12: (1, {'@': 15}), 164: (1, {'@': 15}), 122: (1, {'@': 15}), 165: (1, {'@': 15}), 167: (1, {'@': 15})}, 74: {47: (0, 607), 66: (0, 515), 55: (1, {'@': 400}), 62: (1, {'@': 400}), 56: (1, {'@': 400}), 65: (1, {'@': 400}), 68: (1, {'@': 400}), 57: (1, {'@': 400}), 54: (1, {'@': 400}), 69: (1, {'@': 400}), 67: (1, {'@': 400}), 4: (1, {'@': 400}), 58: (1, {'@': 400}), 59: (1, {'@': 400}), 60: (1, {'@': 400}), 64: (1, {'@': 400}), 63: (1, {'@': 400}), 12: (1, {'@': 400}), 13: (1, {'@': 400}), 11: (1, {'@': 400}), 72: (1, {'@': 400}), 73: (1, {'@': 400}), 74: (1, {'@': 400}), 75: (1, {'@': 400}), 76: (1, {'@': 400}), 77: (1, {'@': 400}), 78: (1, {'@': 400}), 79: (1, {'@': 400}), 80: (1, {'@': 400}), 81: (1, {'@': 400}), 10: (1, {'@': 400}), 82: (1, {'@': 400}), 83: (1, {'@': 400}), 84: (1, {'@': 400}), 85: (1, {'@': 400}), 86: (1, {'@': 400}), 87: (1, {'@': 400}), 88: (1, {'@': 400}), 89: (1, {'@': 400}), 90: (1, {'@': 400}), 91: (1, {'@': 400}), 92: (1, {'@': 400}), 93: (1, {'@': 400}), 94: (1, {'@': 400}), 95: (1, {'@': 400}), 96: (1, {'@': 400}), 97: (1, {'@': 400}), 98: (1, {'@': 400}), 99: (1, {'@': 400}), 100: (1, {'@': 400}), 101: (1, {'@': 400}), 102: (1, {'@': 400}), 43: (1, {'@': 400}), 103: (1, {'@': 400}), 104: (1, {'@': 400}), 105: (1, {'@': 400}), 106: (1, {'@': 400}), 107: (1, {'@': 400}), 108: (1, {'@': 400}), 109: (1, {'@': 400}), 1: (1, {'@': 400}), 110: (1, {'@': 400}), 111: (1, {'@': 400}), 112: (1, {'@': 400}), 113: (1, {'@': 400}), 114: (1, {'@': 400}), 3: (1, {'@': 400}), 32: (1, {'@': 400}), 115: (1, {'@': 400}), 5: (1, {'@': 400}), 116: (1, {'@': 400}), 117: (1, {'@': 400}), 118: (1, {'@': 400}), 119: (1, {'@': 400}), 120: (1, {'@': 400}), 121: (1, {'@': 400}), 6: (1, {'@': 400}), 122: (1, {'@': 400}), 123: (1, {'@': 400}), 124: (1, {'@': 400})}, 75: {72: (1, {'@': 14}), 168: (1, {'@': 14}), 98: (1, {'@': 14}), 99: (1, {'@': 14}), 100: (1, {'@': 14}), 101: (1, {'@': 14}), 102: (1, {'@': 14}), 76: (1, {'@': 14}), 13: (1, {'@': 14}), 103: (1, {'@': 14}), 105: (1, {'@': 14}), 104: (1, {'@': 14}), 78: (1, {'@': 14}), 77: (1, {'@': 14}), 106: (1, {'@': 14}), 107: (1, {'@': 14}), 79: (1, {'@': 14}), 108: (1, {'@': 14}), 80: (1, {'@': 14}), 81: (1, {'@': 14}), 82: (1, {'@': 14}), 109: (1, {'@': 14}), 83: (1, {'@': 14}), 1: (1, {'@': 14}), 110: (1, {'@': 14}), 111: (1, {'@': 14}), 84: (1, {'@': 14}), 112: (1, {'@': 14}), 85: (1, {'@': 14}), 11: (1, {'@': 14}), 86: (1, {'@': 14}), 114: (1, {'@': 14}), 87: (1, {'@': 14}), 3: (1, {'@': 14}), 88: (1, {'@': 14}), 89: (1, {'@': 14}), 115: (1, {'@': 14}), 116: (1, {'@': 14}), 117: (1, {'@': 14}), 118: (1, {'@': 14}), 91: (1, {'@': 14}), 119: (1, {'@': 14}), 92: (1, {'@': 14}), 93: (1, {'@': 14}), 94: (1, {'@': 14}), 120: (1, {'@': 14}), 95: (1, {'@': 14}), 96: (1, {'@': 14}), 121: (1, {'@': 14}), 123: (1, {'@': 14}), 97: (1, {'@': 14}), 124: (1, {'@': 14}), 166: (1, {'@': 14}), 44: (1, {'@': 14}), 163: (1, {'@': 14}), 45: (1, {'@': 14}), 12: (1, {'@': 14}), 164: (1, {'@': 14}), 122: (1, {'@': 14}), 165: (1, {'@': 14}), 167: (1, {'@': 14})}, 76: {10: (0, 340)}, 77: {5: (1, {'@': 336}), 10: (1, {'@': 336})}, 78: {47: (0, 607), 66: (0, 515), 55: (1, {'@': 398}), 62: (1, {'@': 398}), 56: (1, {'@': 398}), 65: (1, {'@': 398}), 68: (1, {'@': 398}), 57: (1, {'@': 398}), 54: (1, {'@': 398}), 69: (1, {'@': 398}), 67: (1, {'@': 398}), 4: (1, {'@': 398}), 58: (1, {'@': 398}), 59: (1, {'@': 398}), 60: (1, {'@': 398}), 64: (1, {'@': 398}), 63: (1, {'@': 398}), 12: (1, {'@': 398}), 13: (1, {'@': 398}), 11: (1, {'@': 398}), 72: (1, {'@': 398}), 73: (1, {'@': 398}), 74: (1, {'@': 398}), 75: (1, {'@': 398}), 76: (1, {'@': 398}), 77: (1, {'@': 398}), 78: (1, {'@': 398}), 79: (1, {'@': 398}), 80: (1, {'@': 398}), 81: (1, {'@': 398}), 10: (1, {'@': 398}), 82: (1, {'@': 398}), 83: (1, {'@': 398}), 84: (1, {'@': 398}), 85: (1, {'@': 398}), 86: (1, {'@': 398}), 87: (1, {'@': 398}), 88: (1, {'@': 398}), 89: (1, {'@': 398}), 90: (1, {'@': 398}), 91: (1, {'@': 398}), 92: (1, {'@': 398}), 93: (1, {'@': 398}), 94: (1, {'@': 398}), 95: (1, {'@': 398}), 96: (1, {'@': 398}), 97: (1, {'@': 398}), 98: (1, {'@': 398}), 99: (1, {'@': 398}), 100: (1, {'@': 398}), 101: (1, {'@': 398}), 102: (1, {'@': 398}), 43: (1, {'@': 398}), 103: (1, {'@': 398}), 104: (1, {'@': 398}), 105: (1, {'@': 398}), 106: (1, {'@': 398}), 107: (1, {'@': 398}), 108: (1, {'@': 398}), 109: (1, {'@': 398}), 1: (1, {'@': 398}), 110: (1, {'@': 398}), 111: (1, {'@': 398}), 112: (1, {'@': 398}), 113: (1, {'@': 398}), 114: (1, {'@': 398}), 3: (1, {'@': 398}), 32: (1, {'@': 398}), 115: (1, {'@': 398}), 5: (1, {'@': 398}), 116: (1, {'@': 398}), 117: (1, {'@': 398}), 118: (1, {'@': 398}), 119: (1, {'@': 398}), 120: (1, {'@': 398}), 121: (1, {'@': 398}), 6: (1, {'@': 398}), 122: (1, {'@': 398}), 123: (1, {'@': 398}), 124: (1, {'@': 398})}, 79: {11: (1, {'@': 129}), 13: (1, {'@': 129}), 12: (1, {'@': 129})}, 80: {173: (0, 439), 121: (0, 743), 11: (1, {'@': 69}), 13: (1, {'@': 69}), 12: (1, {'@': 69})}, 81: {11: (1, {'@': 17}), 13: (1, {'@': 17}), 12: (1, {'@': 17})}, 82: {4: (0, 365)}, 83: {5: (0, 259)}, 84: {10: (0, 610)}, 85: {174: (0, 192), 175: (0, 703), 122: (0, 714), 120: (0, 550)}, 86: {43: (0, 89)}, 87: {11: (1, {'@': 85}), 13: (1, {'@': 85}), 12: (1, {'@': 85})}, 88: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 131: (0, 30), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 10: (0, 33), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 89: {14: (1, {'@': 241}), 15: (1, {'@': 241}), 16: (1, {'@': 241}), 17: (1, {'@': 241}), 18: (1, {'@': 241}), 101: (1, {'@': 241}), 100: (1, {'@': 241}), 19: (1, {'@': 241}), 20: (1, {'@': 241}), 21: (1, {'@': 241}), 22: (1, {'@': 241}), 23: (1, {'@': 241}), 108: (1, {'@': 241}), 82: (1, {'@': 241}), 24: (1, {'@': 241}), 25: (1, {'@': 241}), 1: (1, {'@': 241}), 110: (1, {'@': 241}), 26: (1, {'@': 241}), 27: (1, {'@': 241}), 28: (1, {'@': 241}), 46: (1, {'@': 241}), 29: (1, {'@': 241}), 30: (1, {'@': 241}), 3: (1, {'@': 241}), 31: (1, {'@': 241}), 9: (1, {'@': 241}), 32: (1, {'@': 241}), 34: (1, {'@': 241}), 33: (1, {'@': 241}), 35: (1, {'@': 241}), 36: (1, {'@': 241}), 37: (1, {'@': 241}), 47: (1, {'@': 241}), 38: (1, {'@': 241}), 95: (1, {'@': 241}), 48: (1, {'@': 241}), 39: (1, {'@': 241}), 49: (1, {'@': 241}), 40: (1, {'@': 241}), 41: (1, {'@': 241}), 42: (1, {'@': 241})}, 90: {10: (0, 292)}, 91: {5: (1, {'@': 256}), 11: (1, {'@': 256}), 12: (1, {'@': 256}), 13: (1, {'@': 256})}, 92: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 156: (0, 747), 93: (0, 630), 98: (0, 544), 11: (0, 602), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 150: (0, 705), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 163: (1, {'@': 147}), 120: (1, {'@': 147}), 45: (1, {'@': 147}), 44: (1, {'@': 147}), 12: (1, {'@': 147}), 13: (1, {'@': 147}), 164: (1, {'@': 147}), 122: (1, {'@': 147}), 165: (1, {'@': 147}), 166: (1, {'@': 147}), 167: (1, {'@': 147}), 168: (1, {'@': 147})}, 93: {31: (0, 3), 131: (0, 317), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 176: (0, 122), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 177: (0, 320), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 178: (0, 31), 179: (0, 9), 180: (0, 466), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 181: (0, 334), 20: (0, 247), 138: (0, 287), 182: (0, 248), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 183: (0, 443), 139: (0, 505), 22: (0, 11), 42: (0, 233), 184: (0, 313), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 94: {168: (0, 407)}, 95: {5: (1, {'@': 170}), 10: (1, {'@': 170})}, 96: {55: (0, 693), 60: (0, 562), 65: (0, 668), 68: (0, 513), 62: (1, {'@': 397}), 56: (1, {'@': 397}), 57: (1, {'@': 397}), 54: (1, {'@': 397}), 69: (1, {'@': 397}), 67: (1, {'@': 397}), 4: (1, {'@': 397}), 58: (1, {'@': 397}), 59: (1, {'@': 397}), 64: (1, {'@': 397}), 63: (1, {'@': 397}), 12: (1, {'@': 397}), 13: (1, {'@': 397}), 11: (1, {'@': 397}), 72: (1, {'@': 397}), 73: (1, {'@': 397}), 74: (1, {'@': 397}), 75: (1, {'@': 397}), 76: (1, {'@': 397}), 77: (1, {'@': 397}), 78: (1, {'@': 397}), 79: (1, {'@': 397}), 80: (1, {'@': 397}), 81: (1, {'@': 397}), 10: (1, {'@': 397}), 82: (1, {'@': 397}), 83: (1, {'@': 397}), 84: (1, {'@': 397}), 85: (1, {'@': 397}), 86: (1, {'@': 397}), 87: (1, {'@': 397}), 88: (1, {'@': 397}), 89: (1, {'@': 397}), 90: (1, {'@': 397}), 91: (1, {'@': 397}), 92: (1, {'@': 397}), 93: (1, {'@': 397}), 94: (1, {'@': 397}), 95: (1, {'@': 397}), 96: (1, {'@': 397}), 97: (1, {'@': 397}), 98: (1, {'@': 397}), 99: (1, {'@': 397}), 100: (1, {'@': 397}), 101: (1, {'@': 397}), 102: (1, {'@': 397}), 43: (1, {'@': 397}), 103: (1, {'@': 397}), 104: (1, {'@': 397}), 105: (1, {'@': 397}), 106: (1, {'@': 397}), 107: (1, {'@': 397}), 108: (1, {'@': 397}), 109: (1, {'@': 397}), 1: (1, {'@': 397}), 110: (1, {'@': 397}), 111: (1, {'@': 397}), 112: (1, {'@': 397}), 113: (1, {'@': 397}), 114: (1, {'@': 397}), 3: (1, {'@': 397}), 32: (1, {'@': 397}), 115: (1, {'@': 397}), 5: (1, {'@': 397}), 116: (1, {'@': 397}), 117: (1, {'@': 397}), 118: (1, {'@': 397}), 119: (1, {'@': 397}), 120: (1, {'@': 397}), 121: (1, {'@': 397}), 6: (1, {'@': 397}), 122: (1, {'@': 397}), 123: (1, {'@': 397}), 124: (1, {'@': 397})}, 97: {10: (1, {'@': 363}), 12: (1, {'@': 363}), 13: (1, {'@': 363}), 5: (1, {'@': 363}), 4: (1, {'@': 363}), 11: (1, {'@': 363}), 169: (1, {'@': 363}), 170: (1, {'@': 363})}, 98: {9: (0, 566), 171: (0, 642), 11: (1, {'@': 39}), 13: (1, {'@': 39}), 12: (1, {'@': 39})}, 99: {10: (0, 252)}, 100: {10: (0, 213)}, 101: {55: (1, {'@': 410}), 56: (1, {'@': 410}), 57: (1, {'@': 410}), 58: (1, {'@': 410}), 59: (1, {'@': 410}), 60: (1, {'@': 410}), 61: (1, {'@': 410}), 47: (1, {'@': 410}), 62: (1, {'@': 410}), 63: (1, {'@': 410}), 64: (1, {'@': 410}), 65: (1, {'@': 410}), 66: (1, {'@': 410}), 67: (1, {'@': 410}), 68: (1, {'@': 410}), 54: (1, {'@': 410}), 69: (1, {'@': 410}), 70: (1, {'@': 410}), 4: (1, {'@': 410}), 71: (1, {'@': 410}), 12: (1, {'@': 410}), 13: (1, {'@': 410}), 11: (1, {'@': 410}), 72: (1, {'@': 410}), 73: (1, {'@': 410}), 74: (1, {'@': 410}), 75: (1, {'@': 410}), 76: (1, {'@': 410}), 77: (1, {'@': 410}), 78: (1, {'@': 410}), 79: (1, {'@': 410}), 80: (1, {'@': 410}), 81: (1, {'@': 410}), 10: (1, {'@': 410}), 82: (1, {'@': 410}), 83: (1, {'@': 410}), 84: (1, {'@': 410}), 85: (1, {'@': 410}), 86: (1, {'@': 410}), 87: (1, {'@': 410}), 88: (1, {'@': 410}), 89: (1, {'@': 410}), 90: (1, {'@': 410}), 91: (1, {'@': 410}), 92: (1, {'@': 410}), 93: (1, {'@': 410}), 94: (1, {'@': 410}), 95: (1, {'@': 410}), 96: (1, {'@': 410}), 97: (1, {'@': 410}), 98: (1, {'@': 410}), 99: (1, {'@': 410}), 100: (1, {'@': 410}), 101: (1, {'@': 410}), 102: (1, {'@': 410}), 43: (1, {'@': 410}), 103: (1, {'@': 410}), 104: (1, {'@': 410}), 105: (1, {'@': 410}), 106: (1, {'@': 410}), 107: (1, {'@': 410}), 108: (1, {'@': 410}), 109: (1, {'@': 410}), 1: (1, {'@': 410}), 110: (1, {'@': 410}), 111: (1, {'@': 410}), 112: (1, {'@': 410}), 113: (1, {'@': 410}), 114: (1, {'@': 410}), 3: (1, {'@': 410}), 32: (1, {'@': 410}), 115: (1, {'@': 410}), 5: (1, {'@': 410}), 116: (1, {'@': 410}), 117: (1, {'@': 410}), 118: (1, {'@': 410}), 119: (1, {'@': 410}), 120: (1, {'@': 410}), 121: (1, {'@': 410}), 6: (1, {'@': 410}), 122: (1, {'@': 410}), 123: (1, {'@': 410}), 124: (1, {'@': 410})}, 102: {11: (1, {'@': 228}), 12: (1, {'@': 228}), 13: (1, {'@': 228})}, 103: {11: (1, {'@': 91}), 13: (1, {'@': 91}), 12: (1, {'@': 91})}, 104: {10: (1, {'@': 358}), 12: (1, {'@': 358}), 13: (1, {'@': 358}), 5: (1, {'@': 358}), 4: (1, {'@': 358}), 11: (1, {'@': 358}), 169: (1, {'@': 358}), 170: (1, {'@': 358})}, 105: {11: (1, {'@': 82}), 13: (1, {'@': 82}), 12: (1, {'@': 82})}, 106: {5: (0, 429)}, 107: {11: (1, {'@': 351}), 12: (1, {'@': 351}), 13: (1, {'@': 351})}, 108: {125: (0, 751), 55: (1, {'@': 414}), 56: (1, {'@': 414}), 57: (1, {'@': 414}), 58: (1, {'@': 414}), 59: (1, {'@': 414}), 60: (1, {'@': 414}), 61: (1, {'@': 414}), 47: (1, {'@': 414}), 62: (1, {'@': 414}), 63: (1, {'@': 414}), 65: (1, {'@': 414}), 66: (1, {'@': 414}), 70: (1, {'@': 414}), 68: (1, {'@': 414}), 54: (1, {'@': 414}), 4: (1, {'@': 414}), 69: (1, {'@': 414}), 67: (1, {'@': 414}), 64: (1, {'@': 414}), 71: (1, {'@': 414}), 12: (1, {'@': 414}), 13: (1, {'@': 414}), 11: (1, {'@': 414}), 72: (1, {'@': 414}), 73: (1, {'@': 414}), 74: (1, {'@': 414}), 75: (1, {'@': 414}), 76: (1, {'@': 414}), 77: (1, {'@': 414}), 78: (1, {'@': 414}), 79: (1, {'@': 414}), 80: (1, {'@': 414}), 81: (1, {'@': 414}), 10: (1, {'@': 414}), 82: (1, {'@': 414}), 83: (1, {'@': 414}), 84: (1, {'@': 414}), 85: (1, {'@': 414}), 86: (1, {'@': 414}), 87: (1, {'@': 414}), 88: (1, {'@': 414}), 89: (1, {'@': 414}), 90: (1, {'@': 414}), 91: (1, {'@': 414}), 92: (1, {'@': 414}), 93: (1, {'@': 414}), 94: (1, {'@': 414}), 95: (1, {'@': 414}), 96: (1, {'@': 414}), 97: (1, {'@': 414}), 98: (1, {'@': 414}), 99: (1, {'@': 414}), 100: (1, {'@': 414}), 101: (1, {'@': 414}), 102: (1, {'@': 414}), 43: (1, {'@': 414}), 103: (1, {'@': 414}), 104: (1, {'@': 414}), 105: (1, {'@': 414}), 106: (1, {'@': 414}), 107: (1, {'@': 414}), 108: (1, {'@': 414}), 109: (1, {'@': 414}), 1: (1, {'@': 414}), 110: (1, {'@': 414}), 111: (1, {'@': 414}), 112: (1, {'@': 414}), 113: (1, {'@': 414}), 114: (1, {'@': 414}), 3: (1, {'@': 414}), 32: (1, {'@': 414}), 115: (1, {'@': 414}), 5: (1, {'@': 414}), 116: (1, {'@': 414}), 117: (1, {'@': 414}), 118: (1, {'@': 414}), 119: (1, {'@': 414}), 120: (1, {'@': 414}), 121: (1, {'@': 414}), 6: (1, {'@': 414}), 122: (1, {'@': 414}), 123: (1, {'@': 414}), 124: (1, {'@': 414})}, 109: {11: (1, {'@': 42}), 13: (1, {'@': 42}), 12: (1, {'@': 42})}, 110: {11: (1, {'@': 95}), 13: (1, {'@': 95}), 12: (1, {'@': 95})}, 111: {10: (1, {'@': 361}), 12: (1, {'@': 361}), 13: (1, {'@': 361}), 5: (1, {'@': 361}), 4: (1, {'@': 361}), 11: (1, {'@': 361}), 169: (1, {'@': 361}), 170: (1, {'@': 361})}, 112: {11: (1, {'@': 86}), 13: (1, {'@': 86}), 12: (1, {'@': 86})}, 113: {5: (1, {'@': 343}), 10: (1, {'@': 343})}, 114: {10: (0, 347), 54: (0, 88), 5: (1, {'@': 319})}, 115: {5: (0, 358), 10: (0, 695), 54: (0, 423)}, 116: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 131: (0, 296), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 117: {5: (0, 26)}, 118: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 228), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 119: {185: (0, 139), 164: (0, 531), 175: (0, 619), 165: (0, 586), 120: (0, 550)}, 120: {11: (1, {'@': 128}), 13: (1, {'@': 128}), 12: (1, {'@': 128})}, 121: {55: (1, {'@': 415}), 56: (1, {'@': 415}), 57: (1, {'@': 415}), 125: (1, {'@': 415}), 58: (1, {'@': 415}), 59: (1, {'@': 415}), 60: (1, {'@': 415}), 61: (1, {'@': 415}), 47: (1, {'@': 415}), 62: (1, {'@': 415}), 63: (1, {'@': 415}), 65: (1, {'@': 415}), 66: (1, {'@': 415}), 70: (1, {'@': 415}), 68: (1, {'@': 415}), 54: (1, {'@': 415}), 4: (1, {'@': 415}), 69: (1, {'@': 415}), 67: (1, {'@': 415}), 64: (1, {'@': 415}), 71: (1, {'@': 415}), 12: (1, {'@': 415}), 13: (1, {'@': 415}), 11: (1, {'@': 415}), 72: (1, {'@': 415}), 73: (1, {'@': 415}), 74: (1, {'@': 415}), 75: (1, {'@': 415}), 76: (1, {'@': 415}), 77: (1, {'@': 415}), 78: (1, {'@': 415}), 79: (1, {'@': 415}), 80: (1, {'@': 415}), 81: (1, {'@': 415}), 10: (1, {'@': 415}), 82: (1, {'@': 415}), 83: (1, {'@': 415}), 84: (1, {'@': 415}), 85: (1, {'@': 415}), 86: (1, {'@': 415}), 87: (1, {'@': 415}), 88: (1, {'@': 415}), 89: (1, {'@': 415}), 90: (1, {'@': 415}), 91: (1, {'@': 415}), 92: (1, {'@': 415}), 93: (1, {'@': 415}), 94: (1, {'@': 415}), 95: (1, {'@': 415}), 96: (1, {'@': 415}), 97: (1, {'@': 415}), 98: (1, {'@': 415}), 99: (1, {'@': 415}), 100: (1, {'@': 415}), 101: (1, {'@': 415}), 102: (1, {'@': 415}), 43: (1, {'@': 415}), 103: (1, {'@': 415}), 104: (1, {'@': 415}), 105: (1, {'@': 415}), 106: (1, {'@': 415}), 107: (1, {'@': 415}), 108: (1, {'@': 415}), 109: (1, {'@': 415}), 1: (1, {'@': 415}), 110: (1, {'@': 415}), 111: (1, {'@': 415}), 112: (1, {'@': 415}), 113: (1, {'@': 415}), 114: (1, {'@': 415}), 3: (1, {'@': 415}), 32: (1, {'@': 415}), 115: (1, {'@': 415}), 5: (1, {'@': 415}), 116: (1, {'@': 415}), 117: (1, {'@': 415}), 118: (1, {'@': 415}), 119: (1, {'@': 415}), 120: (1, {'@': 415}), 121: (1, {'@': 415}), 6: (1, {'@': 415}), 122: (1, {'@': 415}), 123: (1, {'@': 415}), 124: (1, {'@': 415})}, 122: {14: (1, {'@': 261}), 15: (1, {'@': 261}), 16: (1, {'@': 261}), 17: (1, {'@': 261}), 18: (1, {'@': 261}), 19: (1, {'@': 261}), 20: (1, {'@': 261}), 21: (1, {'@': 261}), 22: (1, {'@': 261}), 23: (1, {'@': 261}), 24: (1, {'@': 261}), 25: (1, {'@': 261}), 1: (1, {'@': 261}), 26: (1, {'@': 261}), 27: (1, {'@': 261}), 28: (1, {'@': 261}), 46: (1, {'@': 261}), 29: (1, {'@': 261}), 30: (1, {'@': 261}), 3: (1, {'@': 261}), 31: (1, {'@': 261}), 9: (1, {'@': 261}), 32: (1, {'@': 261}), 34: (1, {'@': 261}), 33: (1, {'@': 261}), 5: (1, {'@': 261}), 35: (1, {'@': 261}), 36: (1, {'@': 261}), 37: (1, {'@': 261}), 47: (1, {'@': 261}), 38: (1, {'@': 261}), 48: (1, {'@': 261}), 39: (1, {'@': 261}), 49: (1, {'@': 261}), 40: (1, {'@': 261}), 41: (1, {'@': 261}), 42: (1, {'@': 261})}, 123: {10: (1, {'@': 362}), 12: (1, {'@': 362}), 13: (1, {'@': 362}), 5: (1, {'@': 362}), 4: (1, {'@': 362}), 11: (1, {'@': 362}), 169: (1, {'@': 362}), 170: (1, {'@': 362})}, 124: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 150: (0, 81), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 165: (0, 586), 156: (0, 747), 11: (0, 73), 93: (0, 630), 164: (0, 531), 98: (0, 544), 185: (0, 628), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 175: (0, 619), 94: (0, 584), 82: (0, 568), 159: (0, 595), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 12: (1, {'@': 189}), 13: (1, {'@': 189})}, 125: {9: (0, 777), 172: (0, 530), 72: (1, {'@': 270}), 56: (1, {'@': 270}), 78: (1, {'@': 270}), 80: (1, {'@': 270}), 81: (1, {'@': 270}), 68: (1, {'@': 270}), 84: (1, {'@': 270}), 69: (1, {'@': 270}), 86: (1, {'@': 270}), 71: (1, {'@': 270}), 87: (1, {'@': 270}), 89: (1, {'@': 270}), 12: (1, {'@': 270}), 93: (1, {'@': 270}), 60: (1, {'@': 270}), 63: (1, {'@': 270}), 62: (1, {'@': 270}), 97: (1, {'@': 270}), 99: (1, {'@': 270}), 100: (1, {'@': 270}), 102: (1, {'@': 270}), 43: (1, {'@': 270}), 103: (1, {'@': 270}), 105: (1, {'@': 270}), 107: (1, {'@': 270}), 108: (1, {'@': 270}), 112: (1, {'@': 270}), 70: (1, {'@': 270}), 64: (1, {'@': 270}), 67: (1, {'@': 270}), 3: (1, {'@': 270}), 115: (1, {'@': 270}), 116: (1, {'@': 270}), 5: (1, {'@': 270}), 125: (1, {'@': 270}), 59: (1, {'@': 270}), 120: (1, {'@': 270}), 65: (1, {'@': 270}), 6: (1, {'@': 270}), 54: (1, {'@': 270}), 123: (1, {'@': 270}), 73: (1, {'@': 270}), 74: (1, {'@': 270}), 75: (1, {'@': 270}), 76: (1, {'@': 270}), 77: (1, {'@': 270}), 79: (1, {'@': 270}), 61: (1, {'@': 270}), 82: (1, {'@': 270}), 10: (1, {'@': 270}), 66: (1, {'@': 270}), 83: (1, {'@': 270}), 85: (1, {'@': 270}), 11: (1, {'@': 270}), 88: (1, {'@': 270}), 90: (1, {'@': 270}), 91: (1, {'@': 270}), 92: (1, {'@': 270}), 94: (1, {'@': 270}), 47: (1, {'@': 270}), 95: (1, {'@': 270}), 96: (1, {'@': 270}), 4: (1, {'@': 270}), 55: (1, {'@': 270}), 98: (1, {'@': 270}), 101: (1, {'@': 270}), 13: (1, {'@': 270}), 104: (1, {'@': 270}), 106: (1, {'@': 270}), 58: (1, {'@': 270}), 109: (1, {'@': 270}), 1: (1, {'@': 270}), 110: (1, {'@': 270}), 111: (1, {'@': 270}), 113: (1, {'@': 270}), 114: (1, {'@': 270}), 32: (1, {'@': 270}), 57: (1, {'@': 270}), 117: (1, {'@': 270}), 118: (1, {'@': 270}), 119: (1, {'@': 270}), 121: (1, {'@': 270}), 122: (1, {'@': 270}), 124: (1, {'@': 270})}, 126: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 131: (0, 57), 48: (0, 486)}, 127: {5: (0, 534), 11: (1, {'@': 26}), 13: (1, {'@': 26}), 12: (1, {'@': 26})}, 128: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 150: (0, 81), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 185: (0, 436), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 165: (0, 586), 156: (0, 747), 11: (0, 73), 93: (0, 630), 164: (0, 531), 98: (0, 544), 118: (0, 548), 157: (0, 552), 13: (0, 627), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 175: (0, 619), 94: (0, 584), 82: (0, 568), 159: (0, 595), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 129: {5: (1, {'@': 255}), 11: (1, {'@': 255}), 12: (1, {'@': 255}), 13: (1, {'@': 255})}, 130: {4: (0, 541)}, 131: {14: (1, {'@': 177}), 15: (1, {'@': 177}), 16: (1, {'@': 177}), 17: (1, {'@': 177}), 18: (1, {'@': 177}), 19: (1, {'@': 177}), 20: (1, {'@': 177}), 21: (1, {'@': 177}), 22: (1, {'@': 177}), 23: (1, {'@': 177}), 24: (1, {'@': 177}), 25: (1, {'@': 177}), 1: (1, {'@': 177}), 26: (1, {'@': 177}), 27: (1, {'@': 177}), 28: (1, {'@': 177}), 46: (1, {'@': 177}), 29: (1, {'@': 177}), 30: (1, {'@': 177}), 3: (1, {'@': 177}), 31: (1, {'@': 177}), 9: (1, {'@': 177}), 32: (1, {'@': 177}), 34: (1, {'@': 177}), 33: (1, {'@': 177}), 35: (1, {'@': 177}), 36: (1, {'@': 177}), 37: (1, {'@': 177}), 47: (1, {'@': 177}), 38: (1, {'@': 177}), 48: (1, {'@': 177}), 39: (1, {'@': 177}), 49: (1, {'@': 177}), 40: (1, {'@': 177}), 41: (1, {'@': 177}), 42: (1, {'@': 177})}, 132: {9: (0, 566), 171: (0, 535), 72: (1, {'@': 162}), 56: (1, {'@': 162}), 78: (1, {'@': 162}), 80: (1, {'@': 162}), 81: (1, {'@': 162}), 68: (1, {'@': 162}), 84: (1, {'@': 162}), 69: (1, {'@': 162}), 86: (1, {'@': 162}), 71: (1, {'@': 162}), 87: (1, {'@': 162}), 89: (1, {'@': 162}), 12: (1, {'@': 162}), 93: (1, {'@': 162}), 60: (1, {'@': 162}), 63: (1, {'@': 162}), 62: (1, {'@': 162}), 97: (1, {'@': 162}), 99: (1, {'@': 162}), 100: (1, {'@': 162}), 102: (1, {'@': 162}), 43: (1, {'@': 162}), 103: (1, {'@': 162}), 105: (1, {'@': 162}), 107: (1, {'@': 162}), 108: (1, {'@': 162}), 112: (1, {'@': 162}), 70: (1, {'@': 162}), 64: (1, {'@': 162}), 67: (1, {'@': 162}), 3: (1, {'@': 162}), 115: (1, {'@': 162}), 116: (1, {'@': 162}), 5: (1, {'@': 162}), 125: (1, {'@': 162}), 59: (1, {'@': 162}), 120: (1, {'@': 162}), 65: (1, {'@': 162}), 6: (1, {'@': 162}), 54: (1, {'@': 162}), 123: (1, {'@': 162}), 73: (1, {'@': 162}), 74: (1, {'@': 162}), 75: (1, {'@': 162}), 76: (1, {'@': 162}), 77: (1, {'@': 162}), 79: (1, {'@': 162}), 61: (1, {'@': 162}), 82: (1, {'@': 162}), 10: (1, {'@': 162}), 66: (1, {'@': 162}), 83: (1, {'@': 162}), 85: (1, {'@': 162}), 11: (1, {'@': 162}), 88: (1, {'@': 162}), 90: (1, {'@': 162}), 91: (1, {'@': 162}), 92: (1, {'@': 162}), 94: (1, {'@': 162}), 47: (1, {'@': 162}), 95: (1, {'@': 162}), 96: (1, {'@': 162}), 4: (1, {'@': 162}), 55: (1, {'@': 162}), 98: (1, {'@': 162}), 101: (1, {'@': 162}), 13: (1, {'@': 162}), 104: (1, {'@': 162}), 106: (1, {'@': 162}), 58: (1, {'@': 162}), 109: (1, {'@': 162}), 1: (1, {'@': 162}), 110: (1, {'@': 162}), 111: (1, {'@': 162}), 113: (1, {'@': 162}), 114: (1, {'@': 162}), 32: (1, {'@': 162}), 57: (1, {'@': 162}), 117: (1, {'@': 162}), 118: (1, {'@': 162}), 119: (1, {'@': 162}), 121: (1, {'@': 162}), 122: (1, {'@': 162}), 124: (1, {'@': 162})}, 133: {72: (1, {'@': 283}), 56: (1, {'@': 283}), 78: (1, {'@': 283}), 80: (1, {'@': 283}), 81: (1, {'@': 283}), 68: (1, {'@': 283}), 84: (1, {'@': 283}), 69: (1, {'@': 283}), 86: (1, {'@': 283}), 71: (1, {'@': 283}), 87: (1, {'@': 283}), 89: (1, {'@': 283}), 12: (1, {'@': 283}), 93: (1, {'@': 283}), 60: (1, {'@': 283}), 63: (1, {'@': 283}), 62: (1, {'@': 283}), 97: (1, {'@': 283}), 99: (1, {'@': 283}), 100: (1, {'@': 283}), 102: (1, {'@': 283}), 43: (1, {'@': 283}), 103: (1, {'@': 283}), 105: (1, {'@': 283}), 107: (1, {'@': 283}), 108: (1, {'@': 283}), 112: (1, {'@': 283}), 70: (1, {'@': 283}), 64: (1, {'@': 283}), 67: (1, {'@': 283}), 3: (1, {'@': 283}), 9: (1, {'@': 283}), 115: (1, {'@': 283}), 116: (1, {'@': 283}), 5: (1, {'@': 283}), 125: (1, {'@': 283}), 59: (1, {'@': 283}), 120: (1, {'@': 283}), 65: (1, {'@': 283}), 6: (1, {'@': 283}), 54: (1, {'@': 283}), 123: (1, {'@': 283}), 73: (1, {'@': 283}), 74: (1, {'@': 283}), 75: (1, {'@': 283}), 76: (1, {'@': 283}), 77: (1, {'@': 283}), 79: (1, {'@': 283}), 61: (1, {'@': 283}), 82: (1, {'@': 283}), 10: (1, {'@': 283}), 66: (1, {'@': 283}), 83: (1, {'@': 283}), 85: (1, {'@': 283}), 11: (1, {'@': 283}), 88: (1, {'@': 283}), 90: (1, {'@': 283}), 91: (1, {'@': 283}), 92: (1, {'@': 283}), 94: (1, {'@': 283}), 47: (1, {'@': 283}), 95: (1, {'@': 283}), 96: (1, {'@': 283}), 4: (1, {'@': 283}), 55: (1, {'@': 283}), 98: (1, {'@': 283}), 101: (1, {'@': 283}), 13: (1, {'@': 283}), 104: (1, {'@': 283}), 106: (1, {'@': 283}), 58: (1, {'@': 283}), 109: (1, {'@': 283}), 1: (1, {'@': 283}), 110: (1, {'@': 283}), 111: (1, {'@': 283}), 113: (1, {'@': 283}), 114: (1, {'@': 283}), 32: (1, {'@': 283}), 57: (1, {'@': 283}), 117: (1, {'@': 283}), 118: (1, {'@': 283}), 119: (1, {'@': 283}), 121: (1, {'@': 283}), 122: (1, {'@': 283}), 124: (1, {'@': 283})}, 134: {11: (1, {'@': 22}), 13: (1, {'@': 22}), 12: (1, {'@': 22})}, 135: {5: (0, 55)}, 136: {5: (0, 605), 11: (1, {'@': 35}), 13: (1, {'@': 35}), 12: (1, {'@': 35})}, 137: {7: (1, {'@': 327}), 11: (1, {'@': 327}), 9: (1, {'@': 327}), 13: (1, {'@': 327}), 12: (1, {'@': 327})}, 138: {55: (0, 693), 60: (0, 562), 65: (0, 668), 68: (0, 513), 62: (1, {'@': 396}), 56: (1, {'@': 396}), 57: (1, {'@': 396}), 54: (1, {'@': 396}), 69: (1, {'@': 396}), 67: (1, {'@': 396}), 4: (1, {'@': 396}), 58: (1, {'@': 396}), 59: (1, {'@': 396}), 64: (1, {'@': 396}), 63: (1, {'@': 396}), 12: (1, {'@': 396}), 13: (1, {'@': 396}), 11: (1, {'@': 396}), 72: (1, {'@': 396}), 73: (1, {'@': 396}), 74: (1, {'@': 396}), 75: (1, {'@': 396}), 76: (1, {'@': 396}), 77: (1, {'@': 396}), 78: (1, {'@': 396}), 79: (1, {'@': 396}), 80: (1, {'@': 396}), 81: (1, {'@': 396}), 10: (1, {'@': 396}), 82: (1, {'@': 396}), 83: (1, {'@': 396}), 84: (1, {'@': 396}), 85: (1, {'@': 396}), 86: (1, {'@': 396}), 87: (1, {'@': 396}), 88: (1, {'@': 396}), 89: (1, {'@': 396}), 90: (1, {'@': 396}), 91: (1, {'@': 396}), 92: (1, {'@': 396}), 93: (1, {'@': 396}), 94: (1, {'@': 396}), 95: (1, {'@': 396}), 96: (1, {'@': 396}), 97: (1, {'@': 396}), 98: (1, {'@': 396}), 99: (1, {'@': 396}), 100: (1, {'@': 396}), 101: (1, {'@': 396}), 102: (1, {'@': 396}), 43: (1, {'@': 396}), 103: (1, {'@': 396}), 104: (1, {'@': 396}), 105: (1, {'@': 396}), 106: (1, {'@': 396}), 107: (1, {'@': 396}), 108: (1, {'@': 396}), 109: (1, {'@': 396}), 1: (1, {'@': 396}), 110: (1, {'@': 396}), 111: (1, {'@': 396}), 112: (1, {'@': 396}), 113: (1, {'@': 396}), 114: (1, {'@': 396}), 3: (1, {'@': 396}), 32: (1, {'@': 396}), 115: (1, {'@': 396}), 5: (1, {'@': 396}), 116: (1, {'@': 396}), 117: (1, {'@': 396}), 118: (1, {'@': 396}), 119: (1, {'@': 396}), 120: (1, {'@': 396}), 121: (1, {'@': 396}), 6: (1, {'@': 396}), 122: (1, {'@': 396}), 123: (1, {'@': 396}), 124: (1, {'@': 396})}, 139: {11: (1, {'@': 196}), 12: (1, {'@': 196}), 13: (1, {'@': 196})}, 140: {5: (0, 322), 11: (1, {'@': 124}), 13: (1, {'@': 124}), 12: (1, {'@': 124})}, 141: {55: (1, {'@': 417}), 56: (1, {'@': 417}), 57: (1, {'@': 417}), 125: (1, {'@': 417}), 58: (1, {'@': 417}), 59: (1, {'@': 417}), 60: (1, {'@': 417}), 61: (1, {'@': 417}), 47: (1, {'@': 417}), 62: (1, {'@': 417}), 63: (1, {'@': 417}), 65: (1, {'@': 417}), 66: (1, {'@': 417}), 70: (1, {'@': 417}), 68: (1, {'@': 417}), 54: (1, {'@': 417}), 4: (1, {'@': 417}), 69: (1, {'@': 417}), 67: (1, {'@': 417}), 64: (1, {'@': 417}), 71: (1, {'@': 417}), 12: (1, {'@': 417}), 13: (1, {'@': 417}), 11: (1, {'@': 417}), 72: (1, {'@': 417}), 73: (1, {'@': 417}), 74: (1, {'@': 417}), 75: (1, {'@': 417}), 76: (1, {'@': 417}), 77: (1, {'@': 417}), 78: (1, {'@': 417}), 79: (1, {'@': 417}), 80: (1, {'@': 417}), 81: (1, {'@': 417}), 10: (1, {'@': 417}), 82: (1, {'@': 417}), 83: (1, {'@': 417}), 84: (1, {'@': 417}), 85: (1, {'@': 417}), 86: (1, {'@': 417}), 87: (1, {'@': 417}), 88: (1, {'@': 417}), 89: (1, {'@': 417}), 90: (1, {'@': 417}), 91: (1, {'@': 417}), 92: (1, {'@': 417}), 93: (1, {'@': 417}), 94: (1, {'@': 417}), 95: (1, {'@': 417}), 96: (1, {'@': 417}), 97: (1, {'@': 417}), 98: (1, {'@': 417}), 99: (1, {'@': 417}), 100: (1, {'@': 417}), 101: (1, {'@': 417}), 102: (1, {'@': 417}), 43: (1, {'@': 417}), 103: (1, {'@': 417}), 104: (1, {'@': 417}), 105: (1, {'@': 417}), 106: (1, {'@': 417}), 107: (1, {'@': 417}), 108: (1, {'@': 417}), 109: (1, {'@': 417}), 1: (1, {'@': 417}), 110: (1, {'@': 417}), 111: (1, {'@': 417}), 112: (1, {'@': 417}), 113: (1, {'@': 417}), 114: (1, {'@': 417}), 3: (1, {'@': 417}), 32: (1, {'@': 417}), 115: (1, {'@': 417}), 5: (1, {'@': 417}), 116: (1, {'@': 417}), 117: (1, {'@': 417}), 118: (1, {'@': 417}), 119: (1, {'@': 417}), 120: (1, {'@': 417}), 121: (1, {'@': 417}), 6: (1, {'@': 417}), 122: (1, {'@': 417}), 123: (1, {'@': 417}), 124: (1, {'@': 417})}, 142: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 185: (0, 458), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 165: (0, 586), 156: (0, 747), 93: (0, 630), 164: (0, 531), 13: (0, 599), 98: (0, 544), 11: (0, 602), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 175: (0, 619), 159: (0, 595), 150: (0, 705), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 143: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 131: (0, 148), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 5: (0, 39), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 144: {11: (1, {'@': 156}), 13: (1, {'@': 156}), 12: (1, {'@': 156})}, 145: {10: (0, 215)}, 146: {11: (1, {'@': 113}), 13: (1, {'@': 113}), 12: (1, {'@': 113})}, 147: {11: (1, {'@': 125}), 13: (1, {'@': 125}), 12: (1, {'@': 125})}, 148: {5: (0, 152)}, 149: {11: (1, {'@': 121}), 13: (1, {'@': 121}), 12: (1, {'@': 121})}, 150: {9: (0, 678)}, 151: {10: (0, 304)}, 152: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 131: (0, 52), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 153: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 131: (0, 388), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 154: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 131: (0, 613), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 155: {186: (0, 608), 73: (0, 679), 72: (1, {'@': 213}), 76: (1, {'@': 213}), 77: (1, {'@': 213}), 78: (1, {'@': 213}), 79: (1, {'@': 213}), 80: (1, {'@': 213}), 81: (1, {'@': 213}), 82: (1, {'@': 213}), 83: (1, {'@': 213}), 84: (1, {'@': 213}), 85: (1, {'@': 213}), 11: (1, {'@': 213}), 86: (1, {'@': 213}), 87: (1, {'@': 213}), 88: (1, {'@': 213}), 89: (1, {'@': 213}), 91: (1, {'@': 213}), 92: (1, {'@': 213}), 93: (1, {'@': 213}), 94: (1, {'@': 213}), 95: (1, {'@': 213}), 96: (1, {'@': 213}), 97: (1, {'@': 213}), 98: (1, {'@': 213}), 99: (1, {'@': 213}), 100: (1, {'@': 213}), 101: (1, {'@': 213}), 102: (1, {'@': 213}), 13: (1, {'@': 213}), 103: (1, {'@': 213}), 104: (1, {'@': 213}), 105: (1, {'@': 213}), 106: (1, {'@': 213}), 107: (1, {'@': 213}), 108: (1, {'@': 213}), 109: (1, {'@': 213}), 1: (1, {'@': 213}), 110: (1, {'@': 213}), 111: (1, {'@': 213}), 112: (1, {'@': 213}), 114: (1, {'@': 213}), 3: (1, {'@': 213}), 115: (1, {'@': 213}), 116: (1, {'@': 213}), 117: (1, {'@': 213}), 118: (1, {'@': 213}), 119: (1, {'@': 213}), 121: (1, {'@': 213}), 123: (1, {'@': 213}), 124: (1, {'@': 213})}, 156: {11: (1, {'@': 199}), 12: (1, {'@': 199}), 13: (1, {'@': 199})}, 157: {11: (1, {'@': 160}), 13: (1, {'@': 160}), 12: (1, {'@': 160})}, 158: {57: (1, {'@': 385}), 56: (1, {'@': 385}), 54: (1, {'@': 385}), 63: (1, {'@': 385}), 11: (1, {'@': 385}), 12: (1, {'@': 385}), 13: (1, {'@': 385}), 72: (1, {'@': 385}), 74: (1, {'@': 385}), 98: (1, {'@': 385}), 75: (1, {'@': 385}), 99: (1, {'@': 385}), 100: (1, {'@': 385}), 76: (1, {'@': 385}), 101: (1, {'@': 385}), 102: (1, {'@': 385}), 103: (1, {'@': 385}), 77: (1, {'@': 385}), 78: (1, {'@': 385}), 104: (1, {'@': 385}), 105: (1, {'@': 385}), 106: (1, {'@': 385}), 107: (1, {'@': 385}), 79: (1, {'@': 385}), 108: (1, {'@': 385}), 80: (1, {'@': 385}), 81: (1, {'@': 385}), 82: (1, {'@': 385}), 109: (1, {'@': 385}), 83: (1, {'@': 385}), 1: (1, {'@': 385}), 110: (1, {'@': 385}), 111: (1, {'@': 385}), 84: (1, {'@': 385}), 112: (1, {'@': 385}), 85: (1, {'@': 385}), 86: (1, {'@': 385}), 113: (1, {'@': 385}), 114: (1, {'@': 385}), 87: (1, {'@': 385}), 3: (1, {'@': 385}), 88: (1, {'@': 385}), 89: (1, {'@': 385}), 115: (1, {'@': 385}), 90: (1, {'@': 385}), 116: (1, {'@': 385}), 117: (1, {'@': 385}), 118: (1, {'@': 385}), 91: (1, {'@': 385}), 119: (1, {'@': 385}), 92: (1, {'@': 385}), 93: (1, {'@': 385}), 94: (1, {'@': 385}), 120: (1, {'@': 385}), 95: (1, {'@': 385}), 96: (1, {'@': 385}), 121: (1, {'@': 385}), 123: (1, {'@': 385}), 97: (1, {'@': 385}), 124: (1, {'@': 385}), 5: (1, {'@': 385}), 10: (1, {'@': 385}), 43: (1, {'@': 385}), 73: (1, {'@': 385}), 6: (1, {'@': 385}), 32: (1, {'@': 385}), 122: (1, {'@': 385})}, 159: {54: (0, 88)}, 160: {66: (0, 515), 47: (0, 607), 55: (1, {'@': 402}), 62: (1, {'@': 402}), 56: (1, {'@': 402}), 65: (1, {'@': 402}), 68: (1, {'@': 402}), 57: (1, {'@': 402}), 54: (1, {'@': 402}), 69: (1, {'@': 402}), 67: (1, {'@': 402}), 4: (1, {'@': 402}), 58: (1, {'@': 402}), 59: (1, {'@': 402}), 60: (1, {'@': 402}), 64: (1, {'@': 402}), 63: (1, {'@': 402}), 12: (1, {'@': 402}), 13: (1, {'@': 402}), 11: (1, {'@': 402}), 72: (1, {'@': 402}), 73: (1, {'@': 402}), 74: (1, {'@': 402}), 75: (1, {'@': 402}), 76: (1, {'@': 402}), 77: (1, {'@': 402}), 78: (1, {'@': 402}), 79: (1, {'@': 402}), 80: (1, {'@': 402}), 81: (1, {'@': 402}), 10: (1, {'@': 402}), 82: (1, {'@': 402}), 83: (1, {'@': 402}), 84: (1, {'@': 402}), 85: (1, {'@': 402}), 86: (1, {'@': 402}), 87: (1, {'@': 402}), 88: (1, {'@': 402}), 89: (1, {'@': 402}), 90: (1, {'@': 402}), 91: (1, {'@': 402}), 92: (1, {'@': 402}), 93: (1, {'@': 402}), 94: (1, {'@': 402}), 95: (1, {'@': 402}), 96: (1, {'@': 402}), 97: (1, {'@': 402}), 98: (1, {'@': 402}), 99: (1, {'@': 402}), 100: (1, {'@': 402}), 101: (1, {'@': 402}), 102: (1, {'@': 402}), 43: (1, {'@': 402}), 103: (1, {'@': 402}), 104: (1, {'@': 402}), 105: (1, {'@': 402}), 106: (1, {'@': 402}), 107: (1, {'@': 402}), 108: (1, {'@': 402}), 109: (1, {'@': 402}), 1: (1, {'@': 402}), 110: (1, {'@': 402}), 111: (1, {'@': 402}), 112: (1, {'@': 402}), 113: (1, {'@': 402}), 114: (1, {'@': 402}), 3: (1, {'@': 402}), 32: (1, {'@': 402}), 115: (1, {'@': 402}), 5: (1, {'@': 402}), 116: (1, {'@': 402}), 117: (1, {'@': 402}), 118: (1, {'@': 402}), 119: (1, {'@': 402}), 120: (1, {'@': 402}), 121: (1, {'@': 402}), 6: (1, {'@': 402}), 122: (1, {'@': 402}), 123: (1, {'@': 402}), 124: (1, {'@': 402})}, 161: {11: (1, {'@': 172}), 12: (1, {'@': 172}), 13: (1, {'@': 172}), 5: (1, {'@': 172}), 6: (1, {'@': 172})}, 162: {12: (0, 645), 187: (0, 44), 175: (0, 663), 120: (0, 550), 166: (0, 532), 188: (0, 592), 165: (0, 586), 185: (0, 771), 164: (0, 531), 189: (0, 463), 190: (0, 446)}, 163: {10: (0, 82)}, 164: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 191: (0, 62), 81: (0, 786), 11: (0, 632), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 168: (0, 407), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 116: (0, 774), 155: (0, 651), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 192: (0, 92), 193: (0, 480), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 194: (0, 361), 97: (0, 700), 106: (0, 762), 108: (0, 518), 119: (0, 674), 150: (0, 741), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 120: (1, {'@': 148}), 13: (1, {'@': 148})}, 165: {4: (0, 284)}, 166: {33: (1, {'@': 179}), 3: (1, {'@': 179})}, 167: {9: (1, {'@': 164}), 12: (1, {'@': 164}), 13: (1, {'@': 164}), 5: (1, {'@': 164}), 7: (1, {'@': 164}), 4: (1, {'@': 164}), 11: (1, {'@': 164}), 170: (1, {'@': 164})}, 168: {11: (1, {'@': 130}), 13: (1, {'@': 130}), 12: (1, {'@': 130})}, 169: {71: (0, 725), 55: (1, {'@': 406}), 56: (1, {'@': 406}), 57: (1, {'@': 406}), 58: (1, {'@': 406}), 59: (1, {'@': 406}), 60: (1, {'@': 406}), 64: (1, {'@': 406}), 47: (1, {'@': 406}), 62: (1, {'@': 406}), 63: (1, {'@': 406}), 65: (1, {'@': 406}), 66: (1, {'@': 406}), 68: (1, {'@': 406}), 54: (1, {'@': 406}), 69: (1, {'@': 406}), 67: (1, {'@': 406}), 4: (1, {'@': 406}), 12: (1, {'@': 406}), 13: (1, {'@': 406}), 11: (1, {'@': 406}), 72: (1, {'@': 406}), 73: (1, {'@': 406}), 74: (1, {'@': 406}), 75: (1, {'@': 406}), 76: (1, {'@': 406}), 77: (1, {'@': 406}), 78: (1, {'@': 406}), 79: (1, {'@': 406}), 80: (1, {'@': 406}), 81: (1, {'@': 406}), 10: (1, {'@': 406}), 82: (1, {'@': 406}), 83: (1, {'@': 406}), 84: (1, {'@': 406}), 85: (1, {'@': 406}), 86: (1, {'@': 406}), 87: (1, {'@': 406}), 88: (1, {'@': 406}), 89: (1, {'@': 406}), 90: (1, {'@': 406}), 91: (1, {'@': 406}), 92: (1, {'@': 406}), 93: (1, {'@': 406}), 94: (1, {'@': 406}), 95: (1, {'@': 406}), 96: (1, {'@': 406}), 97: (1, {'@': 406}), 98: (1, {'@': 406}), 99: (1, {'@': 406}), 100: (1, {'@': 406}), 101: (1, {'@': 406}), 102: (1, {'@': 406}), 43: (1, {'@': 406}), 103: (1, {'@': 406}), 104: (1, {'@': 406}), 105: (1, {'@': 406}), 106: (1, {'@': 406}), 107: (1, {'@': 406}), 108: (1, {'@': 406}), 109: (1, {'@': 406}), 1: (1, {'@': 406}), 110: (1, {'@': 406}), 111: (1, {'@': 406}), 112: (1, {'@': 406}), 113: (1, {'@': 406}), 114: (1, {'@': 406}), 3: (1, {'@': 406}), 32: (1, {'@': 406}), 115: (1, {'@': 406}), 5: (1, {'@': 406}), 116: (1, {'@': 406}), 117: (1, {'@': 406}), 118: (1, {'@': 406}), 119: (1, {'@': 406}), 120: (1, {'@': 406}), 121: (1, {'@': 406}), 6: (1, {'@': 406}), 122: (1, {'@': 406}), 123: (1, {'@': 406}), 124: (1, {'@': 406})}, 170: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 90), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 171: {72: (1, {'@': 309}), 56: (1, {'@': 309}), 78: (1, {'@': 309}), 80: (1, {'@': 309}), 81: (1, {'@': 309}), 68: (1, {'@': 309}), 84: (1, {'@': 309}), 69: (1, {'@': 309}), 86: (1, {'@': 309}), 71: (1, {'@': 309}), 87: (1, {'@': 309}), 89: (1, {'@': 309}), 12: (1, {'@': 309}), 93: (1, {'@': 309}), 60: (1, {'@': 309}), 63: (1, {'@': 309}), 62: (1, {'@': 309}), 97: (1, {'@': 309}), 99: (1, {'@': 309}), 100: (1, {'@': 309}), 102: (1, {'@': 309}), 43: (1, {'@': 309}), 103: (1, {'@': 309}), 105: (1, {'@': 309}), 107: (1, {'@': 309}), 108: (1, {'@': 309}), 112: (1, {'@': 309}), 70: (1, {'@': 309}), 67: (1, {'@': 309}), 64: (1, {'@': 309}), 3: (1, {'@': 309}), 9: (1, {'@': 309}), 115: (1, {'@': 309}), 5: (1, {'@': 309}), 116: (1, {'@': 309}), 125: (1, {'@': 309}), 59: (1, {'@': 309}), 120: (1, {'@': 309}), 65: (1, {'@': 309}), 6: (1, {'@': 309}), 54: (1, {'@': 309}), 123: (1, {'@': 309}), 73: (1, {'@': 309}), 74: (1, {'@': 309}), 75: (1, {'@': 309}), 76: (1, {'@': 309}), 77: (1, {'@': 309}), 79: (1, {'@': 309}), 61: (1, {'@': 309}), 10: (1, {'@': 309}), 82: (1, {'@': 309}), 66: (1, {'@': 309}), 83: (1, {'@': 309}), 85: (1, {'@': 309}), 11: (1, {'@': 309}), 88: (1, {'@': 309}), 90: (1, {'@': 309}), 91: (1, {'@': 309}), 92: (1, {'@': 309}), 94: (1, {'@': 309}), 47: (1, {'@': 309}), 95: (1, {'@': 309}), 96: (1, {'@': 309}), 4: (1, {'@': 309}), 55: (1, {'@': 309}), 98: (1, {'@': 309}), 101: (1, {'@': 309}), 13: (1, {'@': 309}), 104: (1, {'@': 309}), 106: (1, {'@': 309}), 58: (1, {'@': 309}), 109: (1, {'@': 309}), 1: (1, {'@': 309}), 110: (1, {'@': 309}), 111: (1, {'@': 309}), 113: (1, {'@': 309}), 114: (1, {'@': 309}), 32: (1, {'@': 309}), 57: (1, {'@': 309}), 117: (1, {'@': 309}), 118: (1, {'@': 309}), 119: (1, {'@': 309}), 121: (1, {'@': 309}), 122: (1, {'@': 309}), 124: (1, {'@': 309})}, 172: {9: (0, 563), 172: (0, 59), 4: (0, 24), 171: (0, 60)}, 173: {3: (1, {'@': 331})}, 174: {5: (0, 413), 11: (1, {'@': 97}), 13: (1, {'@': 97}), 12: (1, {'@': 97})}, 175: {11: (1, {'@': 116}), 13: (1, {'@': 116}), 12: (1, {'@': 116})}, 176: {72: (1, {'@': 3}), 74: (1, {'@': 3}), 75: (1, {'@': 3}), 76: (1, {'@': 3}), 77: (1, {'@': 3}), 78: (1, {'@': 3}), 166: (1, {'@': 3}), 79: (1, {'@': 3}), 80: (1, {'@': 3}), 81: (1, {'@': 3}), 82: (1, {'@': 3}), 44: (1, {'@': 3}), 83: (1, {'@': 3}), 84: (1, {'@': 3}), 85: (1, {'@': 3}), 11: (1, {'@': 3}), 86: (1, {'@': 3}), 163: (1, {'@': 3}), 45: (1, {'@': 3}), 87: (1, {'@': 3}), 88: (1, {'@': 3}), 89: (1, {'@': 3}), 12: (1, {'@': 3}), 91: (1, {'@': 3}), 92: (1, {'@': 3}), 93: (1, {'@': 3}), 94: (1, {'@': 3}), 95: (1, {'@': 3}), 96: (1, {'@': 3}), 164: (1, {'@': 3}), 165: (1, {'@': 3}), 97: (1, {'@': 3}), 167: (1, {'@': 3}), 98: (1, {'@': 3}), 99: (1, {'@': 3}), 100: (1, {'@': 3}), 101: (1, {'@': 3}), 102: (1, {'@': 3}), 13: (1, {'@': 3}), 103: (1, {'@': 3}), 105: (1, {'@': 3}), 104: (1, {'@': 3}), 106: (1, {'@': 3}), 107: (1, {'@': 3}), 108: (1, {'@': 3}), 109: (1, {'@': 3}), 1: (1, {'@': 3}), 110: (1, {'@': 3}), 111: (1, {'@': 3}), 112: (1, {'@': 3}), 113: (1, {'@': 3}), 114: (1, {'@': 3}), 3: (1, {'@': 3}), 115: (1, {'@': 3}), 116: (1, {'@': 3}), 117: (1, {'@': 3}), 118: (1, {'@': 3}), 119: (1, {'@': 3}), 120: (1, {'@': 3}), 121: (1, {'@': 3}), 122: (1, {'@': 3}), 123: (1, {'@': 3}), 124: (1, {'@': 3}), 195: (1, {'@': 3}), 168: (1, {'@': 3})}, 177: {11: (0, 300), 12: (1, {'@': 190}), 13: (1, {'@': 190})}, 178: {10: (1, {'@': 359}), 12: (1, {'@': 359}), 13: (1, {'@': 359}), 5: (1, {'@': 359}), 4: (1, {'@': 359}), 11: (1, {'@': 359}), 169: (1, {'@': 359}), 170: (1, {'@': 359})}, 179: {4: (0, 221)}, 180: {10: (0, 695), 5: (0, 605)}, 181: {4: (0, 214)}, 182: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 131: (0, 386), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 183: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 521), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 196: (0, 180), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 197: (0, 708), 25: (0, 203), 1: (0, 614), 134: (0, 433), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 10: (0, 209), 20: (0, 247), 138: (0, 287), 131: (0, 114), 54: (0, 222), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 184: {10: (0, 370)}, 185: {5: (1, {'@': 162}), 7: (1, {'@': 162}), 10: (1, {'@': 162}), 4: (1, {'@': 162}), 9: (1, {'@': 162}), 12: (1, {'@': 162}), 13: (1, {'@': 162}), 11: (1, {'@': 162}), 170: (1, {'@': 162})}, 186: {10: (0, 412), 54: (0, 88)}, 187: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486), 131: (0, 376)}, 188: {5: (1, {'@': 253}), 11: (1, {'@': 253}), 12: (1, {'@': 253}), 13: (1, {'@': 253})}, 189: {10: (1, {'@': 357}), 12: (1, {'@': 357}), 13: (1, {'@': 357}), 5: (1, {'@': 357}), 4: (1, {'@': 357}), 11: (1, {'@': 357}), 169: (1, {'@': 357}), 170: (1, {'@': 357})}, 190: {5: (1, {'@': 353}), 10: (1, {'@': 353}), 4: (1, {'@': 353}), 11: (1, {'@': 353}), 12: (1, {'@': 353}), 13: (1, {'@': 353}), 169: (1, {'@': 353}), 170: (1, {'@': 353})}, 191: {72: (1, {'@': 219}), 74: (1, {'@': 219}), 98: (1, {'@': 219}), 75: (1, {'@': 219}), 99: (1, {'@': 219}), 100: (1, {'@': 219}), 101: (1, {'@': 219}), 102: (1, {'@': 219}), 76: (1, {'@': 219}), 13: (1, {'@': 219}), 103: (1, {'@': 219}), 104: (1, {'@': 219}), 105: (1, {'@': 219}), 78: (1, {'@': 219}), 77: (1, {'@': 219}), 106: (1, {'@': 219}), 107: (1, {'@': 219}), 79: (1, {'@': 219}), 108: (1, {'@': 219}), 80: (1, {'@': 219}), 81: (1, {'@': 219}), 82: (1, {'@': 219}), 109: (1, {'@': 219}), 83: (1, {'@': 219}), 1: (1, {'@': 219}), 110: (1, {'@': 219}), 111: (1, {'@': 219}), 84: (1, {'@': 219}), 112: (1, {'@': 219}), 85: (1, {'@': 219}), 11: (1, {'@': 219}), 86: (1, {'@': 219}), 113: (1, {'@': 219}), 114: (1, {'@': 219}), 87: (1, {'@': 219}), 3: (1, {'@': 219}), 88: (1, {'@': 219}), 89: (1, {'@': 219}), 115: (1, {'@': 219}), 116: (1, {'@': 219}), 117: (1, {'@': 219}), 118: (1, {'@': 219}), 91: (1, {'@': 219}), 119: (1, {'@': 219}), 92: (1, {'@': 219}), 93: (1, {'@': 219}), 94: (1, {'@': 219}), 120: (1, {'@': 219}), 95: (1, {'@': 219}), 96: (1, {'@': 219}), 121: (1, {'@': 219}), 123: (1, {'@': 219}), 97: (1, {'@': 219}), 124: (1, {'@': 219})}, 192: {173: (0, 326), 121: (0, 335), 11: (1, {'@': 68}), 13: (1, {'@': 68}), 12: (1, {'@': 68})}, 193: {47: (0, 607), 66: (0, 515), 55: (1, {'@': 399}), 62: (1, {'@': 399}), 56: (1, {'@': 399}), 65: (1, {'@': 399}), 68: (1, {'@': 399}), 57: (1, {'@': 399}), 54: (1, {'@': 399}), 69: (1, {'@': 399}), 67: (1, {'@': 399}), 4: (1, {'@': 399}), 58: (1, {'@': 399}), 59: (1, {'@': 399}), 60: (1, {'@': 399}), 64: (1, {'@': 399}), 63: (1, {'@': 399}), 12: (1, {'@': 399}), 13: (1, {'@': 399}), 11: (1, {'@': 399}), 72: (1, {'@': 399}), 73: (1, {'@': 399}), 74: (1, {'@': 399}), 75: (1, {'@': 399}), 76: (1, {'@': 399}), 77: (1, {'@': 399}), 78: (1, {'@': 399}), 79: (1, {'@': 399}), 80: (1, {'@': 399}), 81: (1, {'@': 399}), 10: (1, {'@': 399}), 82: (1, {'@': 399}), 83: (1, {'@': 399}), 84: (1, {'@': 399}), 85: (1, {'@': 399}), 86: (1, {'@': 399}), 87: (1, {'@': 399}), 88: (1, {'@': 399}), 89: (1, {'@': 399}), 90: (1, {'@': 399}), 91: (1, {'@': 399}), 92: (1, {'@': 399}), 93: (1, {'@': 399}), 94: (1, {'@': 399}), 95: (1, {'@': 399}), 96: (1, {'@': 399}), 97: (1, {'@': 399}), 98: (1, {'@': 399}), 99: (1, {'@': 399}), 100: (1, {'@': 399}), 101: (1, {'@': 399}), 102: (1, {'@': 399}), 43: (1, {'@': 399}), 103: (1, {'@': 399}), 104: (1, {'@': 399}), 105: (1, {'@': 399}), 106: (1, {'@': 399}), 107: (1, {'@': 399}), 108: (1, {'@': 399}), 109: (1, {'@': 399}), 1: (1, {'@': 399}), 110: (1, {'@': 399}), 111: (1, {'@': 399}), 112: (1, {'@': 399}), 113: (1, {'@': 399}), 114: (1, {'@': 399}), 3: (1, {'@': 399}), 32: (1, {'@': 399}), 115: (1, {'@': 399}), 5: (1, {'@': 399}), 116: (1, {'@': 399}), 117: (1, {'@': 399}), 118: (1, {'@': 399}), 119: (1, {'@': 399}), 120: (1, {'@': 399}), 121: (1, {'@': 399}), 6: (1, {'@': 399}), 122: (1, {'@': 399}), 123: (1, {'@': 399}), 124: (1, {'@': 399})}, 194: {31: (0, 3), 25: (0, 203), 9: (0, 677), 22: (0, 11), 171: (0, 706), 50: (0, 125), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 23: (0, 219), 53: (0, 696), 29: (0, 474), 14: (0, 493), 15: (0, 205), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 24: (0, 408), 40: (0, 503), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 195: {4: (0, 16)}, 196: {47: (0, 607), 66: (0, 515), 55: (1, {'@': 401}), 62: (1, {'@': 401}), 56: (1, {'@': 401}), 65: (1, {'@': 401}), 68: (1, {'@': 401}), 57: (1, {'@': 401}), 54: (1, {'@': 401}), 69: (1, {'@': 401}), 67: (1, {'@': 401}), 4: (1, {'@': 401}), 58: (1, {'@': 401}), 59: (1, {'@': 401}), 60: (1, {'@': 401}), 64: (1, {'@': 401}), 63: (1, {'@': 401}), 12: (1, {'@': 401}), 13: (1, {'@': 401}), 11: (1, {'@': 401}), 72: (1, {'@': 401}), 73: (1, {'@': 401}), 74: (1, {'@': 401}), 75: (1, {'@': 401}), 76: (1, {'@': 401}), 77: (1, {'@': 401}), 78: (1, {'@': 401}), 79: (1, {'@': 401}), 80: (1, {'@': 401}), 81: (1, {'@': 401}), 10: (1, {'@': 401}), 82: (1, {'@': 401}), 83: (1, {'@': 401}), 84: (1, {'@': 401}), 85: (1, {'@': 401}), 86: (1, {'@': 401}), 87: (1, {'@': 401}), 88: (1, {'@': 401}), 89: (1, {'@': 401}), 90: (1, {'@': 401}), 91: (1, {'@': 401}), 92: (1, {'@': 401}), 93: (1, {'@': 401}), 94: (1, {'@': 401}), 95: (1, {'@': 401}), 96: (1, {'@': 401}), 97: (1, {'@': 401}), 98: (1, {'@': 401}), 99: (1, {'@': 401}), 100: (1, {'@': 401}), 101: (1, {'@': 401}), 102: (1, {'@': 401}), 43: (1, {'@': 401}), 103: (1, {'@': 401}), 104: (1, {'@': 401}), 105: (1, {'@': 401}), 106: (1, {'@': 401}), 107: (1, {'@': 401}), 108: (1, {'@': 401}), 109: (1, {'@': 401}), 1: (1, {'@': 401}), 110: (1, {'@': 401}), 111: (1, {'@': 401}), 112: (1, {'@': 401}), 113: (1, {'@': 401}), 114: (1, {'@': 401}), 3: (1, {'@': 401}), 32: (1, {'@': 401}), 115: (1, {'@': 401}), 5: (1, {'@': 401}), 116: (1, {'@': 401}), 117: (1, {'@': 401}), 118: (1, {'@': 401}), 119: (1, {'@': 401}), 120: (1, {'@': 401}), 121: (1, {'@': 401}), 6: (1, {'@': 401}), 122: (1, {'@': 401}), 123: (1, {'@': 401}), 124: (1, {'@': 401})}, 197: {11: (1, {'@': 200}), 12: (1, {'@': 200}), 13: (1, {'@': 200})}, 198: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 122: (0, 406), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 156: (0, 747), 93: (0, 630), 13: (0, 599), 98: (0, 544), 11: (0, 602), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 150: (0, 705), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 199: {11: (1, {'@': 201}), 12: (1, {'@': 201}), 13: (1, {'@': 201})}, 200: {10: (1, {'@': 356}), 12: (1, {'@': 356}), 13: (1, {'@': 356}), 5: (1, {'@': 356}), 4: (1, {'@': 356}), 11: (1, {'@': 356}), 169: (1, {'@': 356}), 170: (1, {'@': 356})}, 201: {1: (0, 185), 2: (0, 4), 142: (0, 0), 0: (0, 355), 141: (0, 77), 3: (0, 264), 143: (0, 374)}, 202: {10: (0, 20), 5: (0, 380)}, 203: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 23: (0, 219), 29: (0, 474), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 24: (0, 408), 40: (0, 503), 53: (0, 422), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 204: {10: (0, 328), 54: (0, 88), 5: (1, {'@': 319})}, 205: {14: (1, {'@': 378}), 15: (1, {'@': 378}), 16: (1, {'@': 378}), 17: (1, {'@': 378}), 18: (1, {'@': 378}), 19: (1, {'@': 378}), 20: (1, {'@': 378}), 21: (1, {'@': 378}), 22: (1, {'@': 378}), 23: (1, {'@': 378}), 24: (1, {'@': 378}), 25: (1, {'@': 378}), 1: (1, {'@': 378}), 26: (1, {'@': 378}), 27: (1, {'@': 378}), 28: (1, {'@': 378}), 29: (1, {'@': 378}), 30: (1, {'@': 378}), 3: (1, {'@': 378}), 31: (1, {'@': 378}), 9: (1, {'@': 378}), 32: (1, {'@': 378}), 33: (1, {'@': 378}), 34: (1, {'@': 378}), 35: (1, {'@': 378}), 36: (1, {'@': 378}), 37: (1, {'@': 378}), 38: (1, {'@': 378}), 39: (1, {'@': 378}), 40: (1, {'@': 378}), 41: (1, {'@': 378}), 42: (1, {'@': 378})}, 206: {72: (1, {'@': 5}), 74: (1, {'@': 5}), 75: (1, {'@': 5}), 76: (1, {'@': 5}), 77: (1, {'@': 5}), 78: (1, {'@': 5}), 166: (1, {'@': 5}), 79: (1, {'@': 5}), 80: (1, {'@': 5}), 81: (1, {'@': 5}), 82: (1, {'@': 5}), 44: (1, {'@': 5}), 83: (1, {'@': 5}), 84: (1, {'@': 5}), 85: (1, {'@': 5}), 11: (1, {'@': 5}), 86: (1, {'@': 5}), 163: (1, {'@': 5}), 45: (1, {'@': 5}), 87: (1, {'@': 5}), 88: (1, {'@': 5}), 89: (1, {'@': 5}), 12: (1, {'@': 5}), 91: (1, {'@': 5}), 92: (1, {'@': 5}), 93: (1, {'@': 5}), 94: (1, {'@': 5}), 95: (1, {'@': 5}), 96: (1, {'@': 5}), 164: (1, {'@': 5}), 165: (1, {'@': 5}), 97: (1, {'@': 5}), 167: (1, {'@': 5}), 98: (1, {'@': 5}), 99: (1, {'@': 5}), 100: (1, {'@': 5}), 101: (1, {'@': 5}), 102: (1, {'@': 5}), 13: (1, {'@': 5}), 103: (1, {'@': 5}), 105: (1, {'@': 5}), 104: (1, {'@': 5}), 106: (1, {'@': 5}), 107: (1, {'@': 5}), 108: (1, {'@': 5}), 109: (1, {'@': 5}), 1: (1, {'@': 5}), 110: (1, {'@': 5}), 111: (1, {'@': 5}), 112: (1, {'@': 5}), 113: (1, {'@': 5}), 114: (1, {'@': 5}), 3: (1, {'@': 5}), 115: (1, {'@': 5}), 116: (1, {'@': 5}), 117: (1, {'@': 5}), 118: (1, {'@': 5}), 119: (1, {'@': 5}), 120: (1, {'@': 5}), 121: (1, {'@': 5}), 122: (1, {'@': 5}), 123: (1, {'@': 5}), 124: (1, {'@': 5}), 195: (1, {'@': 5}), 168: (1, {'@': 5})}, 207: {198: (0, 441), 4: (0, 58), 5: (1, {'@': 342}), 10: (1, {'@': 342})}, 208: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 131: (0, 338), 139: (0, 505), 22: (0, 11), 42: (0, 233), 10: (0, 323), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 209: {72: (1, {'@': 315}), 56: (1, {'@': 315}), 78: (1, {'@': 315}), 80: (1, {'@': 315}), 81: (1, {'@': 315}), 68: (1, {'@': 315}), 84: (1, {'@': 315}), 69: (1, {'@': 315}), 86: (1, {'@': 315}), 71: (1, {'@': 315}), 87: (1, {'@': 315}), 89: (1, {'@': 315}), 12: (1, {'@': 315}), 93: (1, {'@': 315}), 60: (1, {'@': 315}), 63: (1, {'@': 315}), 62: (1, {'@': 315}), 97: (1, {'@': 315}), 99: (1, {'@': 315}), 100: (1, {'@': 315}), 102: (1, {'@': 315}), 43: (1, {'@': 315}), 103: (1, {'@': 315}), 105: (1, {'@': 315}), 107: (1, {'@': 315}), 108: (1, {'@': 315}), 112: (1, {'@': 315}), 67: (1, {'@': 315}), 70: (1, {'@': 315}), 64: (1, {'@': 315}), 3: (1, {'@': 315}), 9: (1, {'@': 315}), 115: (1, {'@': 315}), 5: (1, {'@': 315}), 116: (1, {'@': 315}), 125: (1, {'@': 315}), 59: (1, {'@': 315}), 120: (1, {'@': 315}), 65: (1, {'@': 315}), 6: (1, {'@': 315}), 54: (1, {'@': 315}), 123: (1, {'@': 315}), 73: (1, {'@': 315}), 74: (1, {'@': 315}), 75: (1, {'@': 315}), 76: (1, {'@': 315}), 77: (1, {'@': 315}), 79: (1, {'@': 315}), 61: (1, {'@': 315}), 10: (1, {'@': 315}), 82: (1, {'@': 315}), 66: (1, {'@': 315}), 83: (1, {'@': 315}), 85: (1, {'@': 315}), 11: (1, {'@': 315}), 88: (1, {'@': 315}), 90: (1, {'@': 315}), 91: (1, {'@': 315}), 92: (1, {'@': 315}), 94: (1, {'@': 315}), 47: (1, {'@': 315}), 95: (1, {'@': 315}), 96: (1, {'@': 315}), 4: (1, {'@': 315}), 55: (1, {'@': 315}), 98: (1, {'@': 315}), 101: (1, {'@': 315}), 13: (1, {'@': 315}), 104: (1, {'@': 315}), 106: (1, {'@': 315}), 58: (1, {'@': 315}), 109: (1, {'@': 315}), 1: (1, {'@': 315}), 110: (1, {'@': 315}), 111: (1, {'@': 315}), 113: (1, {'@': 315}), 114: (1, {'@': 315}), 32: (1, {'@': 315}), 57: (1, {'@': 315}), 117: (1, {'@': 315}), 118: (1, {'@': 315}), 119: (1, {'@': 315}), 121: (1, {'@': 315}), 122: (1, {'@': 315}), 124: (1, {'@': 315})}, 210: {54: (0, 88), 5: (1, {'@': 319}), 10: (1, {'@': 319})}, 211: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 191: (0, 62), 81: (0, 786), 11: (0, 632), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 116: (0, 774), 155: (0, 651), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 192: (0, 92), 193: (0, 480), 122: (0, 733), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 194: (0, 361), 97: (0, 700), 106: (0, 762), 108: (0, 518), 119: (0, 674), 150: (0, 741), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 13: (1, {'@': 148})}, 212: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 136: (0, 403), 23: (0, 219), 29: (0, 474), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 135: (0, 101), 49: (0, 373), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 213: {72: (1, {'@': 286}), 56: (1, {'@': 286}), 78: (1, {'@': 286}), 80: (1, {'@': 286}), 81: (1, {'@': 286}), 68: (1, {'@': 286}), 84: (1, {'@': 286}), 69: (1, {'@': 286}), 86: (1, {'@': 286}), 71: (1, {'@': 286}), 87: (1, {'@': 286}), 89: (1, {'@': 286}), 12: (1, {'@': 286}), 93: (1, {'@': 286}), 60: (1, {'@': 286}), 63: (1, {'@': 286}), 62: (1, {'@': 286}), 97: (1, {'@': 286}), 99: (1, {'@': 286}), 100: (1, {'@': 286}), 102: (1, {'@': 286}), 43: (1, {'@': 286}), 103: (1, {'@': 286}), 105: (1, {'@': 286}), 107: (1, {'@': 286}), 108: (1, {'@': 286}), 112: (1, {'@': 286}), 70: (1, {'@': 286}), 64: (1, {'@': 286}), 67: (1, {'@': 286}), 3: (1, {'@': 286}), 9: (1, {'@': 286}), 115: (1, {'@': 286}), 116: (1, {'@': 286}), 5: (1, {'@': 286}), 125: (1, {'@': 286}), 59: (1, {'@': 286}), 120: (1, {'@': 286}), 65: (1, {'@': 286}), 6: (1, {'@': 286}), 54: (1, {'@': 286}), 123: (1, {'@': 286}), 73: (1, {'@': 286}), 74: (1, {'@': 286}), 75: (1, {'@': 286}), 76: (1, {'@': 286}), 77: (1, {'@': 286}), 79: (1, {'@': 286}), 61: (1, {'@': 286}), 82: (1, {'@': 286}), 10: (1, {'@': 286}), 66: (1, {'@': 286}), 83: (1, {'@': 286}), 85: (1, {'@': 286}), 11: (1, {'@': 286}), 88: (1, {'@': 286}), 90: (1, {'@': 286}), 91: (1, {'@': 286}), 92: (1, {'@': 286}), 94: (1, {'@': 286}), 47: (1, {'@': 286}), 95: (1, {'@': 286}), 96: (1, {'@': 286}), 4: (1, {'@': 286}), 55: (1, {'@': 286}), 98: (1, {'@': 286}), 101: (1, {'@': 286}), 13: (1, {'@': 286}), 104: (1, {'@': 286}), 106: (1, {'@': 286}), 58: (1, {'@': 286}), 109: (1, {'@': 286}), 1: (1, {'@': 286}), 110: (1, {'@': 286}), 111: (1, {'@': 286}), 113: (1, {'@': 286}), 114: (1, {'@': 286}), 32: (1, {'@': 286}), 57: (1, {'@': 286}), 117: (1, {'@': 286}), 118: (1, {'@': 286}), 119: (1, {'@': 286}), 121: (1, {'@': 286}), 122: (1, {'@': 286}), 124: (1, {'@': 286})}, 214: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 131: (0, 442), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 215: {72: (1, {'@': 281}), 56: (1, {'@': 281}), 78: (1, {'@': 281}), 80: (1, {'@': 281}), 81: (1, {'@': 281}), 68: (1, {'@': 281}), 84: (1, {'@': 281}), 69: (1, {'@': 281}), 86: (1, {'@': 281}), 71: (1, {'@': 281}), 87: (1, {'@': 281}), 89: (1, {'@': 281}), 12: (1, {'@': 281}), 93: (1, {'@': 281}), 60: (1, {'@': 281}), 63: (1, {'@': 281}), 62: (1, {'@': 281}), 97: (1, {'@': 281}), 99: (1, {'@': 281}), 100: (1, {'@': 281}), 102: (1, {'@': 281}), 43: (1, {'@': 281}), 103: (1, {'@': 281}), 105: (1, {'@': 281}), 107: (1, {'@': 281}), 108: (1, {'@': 281}), 112: (1, {'@': 281}), 70: (1, {'@': 281}), 64: (1, {'@': 281}), 67: (1, {'@': 281}), 3: (1, {'@': 281}), 9: (1, {'@': 281}), 115: (1, {'@': 281}), 116: (1, {'@': 281}), 5: (1, {'@': 281}), 125: (1, {'@': 281}), 59: (1, {'@': 281}), 120: (1, {'@': 281}), 65: (1, {'@': 281}), 6: (1, {'@': 281}), 54: (1, {'@': 281}), 123: (1, {'@': 281}), 73: (1, {'@': 281}), 74: (1, {'@': 281}), 75: (1, {'@': 281}), 76: (1, {'@': 281}), 77: (1, {'@': 281}), 79: (1, {'@': 281}), 61: (1, {'@': 281}), 82: (1, {'@': 281}), 10: (1, {'@': 281}), 66: (1, {'@': 281}), 83: (1, {'@': 281}), 85: (1, {'@': 281}), 11: (1, {'@': 281}), 88: (1, {'@': 281}), 90: (1, {'@': 281}), 91: (1, {'@': 281}), 92: (1, {'@': 281}), 94: (1, {'@': 281}), 47: (1, {'@': 281}), 95: (1, {'@': 281}), 96: (1, {'@': 281}), 4: (1, {'@': 281}), 55: (1, {'@': 281}), 98: (1, {'@': 281}), 101: (1, {'@': 281}), 13: (1, {'@': 281}), 104: (1, {'@': 281}), 106: (1, {'@': 281}), 58: (1, {'@': 281}), 109: (1, {'@': 281}), 1: (1, {'@': 281}), 110: (1, {'@': 281}), 111: (1, {'@': 281}), 113: (1, {'@': 281}), 114: (1, {'@': 281}), 32: (1, {'@': 281}), 57: (1, {'@': 281}), 117: (1, {'@': 281}), 118: (1, {'@': 281}), 119: (1, {'@': 281}), 121: (1, {'@': 281}), 122: (1, {'@': 281}), 124: (1, {'@': 281})}, 216: {5: (0, 605), 11: (1, {'@': 74}), 13: (1, {'@': 74}), 12: (1, {'@': 74})}, 217: {11: (1, {'@': 44}), 13: (1, {'@': 44}), 12: (1, {'@': 44})}, 218: {4: (0, 330), 169: (0, 371), 11: (1, {'@': 166}), 170: (1, {'@': 166}), 13: (1, {'@': 166}), 12: (1, {'@': 166})}, 219: {14: (1, {'@': 377}), 15: (1, {'@': 377}), 16: (1, {'@': 377}), 17: (1, {'@': 377}), 18: (1, {'@': 377}), 19: (1, {'@': 377}), 20: (1, {'@': 377}), 21: (1, {'@': 377}), 22: (1, {'@': 377}), 23: (1, {'@': 377}), 24: (1, {'@': 377}), 25: (1, {'@': 377}), 1: (1, {'@': 377}), 26: (1, {'@': 377}), 27: (1, {'@': 377}), 28: (1, {'@': 377}), 29: (1, {'@': 377}), 30: (1, {'@': 377}), 3: (1, {'@': 377}), 31: (1, {'@': 377}), 9: (1, {'@': 377}), 32: (1, {'@': 377}), 33: (1, {'@': 377}), 34: (1, {'@': 377}), 35: (1, {'@': 377}), 36: (1, {'@': 377}), 37: (1, {'@': 377}), 38: (1, {'@': 377}), 39: (1, {'@': 377}), 40: (1, {'@': 377}), 41: (1, {'@': 377}), 42: (1, {'@': 377})}, 220: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 131: (0, 61), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 221: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 131: (0, 438), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 222: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 10: (0, 232), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 131: (0, 235), 37: (0, 494), 48: (0, 486)}, 223: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 150: (0, 81), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 165: (0, 586), 156: (0, 747), 11: (0, 73), 93: (0, 630), 164: (0, 531), 185: (0, 344), 98: (0, 544), 118: (0, 548), 157: (0, 552), 13: (0, 627), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 175: (0, 619), 94: (0, 584), 82: (0, 568), 159: (0, 595), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 224: {5: (1, {'@': 340}), 10: (1, {'@': 340})}, 225: {5: (1, {'@': 248}), 11: (1, {'@': 248}), 12: (1, {'@': 248}), 43: (1, {'@': 248}), 13: (1, {'@': 248})}, 226: {5: (1, {'@': 337}), 10: (1, {'@': 337})}, 227: {11: (1, {'@': 34}), 13: (1, {'@': 34}), 12: (1, {'@': 34})}, 228: {11: (1, {'@': 40}), 13: (1, {'@': 40}), 12: (1, {'@': 40})}, 229: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 10: (0, 1), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 131: (0, 35), 48: (0, 486)}, 230: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 95), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 231: {3: (0, 391), 11: (1, {'@': 216}), 12: (1, {'@': 216}), 13: (1, {'@': 216})}, 232: {72: (1, {'@': 312}), 56: (1, {'@': 312}), 78: (1, {'@': 312}), 80: (1, {'@': 312}), 81: (1, {'@': 312}), 68: (1, {'@': 312}), 84: (1, {'@': 312}), 69: (1, {'@': 312}), 86: (1, {'@': 312}), 71: (1, {'@': 312}), 87: (1, {'@': 312}), 89: (1, {'@': 312}), 12: (1, {'@': 312}), 93: (1, {'@': 312}), 60: (1, {'@': 312}), 63: (1, {'@': 312}), 62: (1, {'@': 312}), 97: (1, {'@': 312}), 99: (1, {'@': 312}), 100: (1, {'@': 312}), 102: (1, {'@': 312}), 43: (1, {'@': 312}), 103: (1, {'@': 312}), 105: (1, {'@': 312}), 107: (1, {'@': 312}), 108: (1, {'@': 312}), 112: (1, {'@': 312}), 70: (1, {'@': 312}), 67: (1, {'@': 312}), 64: (1, {'@': 312}), 3: (1, {'@': 312}), 9: (1, {'@': 312}), 115: (1, {'@': 312}), 5: (1, {'@': 312}), 116: (1, {'@': 312}), 125: (1, {'@': 312}), 59: (1, {'@': 312}), 120: (1, {'@': 312}), 65: (1, {'@': 312}), 6: (1, {'@': 312}), 54: (1, {'@': 312}), 123: (1, {'@': 312}), 73: (1, {'@': 312}), 74: (1, {'@': 312}), 75: (1, {'@': 312}), 76: (1, {'@': 312}), 77: (1, {'@': 312}), 79: (1, {'@': 312}), 61: (1, {'@': 312}), 10: (1, {'@': 312}), 82: (1, {'@': 312}), 66: (1, {'@': 312}), 83: (1, {'@': 312}), 85: (1, {'@': 312}), 11: (1, {'@': 312}), 88: (1, {'@': 312}), 90: (1, {'@': 312}), 91: (1, {'@': 312}), 92: (1, {'@': 312}), 94: (1, {'@': 312}), 47: (1, {'@': 312}), 95: (1, {'@': 312}), 96: (1, {'@': 312}), 4: (1, {'@': 312}), 55: (1, {'@': 312}), 98: (1, {'@': 312}), 101: (1, {'@': 312}), 13: (1, {'@': 312}), 104: (1, {'@': 312}), 106: (1, {'@': 312}), 58: (1, {'@': 312}), 109: (1, {'@': 312}), 1: (1, {'@': 312}), 110: (1, {'@': 312}), 111: (1, {'@': 312}), 113: (1, {'@': 312}), 114: (1, {'@': 312}), 32: (1, {'@': 312}), 57: (1, {'@': 312}), 117: (1, {'@': 312}), 118: (1, {'@': 312}), 119: (1, {'@': 312}), 121: (1, {'@': 312}), 122: (1, {'@': 312}), 124: (1, {'@': 312})}, 233: {14: (1, {'@': 375}), 15: (1, {'@': 375}), 16: (1, {'@': 375}), 17: (1, {'@': 375}), 18: (1, {'@': 375}), 19: (1, {'@': 375}), 20: (1, {'@': 375}), 21: (1, {'@': 375}), 22: (1, {'@': 375}), 23: (1, {'@': 375}), 24: (1, {'@': 375}), 25: (1, {'@': 375}), 1: (1, {'@': 375}), 26: (1, {'@': 375}), 27: (1, {'@': 375}), 28: (1, {'@': 375}), 29: (1, {'@': 375}), 30: (1, {'@': 375}), 3: (1, {'@': 375}), 31: (1, {'@': 375}), 9: (1, {'@': 375}), 32: (1, {'@': 375}), 33: (1, {'@': 375}), 34: (1, {'@': 375}), 35: (1, {'@': 375}), 36: (1, {'@': 375}), 37: (1, {'@': 375}), 38: (1, {'@': 375}), 39: (1, {'@': 375}), 40: (1, {'@': 375}), 41: (1, {'@': 375}), 42: (1, {'@': 375})}, 234: {11: (1, {'@': 37}), 13: (1, {'@': 37}), 12: (1, {'@': 37})}, 235: {10: (0, 461)}, 236: {5: (1, {'@': 338}), 10: (1, {'@': 338})}, 237: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 136: (0, 403), 23: (0, 219), 29: (0, 474), 137: (0, 121), 14: (0, 493), 15: (0, 205), 135: (0, 675), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 238: {11: (1, {'@': 62}), 13: (1, {'@': 62}), 12: (1, {'@': 62})}, 239: {4: (0, 465)}, 240: {61: (0, 212), 70: (0, 362), 55: (1, {'@': 407}), 56: (1, {'@': 407}), 57: (1, {'@': 407}), 58: (1, {'@': 407}), 59: (1, {'@': 407}), 60: (1, {'@': 407}), 47: (1, {'@': 407}), 62: (1, {'@': 407}), 63: (1, {'@': 407}), 65: (1, {'@': 407}), 66: (1, {'@': 407}), 68: (1, {'@': 407}), 54: (1, {'@': 407}), 4: (1, {'@': 407}), 69: (1, {'@': 407}), 67: (1, {'@': 407}), 64: (1, {'@': 407}), 71: (1, {'@': 407}), 12: (1, {'@': 407}), 13: (1, {'@': 407}), 11: (1, {'@': 407}), 72: (1, {'@': 407}), 73: (1, {'@': 407}), 74: (1, {'@': 407}), 75: (1, {'@': 407}), 76: (1, {'@': 407}), 77: (1, {'@': 407}), 78: (1, {'@': 407}), 79: (1, {'@': 407}), 80: (1, {'@': 407}), 81: (1, {'@': 407}), 10: (1, {'@': 407}), 82: (1, {'@': 407}), 83: (1, {'@': 407}), 84: (1, {'@': 407}), 85: (1, {'@': 407}), 86: (1, {'@': 407}), 87: (1, {'@': 407}), 88: (1, {'@': 407}), 89: (1, {'@': 407}), 90: (1, {'@': 407}), 91: (1, {'@': 407}), 92: (1, {'@': 407}), 93: (1, {'@': 407}), 94: (1, {'@': 407}), 95: (1, {'@': 407}), 96: (1, {'@': 407}), 97: (1, {'@': 407}), 98: (1, {'@': 407}), 99: (1, {'@': 407}), 100: (1, {'@': 407}), 101: (1, {'@': 407}), 102: (1, {'@': 407}), 43: (1, {'@': 407}), 103: (1, {'@': 407}), 104: (1, {'@': 407}), 105: (1, {'@': 407}), 106: (1, {'@': 407}), 107: (1, {'@': 407}), 108: (1, {'@': 407}), 109: (1, {'@': 407}), 1: (1, {'@': 407}), 110: (1, {'@': 407}), 111: (1, {'@': 407}), 112: (1, {'@': 407}), 113: (1, {'@': 407}), 114: (1, {'@': 407}), 3: (1, {'@': 407}), 32: (1, {'@': 407}), 115: (1, {'@': 407}), 5: (1, {'@': 407}), 116: (1, {'@': 407}), 117: (1, {'@': 407}), 118: (1, {'@': 407}), 119: (1, {'@': 407}), 120: (1, {'@': 407}), 121: (1, {'@': 407}), 6: (1, {'@': 407}), 122: (1, {'@': 407}), 123: (1, {'@': 407}), 124: (1, {'@': 407})}, 241: {164: (0, 531), 175: (0, 619), 165: (0, 586), 120: (0, 550), 185: (0, 336)}, 242: {9: (1, {'@': 163}), 12: (1, {'@': 163}), 13: (1, {'@': 163}), 5: (1, {'@': 163}), 7: (1, {'@': 163}), 4: (1, {'@': 163}), 11: (1, {'@': 163}), 170: (1, {'@': 163})}, 243: {31: (0, 3), 126: (0, 96), 50: (0, 125), 131: (0, 64), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 244: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 199: (0, 620), 148: (0, 769), 149: (0, 779), 13: (0, 707), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 11: (0, 632), 83: (0, 720), 175: (0, 593), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 200: (0, 731), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 74: (0, 597), 113: (0, 712), 153: (0, 715), 154: (0, 780), 107: (0, 591), 193: (0, 624), 191: (0, 658), 114: (0, 759), 116: (0, 774), 155: (0, 651), 75: (0, 654), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 192: (0, 577), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 201: (0, 618), 117: (0, 626), 202: (0, 625), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 194: (0, 721), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 150: (0, 741), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 165: (1, {'@': 149}), 164: (1, {'@': 149}), 122: (1, {'@': 149}), 12: (1, {'@': 149}), 166: (1, {'@': 149}), 45: (1, {'@': 149}), 44: (1, {'@': 149}), 163: (1, {'@': 149}), 167: (1, {'@': 149})}, 245: {11: (1, {'@': 229}), 12: (1, {'@': 229}), 13: (1, {'@': 229})}, 246: {11: (1, {'@': 102}), 13: (1, {'@': 102}), 12: (1, {'@': 102})}, 247: {14: (1, {'@': 379}), 15: (1, {'@': 379}), 16: (1, {'@': 379}), 17: (1, {'@': 379}), 18: (1, {'@': 379}), 19: (1, {'@': 379}), 20: (1, {'@': 379}), 21: (1, {'@': 379}), 22: (1, {'@': 379}), 23: (1, {'@': 379}), 24: (1, {'@': 379}), 25: (1, {'@': 379}), 1: (1, {'@': 379}), 26: (1, {'@': 379}), 27: (1, {'@': 379}), 28: (1, {'@': 379}), 29: (1, {'@': 379}), 30: (1, {'@': 379}), 3: (1, {'@': 379}), 31: (1, {'@': 379}), 9: (1, {'@': 379}), 32: (1, {'@': 379}), 33: (1, {'@': 379}), 34: (1, {'@': 379}), 35: (1, {'@': 379}), 36: (1, {'@': 379}), 37: (1, {'@': 379}), 38: (1, {'@': 379}), 39: (1, {'@': 379}), 40: (1, {'@': 379}), 41: (1, {'@': 379}), 42: (1, {'@': 379})}, 248: {14: (1, {'@': 263}), 15: (1, {'@': 263}), 16: (1, {'@': 263}), 17: (1, {'@': 263}), 18: (1, {'@': 263}), 19: (1, {'@': 263}), 20: (1, {'@': 263}), 21: (1, {'@': 263}), 22: (1, {'@': 263}), 23: (1, {'@': 263}), 24: (1, {'@': 263}), 25: (1, {'@': 263}), 1: (1, {'@': 263}), 26: (1, {'@': 263}), 27: (1, {'@': 263}), 28: (1, {'@': 263}), 46: (1, {'@': 263}), 29: (1, {'@': 263}), 30: (1, {'@': 263}), 3: (1, {'@': 263}), 31: (1, {'@': 263}), 9: (1, {'@': 263}), 32: (1, {'@': 263}), 34: (1, {'@': 263}), 33: (1, {'@': 263}), 5: (1, {'@': 263}), 35: (1, {'@': 263}), 36: (1, {'@': 263}), 37: (1, {'@': 263}), 47: (1, {'@': 263}), 38: (1, {'@': 263}), 48: (1, {'@': 263}), 39: (1, {'@': 263}), 49: (1, {'@': 263}), 40: (1, {'@': 263}), 41: (1, {'@': 263}), 42: (1, {'@': 263})}, 249: {170: (0, 220), 4: (0, 243), 11: (1, {'@': 153}), 13: (1, {'@': 153}), 12: (1, {'@': 153})}, 250: {3: (0, 137)}, 251: {71: (0, 725), 55: (1, {'@': 403}), 56: (1, {'@': 403}), 57: (1, {'@': 403}), 58: (1, {'@': 403}), 59: (1, {'@': 403}), 60: (1, {'@': 403}), 64: (1, {'@': 403}), 47: (1, {'@': 403}), 62: (1, {'@': 403}), 63: (1, {'@': 403}), 65: (1, {'@': 403}), 66: (1, {'@': 403}), 68: (1, {'@': 403}), 54: (1, {'@': 403}), 69: (1, {'@': 403}), 67: (1, {'@': 403}), 4: (1, {'@': 403}), 12: (1, {'@': 403}), 13: (1, {'@': 403}), 11: (1, {'@': 403}), 72: (1, {'@': 403}), 73: (1, {'@': 403}), 74: (1, {'@': 403}), 75: (1, {'@': 403}), 76: (1, {'@': 403}), 77: (1, {'@': 403}), 78: (1, {'@': 403}), 79: (1, {'@': 403}), 80: (1, {'@': 403}), 81: (1, {'@': 403}), 10: (1, {'@': 403}), 82: (1, {'@': 403}), 83: (1, {'@': 403}), 84: (1, {'@': 403}), 85: (1, {'@': 403}), 86: (1, {'@': 403}), 87: (1, {'@': 403}), 88: (1, {'@': 403}), 89: (1, {'@': 403}), 90: (1, {'@': 403}), 91: (1, {'@': 403}), 92: (1, {'@': 403}), 93: (1, {'@': 403}), 94: (1, {'@': 403}), 95: (1, {'@': 403}), 96: (1, {'@': 403}), 97: (1, {'@': 403}), 98: (1, {'@': 403}), 99: (1, {'@': 403}), 100: (1, {'@': 403}), 101: (1, {'@': 403}), 102: (1, {'@': 403}), 43: (1, {'@': 403}), 103: (1, {'@': 403}), 104: (1, {'@': 403}), 105: (1, {'@': 403}), 106: (1, {'@': 403}), 107: (1, {'@': 403}), 108: (1, {'@': 403}), 109: (1, {'@': 403}), 1: (1, {'@': 403}), 110: (1, {'@': 403}), 111: (1, {'@': 403}), 112: (1, {'@': 403}), 113: (1, {'@': 403}), 114: (1, {'@': 403}), 3: (1, {'@': 403}), 32: (1, {'@': 403}), 115: (1, {'@': 403}), 5: (1, {'@': 403}), 116: (1, {'@': 403}), 117: (1, {'@': 403}), 118: (1, {'@': 403}), 119: (1, {'@': 403}), 120: (1, {'@': 403}), 121: (1, {'@': 403}), 6: (1, {'@': 403}), 122: (1, {'@': 403}), 123: (1, {'@': 403}), 124: (1, {'@': 403})}, 252: {72: (1, {'@': 290}), 56: (1, {'@': 290}), 78: (1, {'@': 290}), 80: (1, {'@': 290}), 81: (1, {'@': 290}), 68: (1, {'@': 290}), 84: (1, {'@': 290}), 69: (1, {'@': 290}), 86: (1, {'@': 290}), 71: (1, {'@': 290}), 87: (1, {'@': 290}), 89: (1, {'@': 290}), 12: (1, {'@': 290}), 93: (1, {'@': 290}), 60: (1, {'@': 290}), 63: (1, {'@': 290}), 62: (1, {'@': 290}), 97: (1, {'@': 290}), 99: (1, {'@': 290}), 100: (1, {'@': 290}), 102: (1, {'@': 290}), 43: (1, {'@': 290}), 103: (1, {'@': 290}), 105: (1, {'@': 290}), 107: (1, {'@': 290}), 108: (1, {'@': 290}), 112: (1, {'@': 290}), 70: (1, {'@': 290}), 64: (1, {'@': 290}), 67: (1, {'@': 290}), 3: (1, {'@': 290}), 9: (1, {'@': 290}), 115: (1, {'@': 290}), 116: (1, {'@': 290}), 5: (1, {'@': 290}), 125: (1, {'@': 290}), 59: (1, {'@': 290}), 120: (1, {'@': 290}), 65: (1, {'@': 290}), 6: (1, {'@': 290}), 54: (1, {'@': 290}), 123: (1, {'@': 290}), 73: (1, {'@': 290}), 74: (1, {'@': 290}), 75: (1, {'@': 290}), 76: (1, {'@': 290}), 77: (1, {'@': 290}), 79: (1, {'@': 290}), 61: (1, {'@': 290}), 82: (1, {'@': 290}), 10: (1, {'@': 290}), 66: (1, {'@': 290}), 83: (1, {'@': 290}), 85: (1, {'@': 290}), 11: (1, {'@': 290}), 88: (1, {'@': 290}), 90: (1, {'@': 290}), 91: (1, {'@': 290}), 92: (1, {'@': 290}), 94: (1, {'@': 290}), 47: (1, {'@': 290}), 95: (1, {'@': 290}), 96: (1, {'@': 290}), 4: (1, {'@': 290}), 55: (1, {'@': 290}), 98: (1, {'@': 290}), 101: (1, {'@': 290}), 13: (1, {'@': 290}), 104: (1, {'@': 290}), 106: (1, {'@': 290}), 58: (1, {'@': 290}), 109: (1, {'@': 290}), 1: (1, {'@': 290}), 110: (1, {'@': 290}), 111: (1, {'@': 290}), 113: (1, {'@': 290}), 114: (1, {'@': 290}), 32: (1, {'@': 290}), 57: (1, {'@': 290}), 117: (1, {'@': 290}), 118: (1, {'@': 290}), 119: (1, {'@': 290}), 121: (1, {'@': 290}), 122: (1, {'@': 290}), 124: (1, {'@': 290})}, 253: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 165: (0, 586), 156: (0, 747), 93: (0, 630), 164: (0, 531), 13: (0, 599), 98: (0, 544), 11: (0, 602), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 175: (0, 619), 94: (0, 584), 82: (0, 568), 159: (0, 595), 185: (0, 350), 150: (0, 705), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 254: {4: (0, 722), 69: (0, 559), 58: (0, 739), 59: (0, 673), 62: (0, 756), 64: (0, 767), 57: (1, {'@': 388}), 56: (1, {'@': 388}), 54: (1, {'@': 388}), 63: (1, {'@': 388}), 11: (1, {'@': 388}), 12: (1, {'@': 388}), 13: (1, {'@': 388}), 72: (1, {'@': 388}), 73: (1, {'@': 388}), 74: (1, {'@': 388}), 75: (1, {'@': 388}), 76: (1, {'@': 388}), 77: (1, {'@': 388}), 78: (1, {'@': 388}), 79: (1, {'@': 388}), 80: (1, {'@': 388}), 81: (1, {'@': 388}), 10: (1, {'@': 388}), 82: (1, {'@': 388}), 83: (1, {'@': 388}), 84: (1, {'@': 388}), 85: (1, {'@': 388}), 86: (1, {'@': 388}), 87: (1, {'@': 388}), 88: (1, {'@': 388}), 89: (1, {'@': 388}), 90: (1, {'@': 388}), 91: (1, {'@': 388}), 92: (1, {'@': 388}), 93: (1, {'@': 388}), 94: (1, {'@': 388}), 95: (1, {'@': 388}), 96: (1, {'@': 388}), 97: (1, {'@': 388}), 98: (1, {'@': 388}), 99: (1, {'@': 388}), 100: (1, {'@': 388}), 101: (1, {'@': 388}), 102: (1, {'@': 388}), 43: (1, {'@': 388}), 103: (1, {'@': 388}), 104: (1, {'@': 388}), 105: (1, {'@': 388}), 106: (1, {'@': 388}), 107: (1, {'@': 388}), 108: (1, {'@': 388}), 109: (1, {'@': 388}), 1: (1, {'@': 388}), 110: (1, {'@': 388}), 111: (1, {'@': 388}), 112: (1, {'@': 388}), 113: (1, {'@': 388}), 114: (1, {'@': 388}), 3: (1, {'@': 388}), 32: (1, {'@': 388}), 115: (1, {'@': 388}), 5: (1, {'@': 388}), 116: (1, {'@': 388}), 117: (1, {'@': 388}), 118: (1, {'@': 388}), 119: (1, {'@': 388}), 120: (1, {'@': 388}), 121: (1, {'@': 388}), 6: (1, {'@': 388}), 122: (1, {'@': 388}), 123: (1, {'@': 388}), 124: (1, {'@': 388})}, 255: {71: (0, 725), 55: (1, {'@': 404}), 56: (1, {'@': 404}), 57: (1, {'@': 404}), 58: (1, {'@': 404}), 59: (1, {'@': 404}), 60: (1, {'@': 404}), 64: (1, {'@': 404}), 47: (1, {'@': 404}), 62: (1, {'@': 404}), 63: (1, {'@': 404}), 65: (1, {'@': 404}), 66: (1, {'@': 404}), 68: (1, {'@': 404}), 54: (1, {'@': 404}), 69: (1, {'@': 404}), 67: (1, {'@': 404}), 4: (1, {'@': 404}), 12: (1, {'@': 404}), 13: (1, {'@': 404}), 11: (1, {'@': 404}), 72: (1, {'@': 404}), 73: (1, {'@': 404}), 74: (1, {'@': 404}), 75: (1, {'@': 404}), 76: (1, {'@': 404}), 77: (1, {'@': 404}), 78: (1, {'@': 404}), 79: (1, {'@': 404}), 80: (1, {'@': 404}), 81: (1, {'@': 404}), 10: (1, {'@': 404}), 82: (1, {'@': 404}), 83: (1, {'@': 404}), 84: (1, {'@': 404}), 85: (1, {'@': 404}), 86: (1, {'@': 404}), 87: (1, {'@': 404}), 88: (1, {'@': 404}), 89: (1, {'@': 404}), 90: (1, {'@': 404}), 91: (1, {'@': 404}), 92: (1, {'@': 404}), 93: (1, {'@': 404}), 94: (1, {'@': 404}), 95: (1, {'@': 404}), 96: (1, {'@': 404}), 97: (1, {'@': 404}), 98: (1, {'@': 404}), 99: (1, {'@': 404}), 100: (1, {'@': 404}), 101: (1, {'@': 404}), 102: (1, {'@': 404}), 43: (1, {'@': 404}), 103: (1, {'@': 404}), 104: (1, {'@': 404}), 105: (1, {'@': 404}), 106: (1, {'@': 404}), 107: (1, {'@': 404}), 108: (1, {'@': 404}), 109: (1, {'@': 404}), 1: (1, {'@': 404}), 110: (1, {'@': 404}), 111: (1, {'@': 404}), 112: (1, {'@': 404}), 113: (1, {'@': 404}), 114: (1, {'@': 404}), 3: (1, {'@': 404}), 32: (1, {'@': 404}), 115: (1, {'@': 404}), 5: (1, {'@': 404}), 116: (1, {'@': 404}), 117: (1, {'@': 404}), 118: (1, {'@': 404}), 119: (1, {'@': 404}), 120: (1, {'@': 404}), 121: (1, {'@': 404}), 6: (1, {'@': 404}), 122: (1, {'@': 404}), 123: (1, {'@': 404}), 124: (1, {'@': 404})}, 256: {72: (1, {'@': 324}), 74: (1, {'@': 324}), 98: (1, {'@': 324}), 75: (1, {'@': 324}), 99: (1, {'@': 324}), 100: (1, {'@': 324}), 101: (1, {'@': 324}), 102: (1, {'@': 324}), 76: (1, {'@': 324}), 13: (1, {'@': 324}), 103: (1, {'@': 324}), 104: (1, {'@': 324}), 105: (1, {'@': 324}), 78: (1, {'@': 324}), 77: (1, {'@': 324}), 106: (1, {'@': 324}), 107: (1, {'@': 324}), 79: (1, {'@': 324}), 108: (1, {'@': 324}), 80: (1, {'@': 324}), 81: (1, {'@': 324}), 82: (1, {'@': 324}), 109: (1, {'@': 324}), 83: (1, {'@': 324}), 1: (1, {'@': 324}), 110: (1, {'@': 324}), 111: (1, {'@': 324}), 84: (1, {'@': 324}), 112: (1, {'@': 324}), 85: (1, {'@': 324}), 11: (1, {'@': 324}), 86: (1, {'@': 324}), 113: (1, {'@': 324}), 114: (1, {'@': 324}), 163: (1, {'@': 324}), 87: (1, {'@': 324}), 3: (1, {'@': 324}), 88: (1, {'@': 324}), 89: (1, {'@': 324}), 115: (1, {'@': 324}), 116: (1, {'@': 324}), 117: (1, {'@': 324}), 118: (1, {'@': 324}), 91: (1, {'@': 324}), 119: (1, {'@': 324}), 92: (1, {'@': 324}), 93: (1, {'@': 324}), 94: (1, {'@': 324}), 120: (1, {'@': 324}), 95: (1, {'@': 324}), 96: (1, {'@': 324}), 121: (1, {'@': 324}), 123: (1, {'@': 324}), 97: (1, {'@': 324}), 124: (1, {'@': 324}), 167: (1, {'@': 324})}, 257: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 54: (0, 222), 15: (0, 205), 32: (0, 504), 30: (0, 194), 131: (0, 186), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 258: {10: (0, 449), 54: (0, 88)}, 259: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 131: (0, 127), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 260: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 521), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 196: (0, 180), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 197: (0, 708), 25: (0, 203), 1: (0, 614), 134: (0, 433), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 10: (0, 209), 20: (0, 247), 138: (0, 287), 131: (0, 204), 54: (0, 222), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 261: {11: (1, {'@': 50}), 13: (1, {'@': 50}), 12: (1, {'@': 50})}, 262: {67: (0, 676), 62: (1, {'@': 393}), 56: (1, {'@': 393}), 57: (1, {'@': 393}), 54: (1, {'@': 393}), 69: (1, {'@': 393}), 4: (1, {'@': 393}), 58: (1, {'@': 393}), 59: (1, {'@': 393}), 64: (1, {'@': 393}), 63: (1, {'@': 393}), 11: (1, {'@': 393}), 12: (1, {'@': 393}), 13: (1, {'@': 393}), 72: (1, {'@': 393}), 73: (1, {'@': 393}), 74: (1, {'@': 393}), 75: (1, {'@': 393}), 76: (1, {'@': 393}), 77: (1, {'@': 393}), 78: (1, {'@': 393}), 79: (1, {'@': 393}), 80: (1, {'@': 393}), 81: (1, {'@': 393}), 10: (1, {'@': 393}), 82: (1, {'@': 393}), 83: (1, {'@': 393}), 84: (1, {'@': 393}), 85: (1, {'@': 393}), 86: (1, {'@': 393}), 87: (1, {'@': 393}), 88: (1, {'@': 393}), 89: (1, {'@': 393}), 90: (1, {'@': 393}), 91: (1, {'@': 393}), 92: (1, {'@': 393}), 93: (1, {'@': 393}), 94: (1, {'@': 393}), 95: (1, {'@': 393}), 96: (1, {'@': 393}), 97: (1, {'@': 393}), 98: (1, {'@': 393}), 99: (1, {'@': 393}), 100: (1, {'@': 393}), 101: (1, {'@': 393}), 102: (1, {'@': 393}), 43: (1, {'@': 393}), 103: (1, {'@': 393}), 104: (1, {'@': 393}), 105: (1, {'@': 393}), 106: (1, {'@': 393}), 107: (1, {'@': 393}), 108: (1, {'@': 393}), 109: (1, {'@': 393}), 1: (1, {'@': 393}), 110: (1, {'@': 393}), 111: (1, {'@': 393}), 112: (1, {'@': 393}), 113: (1, {'@': 393}), 114: (1, {'@': 393}), 3: (1, {'@': 393}), 32: (1, {'@': 393}), 115: (1, {'@': 393}), 5: (1, {'@': 393}), 116: (1, {'@': 393}), 117: (1, {'@': 393}), 118: (1, {'@': 393}), 119: (1, {'@': 393}), 120: (1, {'@': 393}), 121: (1, {'@': 393}), 6: (1, {'@': 393}), 122: (1, {'@': 393}), 123: (1, {'@': 393}), 124: (1, {'@': 393})}, 263: {11: (1, {'@': 101}), 13: (1, {'@': 101}), 12: (1, {'@': 101})}, 264: {5: (1, {'@': 161}), 7: (1, {'@': 161}), 10: (1, {'@': 161}), 4: (1, {'@': 161}), 9: (1, {'@': 161}), 12: (1, {'@': 161}), 13: (1, {'@': 161}), 11: (1, {'@': 161}), 170: (1, {'@': 161})}, 265: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 131: (0, 629), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 266: {3: (1, {'@': 330})}, 267: {67: (0, 676), 62: (1, {'@': 394}), 56: (1, {'@': 394}), 57: (1, {'@': 394}), 54: (1, {'@': 394}), 69: (1, {'@': 394}), 4: (1, {'@': 394}), 58: (1, {'@': 394}), 59: (1, {'@': 394}), 64: (1, {'@': 394}), 63: (1, {'@': 394}), 11: (1, {'@': 394}), 12: (1, {'@': 394}), 13: (1, {'@': 394}), 72: (1, {'@': 394}), 73: (1, {'@': 394}), 74: (1, {'@': 394}), 75: (1, {'@': 394}), 76: (1, {'@': 394}), 77: (1, {'@': 394}), 78: (1, {'@': 394}), 79: (1, {'@': 394}), 80: (1, {'@': 394}), 81: (1, {'@': 394}), 10: (1, {'@': 394}), 82: (1, {'@': 394}), 83: (1, {'@': 394}), 84: (1, {'@': 394}), 85: (1, {'@': 394}), 86: (1, {'@': 394}), 87: (1, {'@': 394}), 88: (1, {'@': 394}), 89: (1, {'@': 394}), 90: (1, {'@': 394}), 91: (1, {'@': 394}), 92: (1, {'@': 394}), 93: (1, {'@': 394}), 94: (1, {'@': 394}), 95: (1, {'@': 394}), 96: (1, {'@': 394}), 97: (1, {'@': 394}), 98: (1, {'@': 394}), 99: (1, {'@': 394}), 100: (1, {'@': 394}), 101: (1, {'@': 394}), 102: (1, {'@': 394}), 43: (1, {'@': 394}), 103: (1, {'@': 394}), 104: (1, {'@': 394}), 105: (1, {'@': 394}), 106: (1, {'@': 394}), 107: (1, {'@': 394}), 108: (1, {'@': 394}), 109: (1, {'@': 394}), 1: (1, {'@': 394}), 110: (1, {'@': 394}), 111: (1, {'@': 394}), 112: (1, {'@': 394}), 113: (1, {'@': 394}), 114: (1, {'@': 394}), 3: (1, {'@': 394}), 32: (1, {'@': 394}), 115: (1, {'@': 394}), 5: (1, {'@': 394}), 116: (1, {'@': 394}), 117: (1, {'@': 394}), 118: (1, {'@': 394}), 119: (1, {'@': 394}), 120: (1, {'@': 394}), 121: (1, {'@': 394}), 6: (1, {'@': 394}), 122: (1, {'@': 394}), 123: (1, {'@': 394}), 124: (1, {'@': 394})}, 268: {10: (0, 368)}, 269: {11: (1, {'@': 84}), 13: (1, {'@': 84}), 12: (1, {'@': 84})}, 270: {10: (0, 120)}, 271: {11: (1, {'@': 105}), 13: (1, {'@': 105}), 12: (1, {'@': 105})}, 272: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 131: (0, 48), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 273: {72: (1, {'@': 299}), 56: (1, {'@': 299}), 78: (1, {'@': 299}), 80: (1, {'@': 299}), 81: (1, {'@': 299}), 68: (1, {'@': 299}), 84: (1, {'@': 299}), 69: (1, {'@': 299}), 86: (1, {'@': 299}), 71: (1, {'@': 299}), 87: (1, {'@': 299}), 89: (1, {'@': 299}), 12: (1, {'@': 299}), 93: (1, {'@': 299}), 60: (1, {'@': 299}), 63: (1, {'@': 299}), 62: (1, {'@': 299}), 97: (1, {'@': 299}), 99: (1, {'@': 299}), 100: (1, {'@': 299}), 102: (1, {'@': 299}), 43: (1, {'@': 299}), 103: (1, {'@': 299}), 105: (1, {'@': 299}), 107: (1, {'@': 299}), 108: (1, {'@': 299}), 112: (1, {'@': 299}), 70: (1, {'@': 299}), 64: (1, {'@': 299}), 67: (1, {'@': 299}), 3: (1, {'@': 299}), 9: (1, {'@': 299}), 115: (1, {'@': 299}), 116: (1, {'@': 299}), 5: (1, {'@': 299}), 125: (1, {'@': 299}), 59: (1, {'@': 299}), 120: (1, {'@': 299}), 65: (1, {'@': 299}), 6: (1, {'@': 299}), 54: (1, {'@': 299}), 123: (1, {'@': 299}), 73: (1, {'@': 299}), 74: (1, {'@': 299}), 75: (1, {'@': 299}), 76: (1, {'@': 299}), 77: (1, {'@': 299}), 79: (1, {'@': 299}), 61: (1, {'@': 299}), 82: (1, {'@': 299}), 10: (1, {'@': 299}), 66: (1, {'@': 299}), 83: (1, {'@': 299}), 85: (1, {'@': 299}), 11: (1, {'@': 299}), 88: (1, {'@': 299}), 90: (1, {'@': 299}), 91: (1, {'@': 299}), 92: (1, {'@': 299}), 94: (1, {'@': 299}), 47: (1, {'@': 299}), 95: (1, {'@': 299}), 96: (1, {'@': 299}), 4: (1, {'@': 299}), 55: (1, {'@': 299}), 98: (1, {'@': 299}), 101: (1, {'@': 299}), 13: (1, {'@': 299}), 104: (1, {'@': 299}), 106: (1, {'@': 299}), 58: (1, {'@': 299}), 109: (1, {'@': 299}), 1: (1, {'@': 299}), 110: (1, {'@': 299}), 111: (1, {'@': 299}), 113: (1, {'@': 299}), 114: (1, {'@': 299}), 32: (1, {'@': 299}), 57: (1, {'@': 299}), 117: (1, {'@': 299}), 118: (1, {'@': 299}), 119: (1, {'@': 299}), 121: (1, {'@': 299}), 122: (1, {'@': 299}), 124: (1, {'@': 299})}, 274: {72: (1, {'@': 191}), 74: (1, {'@': 191}), 98: (1, {'@': 191}), 75: (1, {'@': 191}), 99: (1, {'@': 191}), 100: (1, {'@': 191}), 101: (1, {'@': 191}), 102: (1, {'@': 191}), 76: (1, {'@': 191}), 13: (1, {'@': 191}), 103: (1, {'@': 191}), 104: (1, {'@': 191}), 105: (1, {'@': 191}), 78: (1, {'@': 191}), 77: (1, {'@': 191}), 106: (1, {'@': 191}), 107: (1, {'@': 191}), 79: (1, {'@': 191}), 108: (1, {'@': 191}), 80: (1, {'@': 191}), 81: (1, {'@': 191}), 82: (1, {'@': 191}), 109: (1, {'@': 191}), 83: (1, {'@': 191}), 1: (1, {'@': 191}), 110: (1, {'@': 191}), 111: (1, {'@': 191}), 84: (1, {'@': 191}), 112: (1, {'@': 191}), 85: (1, {'@': 191}), 11: (1, {'@': 191}), 86: (1, {'@': 191}), 113: (1, {'@': 191}), 114: (1, {'@': 191}), 87: (1, {'@': 191}), 3: (1, {'@': 191}), 88: (1, {'@': 191}), 89: (1, {'@': 191}), 115: (1, {'@': 191}), 116: (1, {'@': 191}), 117: (1, {'@': 191}), 118: (1, {'@': 191}), 91: (1, {'@': 191}), 119: (1, {'@': 191}), 92: (1, {'@': 191}), 93: (1, {'@': 191}), 94: (1, {'@': 191}), 120: (1, {'@': 191}), 95: (1, {'@': 191}), 96: (1, {'@': 191}), 121: (1, {'@': 191}), 123: (1, {'@': 191}), 97: (1, {'@': 191}), 124: (1, {'@': 191})}, 275: {5: (0, 789)}, 276: {5: (1, {'@': 168}), 10: (1, {'@': 168})}, 277: {67: (0, 676), 62: (1, {'@': 391}), 56: (1, {'@': 391}), 57: (1, {'@': 391}), 54: (1, {'@': 391}), 69: (1, {'@': 391}), 4: (1, {'@': 391}), 58: (1, {'@': 391}), 59: (1, {'@': 391}), 64: (1, {'@': 391}), 63: (1, {'@': 391}), 11: (1, {'@': 391}), 12: (1, {'@': 391}), 13: (1, {'@': 391}), 72: (1, {'@': 391}), 73: (1, {'@': 391}), 74: (1, {'@': 391}), 75: (1, {'@': 391}), 76: (1, {'@': 391}), 77: (1, {'@': 391}), 78: (1, {'@': 391}), 79: (1, {'@': 391}), 80: (1, {'@': 391}), 81: (1, {'@': 391}), 10: (1, {'@': 391}), 82: (1, {'@': 391}), 83: (1, {'@': 391}), 84: (1, {'@': 391}), 85: (1, {'@': 391}), 86: (1, {'@': 391}), 87: (1, {'@': 391}), 88: (1, {'@': 391}), 89: (1, {'@': 391}), 90: (1, {'@': 391}), 91: (1, {'@': 391}), 92: (1, {'@': 391}), 93: (1, {'@': 391}), 94: (1, {'@': 391}), 95: (1, {'@': 391}), 96: (1, {'@': 391}), 97: (1, {'@': 391}), 98: (1, {'@': 391}), 99: (1, {'@': 391}), 100: (1, {'@': 391}), 101: (1, {'@': 391}), 102: (1, {'@': 391}), 43: (1, {'@': 391}), 103: (1, {'@': 391}), 104: (1, {'@': 391}), 105: (1, {'@': 391}), 106: (1, {'@': 391}), 107: (1, {'@': 391}), 108: (1, {'@': 391}), 109: (1, {'@': 391}), 1: (1, {'@': 391}), 110: (1, {'@': 391}), 111: (1, {'@': 391}), 112: (1, {'@': 391}), 113: (1, {'@': 391}), 114: (1, {'@': 391}), 3: (1, {'@': 391}), 32: (1, {'@': 391}), 115: (1, {'@': 391}), 5: (1, {'@': 391}), 116: (1, {'@': 391}), 117: (1, {'@': 391}), 118: (1, {'@': 391}), 119: (1, {'@': 391}), 120: (1, {'@': 391}), 121: (1, {'@': 391}), 6: (1, {'@': 391}), 122: (1, {'@': 391}), 123: (1, {'@': 391}), 124: (1, {'@': 391})}, 278: {31: (0, 3), 131: (0, 635), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 279: {11: (1, {'@': 323}), 13: (1, {'@': 323}), 12: (1, {'@': 323})}, 280: {5: (0, 496)}, 281: {90: (0, 478), 203: (0, 191), 72: (1, {'@': 220}), 74: (1, {'@': 220}), 75: (1, {'@': 220}), 76: (1, {'@': 220}), 77: (1, {'@': 220}), 78: (1, {'@': 220}), 79: (1, {'@': 220}), 80: (1, {'@': 220}), 81: (1, {'@': 220}), 82: (1, {'@': 220}), 83: (1, {'@': 220}), 84: (1, {'@': 220}), 85: (1, {'@': 220}), 11: (1, {'@': 220}), 86: (1, {'@': 220}), 87: (1, {'@': 220}), 88: (1, {'@': 220}), 89: (1, {'@': 220}), 91: (1, {'@': 220}), 92: (1, {'@': 220}), 93: (1, {'@': 220}), 94: (1, {'@': 220}), 95: (1, {'@': 220}), 96: (1, {'@': 220}), 97: (1, {'@': 220}), 98: (1, {'@': 220}), 99: (1, {'@': 220}), 100: (1, {'@': 220}), 101: (1, {'@': 220}), 102: (1, {'@': 220}), 13: (1, {'@': 220}), 103: (1, {'@': 220}), 104: (1, {'@': 220}), 105: (1, {'@': 220}), 106: (1, {'@': 220}), 107: (1, {'@': 220}), 108: (1, {'@': 220}), 109: (1, {'@': 220}), 1: (1, {'@': 220}), 110: (1, {'@': 220}), 111: (1, {'@': 220}), 112: (1, {'@': 220}), 113: (1, {'@': 220}), 114: (1, {'@': 220}), 3: (1, {'@': 220}), 115: (1, {'@': 220}), 116: (1, {'@': 220}), 117: (1, {'@': 220}), 118: (1, {'@': 220}), 119: (1, {'@': 220}), 120: (1, {'@': 220}), 121: (1, {'@': 220}), 123: (1, {'@': 220}), 124: (1, {'@': 220})}, 282: {5: (1, {'@': 250}), 11: (1, {'@': 250}), 12: (1, {'@': 250}), 43: (1, {'@': 250}), 13: (1, {'@': 250})}, 283: {10: (0, 28)}, 284: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 131: (0, 444), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 285: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 131: (0, 135), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 286: {13: (1, {'@': 366})}, 287: {67: (0, 676), 62: (1, {'@': 395}), 56: (1, {'@': 395}), 57: (1, {'@': 395}), 54: (1, {'@': 395}), 69: (1, {'@': 395}), 4: (1, {'@': 395}), 58: (1, {'@': 395}), 59: (1, {'@': 395}), 64: (1, {'@': 395}), 63: (1, {'@': 395}), 11: (1, {'@': 395}), 12: (1, {'@': 395}), 13: (1, {'@': 395}), 72: (1, {'@': 395}), 73: (1, {'@': 395}), 74: (1, {'@': 395}), 75: (1, {'@': 395}), 76: (1, {'@': 395}), 77: (1, {'@': 395}), 78: (1, {'@': 395}), 79: (1, {'@': 395}), 80: (1, {'@': 395}), 81: (1, {'@': 395}), 10: (1, {'@': 395}), 82: (1, {'@': 395}), 83: (1, {'@': 395}), 84: (1, {'@': 395}), 85: (1, {'@': 395}), 86: (1, {'@': 395}), 87: (1, {'@': 395}), 88: (1, {'@': 395}), 89: (1, {'@': 395}), 90: (1, {'@': 395}), 91: (1, {'@': 395}), 92: (1, {'@': 395}), 93: (1, {'@': 395}), 94: (1, {'@': 395}), 95: (1, {'@': 395}), 96: (1, {'@': 395}), 97: (1, {'@': 395}), 98: (1, {'@': 395}), 99: (1, {'@': 395}), 100: (1, {'@': 395}), 101: (1, {'@': 395}), 102: (1, {'@': 395}), 43: (1, {'@': 395}), 103: (1, {'@': 395}), 104: (1, {'@': 395}), 105: (1, {'@': 395}), 106: (1, {'@': 395}), 107: (1, {'@': 395}), 108: (1, {'@': 395}), 109: (1, {'@': 395}), 1: (1, {'@': 395}), 110: (1, {'@': 395}), 111: (1, {'@': 395}), 112: (1, {'@': 395}), 113: (1, {'@': 395}), 114: (1, {'@': 395}), 3: (1, {'@': 395}), 32: (1, {'@': 395}), 115: (1, {'@': 395}), 5: (1, {'@': 395}), 116: (1, {'@': 395}), 117: (1, {'@': 395}), 118: (1, {'@': 395}), 119: (1, {'@': 395}), 120: (1, {'@': 395}), 121: (1, {'@': 395}), 6: (1, {'@': 395}), 122: (1, {'@': 395}), 123: (1, {'@': 395}), 124: (1, {'@': 395})}, 288: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 521), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 196: (0, 49), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 197: (0, 708), 1: (0, 614), 25: (0, 203), 134: (0, 433), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 10: (0, 209), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 66), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 289: {67: (0, 676), 62: (1, {'@': 389}), 56: (1, {'@': 389}), 57: (1, {'@': 389}), 54: (1, {'@': 389}), 69: (1, {'@': 389}), 4: (1, {'@': 389}), 58: (1, {'@': 389}), 59: (1, {'@': 389}), 64: (1, {'@': 389}), 63: (1, {'@': 389}), 11: (1, {'@': 389}), 12: (1, {'@': 389}), 13: (1, {'@': 389}), 72: (1, {'@': 389}), 73: (1, {'@': 389}), 74: (1, {'@': 389}), 75: (1, {'@': 389}), 76: (1, {'@': 389}), 77: (1, {'@': 389}), 78: (1, {'@': 389}), 79: (1, {'@': 389}), 80: (1, {'@': 389}), 81: (1, {'@': 389}), 10: (1, {'@': 389}), 82: (1, {'@': 389}), 83: (1, {'@': 389}), 84: (1, {'@': 389}), 85: (1, {'@': 389}), 86: (1, {'@': 389}), 87: (1, {'@': 389}), 88: (1, {'@': 389}), 89: (1, {'@': 389}), 90: (1, {'@': 389}), 91: (1, {'@': 389}), 92: (1, {'@': 389}), 93: (1, {'@': 389}), 94: (1, {'@': 389}), 95: (1, {'@': 389}), 96: (1, {'@': 389}), 97: (1, {'@': 389}), 98: (1, {'@': 389}), 99: (1, {'@': 389}), 100: (1, {'@': 389}), 101: (1, {'@': 389}), 102: (1, {'@': 389}), 43: (1, {'@': 389}), 103: (1, {'@': 389}), 104: (1, {'@': 389}), 105: (1, {'@': 389}), 106: (1, {'@': 389}), 107: (1, {'@': 389}), 108: (1, {'@': 389}), 109: (1, {'@': 389}), 1: (1, {'@': 389}), 110: (1, {'@': 389}), 111: (1, {'@': 389}), 112: (1, {'@': 389}), 113: (1, {'@': 389}), 114: (1, {'@': 389}), 3: (1, {'@': 389}), 32: (1, {'@': 389}), 115: (1, {'@': 389}), 5: (1, {'@': 389}), 116: (1, {'@': 389}), 117: (1, {'@': 389}), 118: (1, {'@': 389}), 119: (1, {'@': 389}), 120: (1, {'@': 389}), 121: (1, {'@': 389}), 6: (1, {'@': 389}), 122: (1, {'@': 389}), 123: (1, {'@': 389}), 124: (1, {'@': 389})}, 290: {67: (0, 676), 62: (1, {'@': 392}), 56: (1, {'@': 392}), 57: (1, {'@': 392}), 54: (1, {'@': 392}), 69: (1, {'@': 392}), 4: (1, {'@': 392}), 58: (1, {'@': 392}), 59: (1, {'@': 392}), 64: (1, {'@': 392}), 63: (1, {'@': 392}), 11: (1, {'@': 392}), 12: (1, {'@': 392}), 13: (1, {'@': 392}), 72: (1, {'@': 392}), 73: (1, {'@': 392}), 74: (1, {'@': 392}), 75: (1, {'@': 392}), 76: (1, {'@': 392}), 77: (1, {'@': 392}), 78: (1, {'@': 392}), 79: (1, {'@': 392}), 80: (1, {'@': 392}), 81: (1, {'@': 392}), 10: (1, {'@': 392}), 82: (1, {'@': 392}), 83: (1, {'@': 392}), 84: (1, {'@': 392}), 85: (1, {'@': 392}), 86: (1, {'@': 392}), 87: (1, {'@': 392}), 88: (1, {'@': 392}), 89: (1, {'@': 392}), 90: (1, {'@': 392}), 91: (1, {'@': 392}), 92: (1, {'@': 392}), 93: (1, {'@': 392}), 94: (1, {'@': 392}), 95: (1, {'@': 392}), 96: (1, {'@': 392}), 97: (1, {'@': 392}), 98: (1, {'@': 392}), 99: (1, {'@': 392}), 100: (1, {'@': 392}), 101: (1, {'@': 392}), 102: (1, {'@': 392}), 43: (1, {'@': 392}), 103: (1, {'@': 392}), 104: (1, {'@': 392}), 105: (1, {'@': 392}), 106: (1, {'@': 392}), 107: (1, {'@': 392}), 108: (1, {'@': 392}), 109: (1, {'@': 392}), 1: (1, {'@': 392}), 110: (1, {'@': 392}), 111: (1, {'@': 392}), 112: (1, {'@': 392}), 113: (1, {'@': 392}), 114: (1, {'@': 392}), 3: (1, {'@': 392}), 32: (1, {'@': 392}), 115: (1, {'@': 392}), 5: (1, {'@': 392}), 116: (1, {'@': 392}), 117: (1, {'@': 392}), 118: (1, {'@': 392}), 119: (1, {'@': 392}), 120: (1, {'@': 392}), 121: (1, {'@': 392}), 6: (1, {'@': 392}), 122: (1, {'@': 392}), 123: (1, {'@': 392}), 124: (1, {'@': 392})}, 291: {13: (1, {'@': 367})}, 292: {55: (1, {'@': 416}), 56: (1, {'@': 416}), 57: (1, {'@': 416}), 125: (1, {'@': 416}), 58: (1, {'@': 416}), 59: (1, {'@': 416}), 60: (1, {'@': 416}), 61: (1, {'@': 416}), 47: (1, {'@': 416}), 62: (1, {'@': 416}), 63: (1, {'@': 416}), 65: (1, {'@': 416}), 66: (1, {'@': 416}), 70: (1, {'@': 416}), 68: (1, {'@': 416}), 54: (1, {'@': 416}), 4: (1, {'@': 416}), 69: (1, {'@': 416}), 67: (1, {'@': 416}), 64: (1, {'@': 416}), 71: (1, {'@': 416}), 12: (1, {'@': 416}), 13: (1, {'@': 416}), 11: (1, {'@': 416}), 72: (1, {'@': 416}), 73: (1, {'@': 416}), 74: (1, {'@': 416}), 75: (1, {'@': 416}), 76: (1, {'@': 416}), 77: (1, {'@': 416}), 78: (1, {'@': 416}), 79: (1, {'@': 416}), 80: (1, {'@': 416}), 81: (1, {'@': 416}), 10: (1, {'@': 416}), 82: (1, {'@': 416}), 83: (1, {'@': 416}), 84: (1, {'@': 416}), 85: (1, {'@': 416}), 86: (1, {'@': 416}), 87: (1, {'@': 416}), 88: (1, {'@': 416}), 89: (1, {'@': 416}), 90: (1, {'@': 416}), 91: (1, {'@': 416}), 92: (1, {'@': 416}), 93: (1, {'@': 416}), 94: (1, {'@': 416}), 95: (1, {'@': 416}), 96: (1, {'@': 416}), 97: (1, {'@': 416}), 98: (1, {'@': 416}), 99: (1, {'@': 416}), 100: (1, {'@': 416}), 101: (1, {'@': 416}), 102: (1, {'@': 416}), 43: (1, {'@': 416}), 103: (1, {'@': 416}), 104: (1, {'@': 416}), 105: (1, {'@': 416}), 106: (1, {'@': 416}), 107: (1, {'@': 416}), 108: (1, {'@': 416}), 109: (1, {'@': 416}), 1: (1, {'@': 416}), 110: (1, {'@': 416}), 111: (1, {'@': 416}), 112: (1, {'@': 416}), 113: (1, {'@': 416}), 114: (1, {'@': 416}), 3: (1, {'@': 416}), 32: (1, {'@': 416}), 115: (1, {'@': 416}), 5: (1, {'@': 416}), 116: (1, {'@': 416}), 117: (1, {'@': 416}), 118: (1, {'@': 416}), 119: (1, {'@': 416}), 120: (1, {'@': 416}), 121: (1, {'@': 416}), 6: (1, {'@': 416}), 122: (1, {'@': 416}), 123: (1, {'@': 416}), 124: (1, {'@': 416})}, 293: {67: (0, 676), 62: (1, {'@': 390}), 56: (1, {'@': 390}), 57: (1, {'@': 390}), 54: (1, {'@': 390}), 69: (1, {'@': 390}), 4: (1, {'@': 390}), 58: (1, {'@': 390}), 59: (1, {'@': 390}), 64: (1, {'@': 390}), 63: (1, {'@': 390}), 11: (1, {'@': 390}), 12: (1, {'@': 390}), 13: (1, {'@': 390}), 72: (1, {'@': 390}), 73: (1, {'@': 390}), 74: (1, {'@': 390}), 75: (1, {'@': 390}), 76: (1, {'@': 390}), 77: (1, {'@': 390}), 78: (1, {'@': 390}), 79: (1, {'@': 390}), 80: (1, {'@': 390}), 81: (1, {'@': 390}), 10: (1, {'@': 390}), 82: (1, {'@': 390}), 83: (1, {'@': 390}), 84: (1, {'@': 390}), 85: (1, {'@': 390}), 86: (1, {'@': 390}), 87: (1, {'@': 390}), 88: (1, {'@': 390}), 89: (1, {'@': 390}), 90: (1, {'@': 390}), 91: (1, {'@': 390}), 92: (1, {'@': 390}), 93: (1, {'@': 390}), 94: (1, {'@': 390}), 95: (1, {'@': 390}), 96: (1, {'@': 390}), 97: (1, {'@': 390}), 98: (1, {'@': 390}), 99: (1, {'@': 390}), 100: (1, {'@': 390}), 101: (1, {'@': 390}), 102: (1, {'@': 390}), 43: (1, {'@': 390}), 103: (1, {'@': 390}), 104: (1, {'@': 390}), 105: (1, {'@': 390}), 106: (1, {'@': 390}), 107: (1, {'@': 390}), 108: (1, {'@': 390}), 109: (1, {'@': 390}), 1: (1, {'@': 390}), 110: (1, {'@': 390}), 111: (1, {'@': 390}), 112: (1, {'@': 390}), 113: (1, {'@': 390}), 114: (1, {'@': 390}), 3: (1, {'@': 390}), 32: (1, {'@': 390}), 115: (1, {'@': 390}), 5: (1, {'@': 390}), 116: (1, {'@': 390}), 117: (1, {'@': 390}), 118: (1, {'@': 390}), 119: (1, {'@': 390}), 120: (1, {'@': 390}), 121: (1, {'@': 390}), 6: (1, {'@': 390}), 122: (1, {'@': 390}), 123: (1, {'@': 390}), 124: (1, {'@': 390})}, 294: {11: (1, {'@': 159}), 13: (1, {'@': 159}), 12: (1, {'@': 159})}, 295: {56: (0, 770), 54: (1, {'@': 381}), 63: (1, {'@': 381}), 11: (1, {'@': 381}), 12: (1, {'@': 381}), 13: (1, {'@': 381}), 72: (1, {'@': 381}), 74: (1, {'@': 381}), 98: (1, {'@': 381}), 75: (1, {'@': 381}), 99: (1, {'@': 381}), 100: (1, {'@': 381}), 76: (1, {'@': 381}), 101: (1, {'@': 381}), 102: (1, {'@': 381}), 103: (1, {'@': 381}), 77: (1, {'@': 381}), 78: (1, {'@': 381}), 104: (1, {'@': 381}), 105: (1, {'@': 381}), 106: (1, {'@': 381}), 107: (1, {'@': 381}), 79: (1, {'@': 381}), 108: (1, {'@': 381}), 80: (1, {'@': 381}), 81: (1, {'@': 381}), 82: (1, {'@': 381}), 109: (1, {'@': 381}), 83: (1, {'@': 381}), 1: (1, {'@': 381}), 110: (1, {'@': 381}), 111: (1, {'@': 381}), 84: (1, {'@': 381}), 112: (1, {'@': 381}), 85: (1, {'@': 381}), 86: (1, {'@': 381}), 113: (1, {'@': 381}), 114: (1, {'@': 381}), 87: (1, {'@': 381}), 3: (1, {'@': 381}), 88: (1, {'@': 381}), 89: (1, {'@': 381}), 115: (1, {'@': 381}), 90: (1, {'@': 381}), 116: (1, {'@': 381}), 117: (1, {'@': 381}), 118: (1, {'@': 381}), 91: (1, {'@': 381}), 119: (1, {'@': 381}), 92: (1, {'@': 381}), 93: (1, {'@': 381}), 94: (1, {'@': 381}), 120: (1, {'@': 381}), 95: (1, {'@': 381}), 96: (1, {'@': 381}), 121: (1, {'@': 381}), 123: (1, {'@': 381}), 97: (1, {'@': 381}), 124: (1, {'@': 381}), 5: (1, {'@': 381}), 10: (1, {'@': 381}), 43: (1, {'@': 381}), 73: (1, {'@': 381}), 6: (1, {'@': 381}), 122: (1, {'@': 381}), 32: (1, {'@': 381})}, 296: {11: (1, {'@': 23}), 13: (1, {'@': 23}), 12: (1, {'@': 23})}, 297: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 131: (0, 763), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 298: {10: (0, 623)}, 299: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 477), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 300: {72: (1, {'@': 10}), 168: (1, {'@': 10}), 98: (1, {'@': 10}), 99: (1, {'@': 10}), 100: (1, {'@': 10}), 101: (1, {'@': 10}), 102: (1, {'@': 10}), 76: (1, {'@': 10}), 13: (1, {'@': 10}), 103: (1, {'@': 10}), 105: (1, {'@': 10}), 104: (1, {'@': 10}), 78: (1, {'@': 10}), 77: (1, {'@': 10}), 106: (1, {'@': 10}), 107: (1, {'@': 10}), 79: (1, {'@': 10}), 108: (1, {'@': 10}), 80: (1, {'@': 10}), 81: (1, {'@': 10}), 82: (1, {'@': 10}), 109: (1, {'@': 10}), 83: (1, {'@': 10}), 1: (1, {'@': 10}), 110: (1, {'@': 10}), 111: (1, {'@': 10}), 84: (1, {'@': 10}), 112: (1, {'@': 10}), 85: (1, {'@': 10}), 11: (1, {'@': 10}), 86: (1, {'@': 10}), 114: (1, {'@': 10}), 87: (1, {'@': 10}), 3: (1, {'@': 10}), 88: (1, {'@': 10}), 89: (1, {'@': 10}), 115: (1, {'@': 10}), 116: (1, {'@': 10}), 117: (1, {'@': 10}), 118: (1, {'@': 10}), 91: (1, {'@': 10}), 119: (1, {'@': 10}), 92: (1, {'@': 10}), 93: (1, {'@': 10}), 94: (1, {'@': 10}), 120: (1, {'@': 10}), 95: (1, {'@': 10}), 96: (1, {'@': 10}), 121: (1, {'@': 10}), 123: (1, {'@': 10}), 97: (1, {'@': 10}), 124: (1, {'@': 10}), 166: (1, {'@': 10}), 44: (1, {'@': 10}), 163: (1, {'@': 10}), 45: (1, {'@': 10}), 12: (1, {'@': 10}), 164: (1, {'@': 10}), 122: (1, {'@': 10}), 165: (1, {'@': 10}), 167: (1, {'@': 10})}, 301: {11: (1, {'@': 81}), 13: (1, {'@': 81}), 12: (1, {'@': 81})}, 302: {13: (1, {'@': 368})}, 303: {5: (1, {'@': 167}), 10: (1, {'@': 167})}, 304: {72: (1, {'@': 285}), 56: (1, {'@': 285}), 78: (1, {'@': 285}), 80: (1, {'@': 285}), 81: (1, {'@': 285}), 68: (1, {'@': 285}), 84: (1, {'@': 285}), 69: (1, {'@': 285}), 86: (1, {'@': 285}), 71: (1, {'@': 285}), 87: (1, {'@': 285}), 89: (1, {'@': 285}), 12: (1, {'@': 285}), 93: (1, {'@': 285}), 60: (1, {'@': 285}), 63: (1, {'@': 285}), 62: (1, {'@': 285}), 97: (1, {'@': 285}), 99: (1, {'@': 285}), 100: (1, {'@': 285}), 102: (1, {'@': 285}), 43: (1, {'@': 285}), 103: (1, {'@': 285}), 105: (1, {'@': 285}), 107: (1, {'@': 285}), 108: (1, {'@': 285}), 112: (1, {'@': 285}), 70: (1, {'@': 285}), 64: (1, {'@': 285}), 67: (1, {'@': 285}), 3: (1, {'@': 285}), 9: (1, {'@': 285}), 115: (1, {'@': 285}), 116: (1, {'@': 285}), 5: (1, {'@': 285}), 125: (1, {'@': 285}), 59: (1, {'@': 285}), 120: (1, {'@': 285}), 65: (1, {'@': 285}), 6: (1, {'@': 285}), 54: (1, {'@': 285}), 123: (1, {'@': 285}), 73: (1, {'@': 285}), 74: (1, {'@': 285}), 75: (1, {'@': 285}), 76: (1, {'@': 285}), 77: (1, {'@': 285}), 79: (1, {'@': 285}), 61: (1, {'@': 285}), 82: (1, {'@': 285}), 10: (1, {'@': 285}), 66: (1, {'@': 285}), 83: (1, {'@': 285}), 85: (1, {'@': 285}), 11: (1, {'@': 285}), 88: (1, {'@': 285}), 90: (1, {'@': 285}), 91: (1, {'@': 285}), 92: (1, {'@': 285}), 94: (1, {'@': 285}), 47: (1, {'@': 285}), 95: (1, {'@': 285}), 96: (1, {'@': 285}), 4: (1, {'@': 285}), 55: (1, {'@': 285}), 98: (1, {'@': 285}), 101: (1, {'@': 285}), 13: (1, {'@': 285}), 104: (1, {'@': 285}), 106: (1, {'@': 285}), 58: (1, {'@': 285}), 109: (1, {'@': 285}), 1: (1, {'@': 285}), 110: (1, {'@': 285}), 111: (1, {'@': 285}), 113: (1, {'@': 285}), 114: (1, {'@': 285}), 32: (1, {'@': 285}), 57: (1, {'@': 285}), 117: (1, {'@': 285}), 118: (1, {'@': 285}), 119: (1, {'@': 285}), 121: (1, {'@': 285}), 122: (1, {'@': 285}), 124: (1, {'@': 285})}, 305: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 23: (0, 219), 53: (0, 723), 29: (0, 474), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 24: (0, 408), 40: (0, 503), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 306: {9: (0, 704), 4: (0, 631), 171: (0, 501)}, 307: {5: (1, {'@': 354}), 10: (1, {'@': 354}), 4: (1, {'@': 354}), 11: (1, {'@': 354}), 12: (1, {'@': 354}), 13: (1, {'@': 354}), 169: (1, {'@': 354}), 170: (1, {'@': 354})}, 308: {10: (0, 509), 5: (0, 54)}, 309: {11: (1, {'@': 73}), 13: (1, {'@': 73}), 12: (1, {'@': 73})}, 310: {54: (0, 230), 5: (1, {'@': 169}), 10: (1, {'@': 169})}, 311: {11: (1, {'@': 27}), 13: (1, {'@': 27}), 12: (1, {'@': 27})}, 312: {11: (1, {'@': 350}), 12: (1, {'@': 350}), 13: (1, {'@': 350})}, 313: {14: (1, {'@': 265}), 15: (1, {'@': 265}), 16: (1, {'@': 265}), 17: (1, {'@': 265}), 18: (1, {'@': 265}), 19: (1, {'@': 265}), 20: (1, {'@': 265}), 21: (1, {'@': 265}), 22: (1, {'@': 265}), 23: (1, {'@': 265}), 24: (1, {'@': 265}), 25: (1, {'@': 265}), 1: (1, {'@': 265}), 26: (1, {'@': 265}), 27: (1, {'@': 265}), 28: (1, {'@': 265}), 46: (1, {'@': 265}), 29: (1, {'@': 265}), 30: (1, {'@': 265}), 3: (1, {'@': 265}), 31: (1, {'@': 265}), 9: (1, {'@': 265}), 32: (1, {'@': 265}), 34: (1, {'@': 265}), 33: (1, {'@': 265}), 5: (1, {'@': 265}), 35: (1, {'@': 265}), 36: (1, {'@': 265}), 37: (1, {'@': 265}), 47: (1, {'@': 265}), 38: (1, {'@': 265}), 48: (1, {'@': 265}), 39: (1, {'@': 265}), 49: (1, {'@': 265}), 40: (1, {'@': 265}), 41: (1, {'@': 265}), 42: (1, {'@': 265})}, 314: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 131: (0, 730), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 315: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 144: (0, 303), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 204: (0, 308), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 131: (0, 310), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 316: {7: (1, {'@': 334}), 11: (1, {'@': 334}), 12: (1, {'@': 334}), 13: (1, {'@': 334})}, 317: {10: (0, 347), 5: (0, 415)}, 318: {56: (0, 770), 54: (1, {'@': 382}), 63: (1, {'@': 382}), 11: (1, {'@': 382}), 12: (1, {'@': 382}), 13: (1, {'@': 382}), 72: (1, {'@': 382}), 74: (1, {'@': 382}), 98: (1, {'@': 382}), 75: (1, {'@': 382}), 99: (1, {'@': 382}), 100: (1, {'@': 382}), 76: (1, {'@': 382}), 101: (1, {'@': 382}), 102: (1, {'@': 382}), 103: (1, {'@': 382}), 77: (1, {'@': 382}), 78: (1, {'@': 382}), 104: (1, {'@': 382}), 105: (1, {'@': 382}), 106: (1, {'@': 382}), 107: (1, {'@': 382}), 79: (1, {'@': 382}), 108: (1, {'@': 382}), 80: (1, {'@': 382}), 81: (1, {'@': 382}), 82: (1, {'@': 382}), 109: (1, {'@': 382}), 83: (1, {'@': 382}), 1: (1, {'@': 382}), 110: (1, {'@': 382}), 111: (1, {'@': 382}), 84: (1, {'@': 382}), 112: (1, {'@': 382}), 85: (1, {'@': 382}), 86: (1, {'@': 382}), 113: (1, {'@': 382}), 114: (1, {'@': 382}), 87: (1, {'@': 382}), 3: (1, {'@': 382}), 88: (1, {'@': 382}), 89: (1, {'@': 382}), 115: (1, {'@': 382}), 90: (1, {'@': 382}), 116: (1, {'@': 382}), 117: (1, {'@': 382}), 118: (1, {'@': 382}), 91: (1, {'@': 382}), 119: (1, {'@': 382}), 92: (1, {'@': 382}), 93: (1, {'@': 382}), 94: (1, {'@': 382}), 120: (1, {'@': 382}), 95: (1, {'@': 382}), 96: (1, {'@': 382}), 121: (1, {'@': 382}), 123: (1, {'@': 382}), 97: (1, {'@': 382}), 124: (1, {'@': 382}), 5: (1, {'@': 382}), 10: (1, {'@': 382}), 43: (1, {'@': 382}), 73: (1, {'@': 382}), 6: (1, {'@': 382}), 122: (1, {'@': 382}), 32: (1, {'@': 382})}, 319: {5: (0, 343), 7: (0, 377), 8: (0, 539), 4: (1, {'@': 352})}, 320: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 5: (0, 272), 41: (0, 273), 21: (0, 305), 131: (0, 275), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 321: {10: (0, 23)}, 322: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 131: (0, 147), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 323: {4: (0, 299)}, 324: {9: (0, 566), 171: (0, 642)}, 325: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 156: (0, 747), 93: (0, 630), 13: (0, 599), 98: (0, 544), 11: (0, 602), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 167: (0, 648), 82: (0, 568), 94: (0, 584), 159: (0, 595), 150: (0, 705), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 163: (0, 662), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 326: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 131: (0, 467), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 327: {54: (0, 38)}, 328: {9: (0, 332), 4: (0, 430), 172: (0, 375), 55: (1, {'@': 267}), 56: (1, {'@': 267}), 12: (1, {'@': 267}), 13: (1, {'@': 267}), 57: (1, {'@': 267}), 5: (1, {'@': 267}), 125: (1, {'@': 267}), 58: (1, {'@': 267}), 59: (1, {'@': 267}), 60: (1, {'@': 267}), 61: (1, {'@': 267}), 47: (1, {'@': 267}), 63: (1, {'@': 267}), 62: (1, {'@': 267}), 64: (1, {'@': 267}), 65: (1, {'@': 267}), 66: (1, {'@': 267}), 70: (1, {'@': 267}), 68: (1, {'@': 267}), 69: (1, {'@': 267}), 67: (1, {'@': 267}), 11: (1, {'@': 267}), 71: (1, {'@': 267})}, 329: {10: (0, 149)}, 330: {205: (0, 410), 206: (0, 157)}, 331: {10: (0, 347), 5: (1, {'@': 319})}, 332: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 54: (0, 222), 15: (0, 205), 32: (0, 504), 30: (0, 194), 131: (0, 159), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 333: {72: (1, {'@': 192}), 74: (1, {'@': 192}), 98: (1, {'@': 192}), 75: (1, {'@': 192}), 99: (1, {'@': 192}), 100: (1, {'@': 192}), 101: (1, {'@': 192}), 102: (1, {'@': 192}), 76: (1, {'@': 192}), 13: (1, {'@': 192}), 103: (1, {'@': 192}), 104: (1, {'@': 192}), 105: (1, {'@': 192}), 78: (1, {'@': 192}), 77: (1, {'@': 192}), 106: (1, {'@': 192}), 107: (1, {'@': 192}), 79: (1, {'@': 192}), 108: (1, {'@': 192}), 80: (1, {'@': 192}), 81: (1, {'@': 192}), 82: (1, {'@': 192}), 109: (1, {'@': 192}), 83: (1, {'@': 192}), 1: (1, {'@': 192}), 110: (1, {'@': 192}), 111: (1, {'@': 192}), 84: (1, {'@': 192}), 112: (1, {'@': 192}), 85: (1, {'@': 192}), 11: (1, {'@': 192}), 86: (1, {'@': 192}), 113: (1, {'@': 192}), 114: (1, {'@': 192}), 87: (1, {'@': 192}), 3: (1, {'@': 192}), 88: (1, {'@': 192}), 89: (1, {'@': 192}), 115: (1, {'@': 192}), 116: (1, {'@': 192}), 117: (1, {'@': 192}), 118: (1, {'@': 192}), 91: (1, {'@': 192}), 119: (1, {'@': 192}), 92: (1, {'@': 192}), 93: (1, {'@': 192}), 94: (1, {'@': 192}), 120: (1, {'@': 192}), 95: (1, {'@': 192}), 96: (1, {'@': 192}), 121: (1, {'@': 192}), 123: (1, {'@': 192}), 97: (1, {'@': 192}), 124: (1, {'@': 192})}, 334: {14: (1, {'@': 264}), 15: (1, {'@': 264}), 16: (1, {'@': 264}), 17: (1, {'@': 264}), 18: (1, {'@': 264}), 19: (1, {'@': 264}), 20: (1, {'@': 264}), 21: (1, {'@': 264}), 22: (1, {'@': 264}), 23: (1, {'@': 264}), 24: (1, {'@': 264}), 25: (1, {'@': 264}), 1: (1, {'@': 264}), 26: (1, {'@': 264}), 27: (1, {'@': 264}), 28: (1, {'@': 264}), 46: (1, {'@': 264}), 29: (1, {'@': 264}), 30: (1, {'@': 264}), 3: (1, {'@': 264}), 31: (1, {'@': 264}), 9: (1, {'@': 264}), 32: (1, {'@': 264}), 34: (1, {'@': 264}), 33: (1, {'@': 264}), 5: (1, {'@': 264}), 35: (1, {'@': 264}), 36: (1, {'@': 264}), 37: (1, {'@': 264}), 47: (1, {'@': 264}), 38: (1, {'@': 264}), 48: (1, {'@': 264}), 39: (1, {'@': 264}), 49: (1, {'@': 264}), 40: (1, {'@': 264}), 41: (1, {'@': 264}), 42: (1, {'@': 264})}, 335: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 131: (0, 469), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 336: {11: (1, {'@': 209}), 12: (1, {'@': 209}), 13: (1, {'@': 209})}, 337: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 150: (0, 81), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 165: (0, 586), 156: (0, 747), 11: (0, 73), 93: (0, 630), 164: (0, 531), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 175: (0, 619), 94: (0, 584), 82: (0, 568), 159: (0, 595), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 185: (0, 506), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 13: (1, {'@': 146})}, 338: {10: (0, 239)}, 339: {122: (0, 105)}, 340: {72: (1, {'@': 289}), 56: (1, {'@': 289}), 78: (1, {'@': 289}), 80: (1, {'@': 289}), 81: (1, {'@': 289}), 68: (1, {'@': 289}), 84: (1, {'@': 289}), 69: (1, {'@': 289}), 86: (1, {'@': 289}), 71: (1, {'@': 289}), 87: (1, {'@': 289}), 89: (1, {'@': 289}), 12: (1, {'@': 289}), 93: (1, {'@': 289}), 60: (1, {'@': 289}), 63: (1, {'@': 289}), 62: (1, {'@': 289}), 97: (1, {'@': 289}), 99: (1, {'@': 289}), 100: (1, {'@': 289}), 102: (1, {'@': 289}), 43: (1, {'@': 289}), 103: (1, {'@': 289}), 105: (1, {'@': 289}), 107: (1, {'@': 289}), 108: (1, {'@': 289}), 112: (1, {'@': 289}), 70: (1, {'@': 289}), 64: (1, {'@': 289}), 67: (1, {'@': 289}), 3: (1, {'@': 289}), 9: (1, {'@': 289}), 115: (1, {'@': 289}), 116: (1, {'@': 289}), 5: (1, {'@': 289}), 125: (1, {'@': 289}), 59: (1, {'@': 289}), 120: (1, {'@': 289}), 65: (1, {'@': 289}), 6: (1, {'@': 289}), 54: (1, {'@': 289}), 123: (1, {'@': 289}), 73: (1, {'@': 289}), 74: (1, {'@': 289}), 75: (1, {'@': 289}), 76: (1, {'@': 289}), 77: (1, {'@': 289}), 79: (1, {'@': 289}), 61: (1, {'@': 289}), 82: (1, {'@': 289}), 10: (1, {'@': 289}), 66: (1, {'@': 289}), 83: (1, {'@': 289}), 85: (1, {'@': 289}), 11: (1, {'@': 289}), 88: (1, {'@': 289}), 90: (1, {'@': 289}), 91: (1, {'@': 289}), 92: (1, {'@': 289}), 94: (1, {'@': 289}), 47: (1, {'@': 289}), 95: (1, {'@': 289}), 96: (1, {'@': 289}), 4: (1, {'@': 289}), 55: (1, {'@': 289}), 98: (1, {'@': 289}), 101: (1, {'@': 289}), 13: (1, {'@': 289}), 104: (1, {'@': 289}), 106: (1, {'@': 289}), 58: (1, {'@': 289}), 109: (1, {'@': 289}), 1: (1, {'@': 289}), 110: (1, {'@': 289}), 111: (1, {'@': 289}), 113: (1, {'@': 289}), 114: (1, {'@': 289}), 32: (1, {'@': 289}), 57: (1, {'@': 289}), 117: (1, {'@': 289}), 118: (1, {'@': 289}), 119: (1, {'@': 289}), 121: (1, {'@': 289}), 122: (1, {'@': 289}), 124: (1, {'@': 289})}, 341: {57: (0, 36), 56: (1, {'@': 384}), 54: (1, {'@': 384}), 63: (1, {'@': 384}), 11: (1, {'@': 384}), 12: (1, {'@': 384}), 13: (1, {'@': 384}), 72: (1, {'@': 384}), 74: (1, {'@': 384}), 98: (1, {'@': 384}), 75: (1, {'@': 384}), 99: (1, {'@': 384}), 100: (1, {'@': 384}), 76: (1, {'@': 384}), 101: (1, {'@': 384}), 102: (1, {'@': 384}), 103: (1, {'@': 384}), 77: (1, {'@': 384}), 78: (1, {'@': 384}), 104: (1, {'@': 384}), 105: (1, {'@': 384}), 106: (1, {'@': 384}), 107: (1, {'@': 384}), 79: (1, {'@': 384}), 108: (1, {'@': 384}), 80: (1, {'@': 384}), 81: (1, {'@': 384}), 82: (1, {'@': 384}), 109: (1, {'@': 384}), 83: (1, {'@': 384}), 1: (1, {'@': 384}), 110: (1, {'@': 384}), 111: (1, {'@': 384}), 84: (1, {'@': 384}), 112: (1, {'@': 384}), 85: (1, {'@': 384}), 86: (1, {'@': 384}), 113: (1, {'@': 384}), 114: (1, {'@': 384}), 87: (1, {'@': 384}), 3: (1, {'@': 384}), 88: (1, {'@': 384}), 89: (1, {'@': 384}), 115: (1, {'@': 384}), 90: (1, {'@': 384}), 116: (1, {'@': 384}), 117: (1, {'@': 384}), 118: (1, {'@': 384}), 91: (1, {'@': 384}), 119: (1, {'@': 384}), 92: (1, {'@': 384}), 93: (1, {'@': 384}), 94: (1, {'@': 384}), 120: (1, {'@': 384}), 95: (1, {'@': 384}), 96: (1, {'@': 384}), 121: (1, {'@': 384}), 123: (1, {'@': 384}), 97: (1, {'@': 384}), 124: (1, {'@': 384}), 5: (1, {'@': 384}), 10: (1, {'@': 384}), 43: (1, {'@': 384}), 73: (1, {'@': 384}), 6: (1, {'@': 384}), 32: (1, {'@': 384}), 122: (1, {'@': 384})}, 342: {5: (1, {'@': 254}), 11: (1, {'@': 254}), 12: (1, {'@': 254}), 13: (1, {'@': 254})}, 343: {1: (0, 185), 2: (0, 167), 3: (0, 264)}, 344: {11: (1, {'@': 210}), 12: (1, {'@': 210}), 13: (1, {'@': 210})}, 345: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 13: (0, 707), 199: (0, 620), 105: (0, 588), 149: (0, 779), 148: (0, 769), 123: (0, 776), 110: (0, 724), 91: (0, 716), 160: (0, 746), 81: (0, 786), 11: (0, 632), 175: (0, 593), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 92: (0, 604), 101: (0, 699), 100: (0, 685), 152: (0, 536), 74: (0, 597), 113: (0, 712), 153: (0, 715), 154: (0, 780), 107: (0, 591), 193: (0, 624), 191: (0, 757), 114: (0, 759), 200: (0, 750), 116: (0, 774), 155: (0, 651), 75: (0, 654), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 207: (0, 556), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 201: (0, 618), 117: (0, 626), 202: (0, 637), 80: (0, 639), 88: (0, 650), 208: (0, 657), 99: (0, 644), 97: (0, 700), 192: (0, 686), 194: (0, 721), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 150: (0, 741), 161: (0, 729), 111: (0, 783), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 346: {9: (0, 578)}, 347: {9: (0, 332), 172: (0, 375), 72: (1, {'@': 267}), 56: (1, {'@': 267}), 78: (1, {'@': 267}), 80: (1, {'@': 267}), 81: (1, {'@': 267}), 68: (1, {'@': 267}), 84: (1, {'@': 267}), 69: (1, {'@': 267}), 86: (1, {'@': 267}), 71: (1, {'@': 267}), 87: (1, {'@': 267}), 89: (1, {'@': 267}), 12: (1, {'@': 267}), 93: (1, {'@': 267}), 60: (1, {'@': 267}), 63: (1, {'@': 267}), 62: (1, {'@': 267}), 97: (1, {'@': 267}), 99: (1, {'@': 267}), 100: (1, {'@': 267}), 102: (1, {'@': 267}), 43: (1, {'@': 267}), 103: (1, {'@': 267}), 105: (1, {'@': 267}), 107: (1, {'@': 267}), 108: (1, {'@': 267}), 112: (1, {'@': 267}), 70: (1, {'@': 267}), 64: (1, {'@': 267}), 67: (1, {'@': 267}), 3: (1, {'@': 267}), 115: (1, {'@': 267}), 116: (1, {'@': 267}), 5: (1, {'@': 267}), 125: (1, {'@': 267}), 59: (1, {'@': 267}), 120: (1, {'@': 267}), 65: (1, {'@': 267}), 6: (1, {'@': 267}), 54: (1, {'@': 267}), 123: (1, {'@': 267}), 73: (1, {'@': 267}), 74: (1, {'@': 267}), 75: (1, {'@': 267}), 76: (1, {'@': 267}), 77: (1, {'@': 267}), 79: (1, {'@': 267}), 61: (1, {'@': 267}), 82: (1, {'@': 267}), 10: (1, {'@': 267}), 66: (1, {'@': 267}), 83: (1, {'@': 267}), 85: (1, {'@': 267}), 11: (1, {'@': 267}), 88: (1, {'@': 267}), 90: (1, {'@': 267}), 91: (1, {'@': 267}), 92: (1, {'@': 267}), 94: (1, {'@': 267}), 47: (1, {'@': 267}), 95: (1, {'@': 267}), 96: (1, {'@': 267}), 4: (1, {'@': 267}), 55: (1, {'@': 267}), 98: (1, {'@': 267}), 101: (1, {'@': 267}), 13: (1, {'@': 267}), 104: (1, {'@': 267}), 106: (1, {'@': 267}), 58: (1, {'@': 267}), 109: (1, {'@': 267}), 1: (1, {'@': 267}), 110: (1, {'@': 267}), 111: (1, {'@': 267}), 113: (1, {'@': 267}), 114: (1, {'@': 267}), 32: (1, {'@': 267}), 57: (1, {'@': 267}), 117: (1, {'@': 267}), 118: (1, {'@': 267}), 119: (1, {'@': 267}), 121: (1, {'@': 267}), 122: (1, {'@': 267}), 124: (1, {'@': 267})}, 348: {11: (1, {'@': 186}), 12: (1, {'@': 186}), 13: (1, {'@': 186})}, 349: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 131: (0, 134), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 350: {11: (1, {'@': 211}), 12: (1, {'@': 211}), 13: (1, {'@': 211})}, 351: {167: (0, 527), 163: (0, 788)}, 352: {7: (0, 377), 8: (0, 582), 11: (1, {'@': 352}), 12: (1, {'@': 352}), 13: (1, {'@': 352})}, 353: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 521), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 1: (0, 614), 25: (0, 203), 134: (0, 433), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 54: (0, 41), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 66), 197: (0, 464), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 354: {9: (0, 257), 172: (0, 687), 72: (1, {'@': 273}), 56: (1, {'@': 273}), 78: (1, {'@': 273}), 80: (1, {'@': 273}), 81: (1, {'@': 273}), 68: (1, {'@': 273}), 84: (1, {'@': 273}), 69: (1, {'@': 273}), 86: (1, {'@': 273}), 71: (1, {'@': 273}), 87: (1, {'@': 273}), 89: (1, {'@': 273}), 12: (1, {'@': 273}), 93: (1, {'@': 273}), 60: (1, {'@': 273}), 63: (1, {'@': 273}), 62: (1, {'@': 273}), 97: (1, {'@': 273}), 99: (1, {'@': 273}), 100: (1, {'@': 273}), 102: (1, {'@': 273}), 43: (1, {'@': 273}), 103: (1, {'@': 273}), 105: (1, {'@': 273}), 107: (1, {'@': 273}), 108: (1, {'@': 273}), 112: (1, {'@': 273}), 70: (1, {'@': 273}), 64: (1, {'@': 273}), 67: (1, {'@': 273}), 3: (1, {'@': 273}), 115: (1, {'@': 273}), 116: (1, {'@': 273}), 5: (1, {'@': 273}), 125: (1, {'@': 273}), 59: (1, {'@': 273}), 120: (1, {'@': 273}), 65: (1, {'@': 273}), 6: (1, {'@': 273}), 54: (1, {'@': 273}), 123: (1, {'@': 273}), 73: (1, {'@': 273}), 74: (1, {'@': 273}), 75: (1, {'@': 273}), 76: (1, {'@': 273}), 77: (1, {'@': 273}), 79: (1, {'@': 273}), 61: (1, {'@': 273}), 82: (1, {'@': 273}), 10: (1, {'@': 273}), 66: (1, {'@': 273}), 83: (1, {'@': 273}), 85: (1, {'@': 273}), 11: (1, {'@': 273}), 88: (1, {'@': 273}), 90: (1, {'@': 273}), 91: (1, {'@': 273}), 92: (1, {'@': 273}), 94: (1, {'@': 273}), 47: (1, {'@': 273}), 95: (1, {'@': 273}), 96: (1, {'@': 273}), 4: (1, {'@': 273}), 55: (1, {'@': 273}), 98: (1, {'@': 273}), 101: (1, {'@': 273}), 13: (1, {'@': 273}), 104: (1, {'@': 273}), 106: (1, {'@': 273}), 58: (1, {'@': 273}), 109: (1, {'@': 273}), 1: (1, {'@': 273}), 110: (1, {'@': 273}), 111: (1, {'@': 273}), 113: (1, {'@': 273}), 114: (1, {'@': 273}), 32: (1, {'@': 273}), 57: (1, {'@': 273}), 117: (1, {'@': 273}), 118: (1, {'@': 273}), 119: (1, {'@': 273}), 121: (1, {'@': 273}), 122: (1, {'@': 273}), 124: (1, {'@': 273})}, 355: {5: (1, {'@': 339}), 10: (1, {'@': 339})}, 356: {72: (1, {'@': 268}), 56: (1, {'@': 268}), 78: (1, {'@': 268}), 80: (1, {'@': 268}), 81: (1, {'@': 268}), 68: (1, {'@': 268}), 84: (1, {'@': 268}), 69: (1, {'@': 268}), 86: (1, {'@': 268}), 71: (1, {'@': 268}), 87: (1, {'@': 268}), 89: (1, {'@': 268}), 12: (1, {'@': 268}), 93: (1, {'@': 268}), 60: (1, {'@': 268}), 63: (1, {'@': 268}), 62: (1, {'@': 268}), 97: (1, {'@': 268}), 99: (1, {'@': 268}), 100: (1, {'@': 268}), 102: (1, {'@': 268}), 43: (1, {'@': 268}), 103: (1, {'@': 268}), 105: (1, {'@': 268}), 107: (1, {'@': 268}), 108: (1, {'@': 268}), 112: (1, {'@': 268}), 70: (1, {'@': 268}), 64: (1, {'@': 268}), 67: (1, {'@': 268}), 3: (1, {'@': 268}), 9: (1, {'@': 268}), 115: (1, {'@': 268}), 116: (1, {'@': 268}), 5: (1, {'@': 268}), 125: (1, {'@': 268}), 59: (1, {'@': 268}), 120: (1, {'@': 268}), 65: (1, {'@': 268}), 6: (1, {'@': 268}), 54: (1, {'@': 268}), 123: (1, {'@': 268}), 73: (1, {'@': 268}), 74: (1, {'@': 268}), 75: (1, {'@': 268}), 76: (1, {'@': 268}), 77: (1, {'@': 268}), 79: (1, {'@': 268}), 61: (1, {'@': 268}), 82: (1, {'@': 268}), 10: (1, {'@': 268}), 66: (1, {'@': 268}), 83: (1, {'@': 268}), 85: (1, {'@': 268}), 11: (1, {'@': 268}), 88: (1, {'@': 268}), 90: (1, {'@': 268}), 91: (1, {'@': 268}), 92: (1, {'@': 268}), 94: (1, {'@': 268}), 47: (1, {'@': 268}), 95: (1, {'@': 268}), 96: (1, {'@': 268}), 4: (1, {'@': 268}), 55: (1, {'@': 268}), 98: (1, {'@': 268}), 101: (1, {'@': 268}), 13: (1, {'@': 268}), 104: (1, {'@': 268}), 106: (1, {'@': 268}), 58: (1, {'@': 268}), 109: (1, {'@': 268}), 1: (1, {'@': 268}), 110: (1, {'@': 268}), 111: (1, {'@': 268}), 113: (1, {'@': 268}), 114: (1, {'@': 268}), 32: (1, {'@': 268}), 57: (1, {'@': 268}), 117: (1, {'@': 268}), 118: (1, {'@': 268}), 119: (1, {'@': 268}), 121: (1, {'@': 268}), 122: (1, {'@': 268}), 124: (1, {'@': 268})}, 357: {5: (0, 416)}, 358: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 521), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 54: (0, 208), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 1: (0, 614), 25: (0, 203), 134: (0, 433), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 66), 197: (0, 464), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 359: {173: (0, 526), 121: (0, 528), 11: (1, {'@': 70}), 13: (1, {'@': 70}), 12: (1, {'@': 70})}, 360: {5: (0, 116), 11: (1, {'@': 25}), 13: (1, {'@': 25}), 12: (1, {'@': 25})}, 361: {11: (0, 75), 13: (1, {'@': 143})}, 362: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 136: (0, 403), 23: (0, 219), 29: (0, 474), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 135: (0, 14), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 363: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 131: (0, 476), 37: (0, 494), 48: (0, 486)}, 364: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 131: (0, 453), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 365: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 131: (0, 479), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 366: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 131: (0, 482), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 367: {11: (1, {'@': 21}), 13: (1, {'@': 21}), 12: (1, {'@': 21})}, 368: {11: (1, {'@': 104}), 13: (1, {'@': 104}), 12: (1, {'@': 104})}, 369: {72: (1, {'@': 306}), 56: (1, {'@': 306}), 78: (1, {'@': 306}), 80: (1, {'@': 306}), 81: (1, {'@': 306}), 68: (1, {'@': 306}), 84: (1, {'@': 306}), 69: (1, {'@': 306}), 86: (1, {'@': 306}), 71: (1, {'@': 306}), 87: (1, {'@': 306}), 89: (1, {'@': 306}), 12: (1, {'@': 306}), 93: (1, {'@': 306}), 60: (1, {'@': 306}), 63: (1, {'@': 306}), 62: (1, {'@': 306}), 97: (1, {'@': 306}), 99: (1, {'@': 306}), 100: (1, {'@': 306}), 102: (1, {'@': 306}), 43: (1, {'@': 306}), 103: (1, {'@': 306}), 105: (1, {'@': 306}), 107: (1, {'@': 306}), 108: (1, {'@': 306}), 112: (1, {'@': 306}), 70: (1, {'@': 306}), 64: (1, {'@': 306}), 67: (1, {'@': 306}), 3: (1, {'@': 306}), 9: (1, {'@': 306}), 115: (1, {'@': 306}), 116: (1, {'@': 306}), 5: (1, {'@': 306}), 125: (1, {'@': 306}), 59: (1, {'@': 306}), 120: (1, {'@': 306}), 65: (1, {'@': 306}), 6: (1, {'@': 306}), 54: (1, {'@': 306}), 123: (1, {'@': 306}), 73: (1, {'@': 306}), 74: (1, {'@': 306}), 75: (1, {'@': 306}), 76: (1, {'@': 306}), 77: (1, {'@': 306}), 79: (1, {'@': 306}), 61: (1, {'@': 306}), 82: (1, {'@': 306}), 10: (1, {'@': 306}), 66: (1, {'@': 306}), 83: (1, {'@': 306}), 85: (1, {'@': 306}), 11: (1, {'@': 306}), 88: (1, {'@': 306}), 90: (1, {'@': 306}), 91: (1, {'@': 306}), 92: (1, {'@': 306}), 94: (1, {'@': 306}), 47: (1, {'@': 306}), 95: (1, {'@': 306}), 96: (1, {'@': 306}), 4: (1, {'@': 306}), 55: (1, {'@': 306}), 98: (1, {'@': 306}), 101: (1, {'@': 306}), 13: (1, {'@': 306}), 104: (1, {'@': 306}), 106: (1, {'@': 306}), 58: (1, {'@': 306}), 109: (1, {'@': 306}), 1: (1, {'@': 306}), 110: (1, {'@': 306}), 111: (1, {'@': 306}), 113: (1, {'@': 306}), 114: (1, {'@': 306}), 32: (1, {'@': 306}), 57: (1, {'@': 306}), 117: (1, {'@': 306}), 118: (1, {'@': 306}), 119: (1, {'@': 306}), 121: (1, {'@': 306}), 122: (1, {'@': 306}), 124: (1, {'@': 306})}, 370: {72: (1, {'@': 288}), 56: (1, {'@': 288}), 78: (1, {'@': 288}), 80: (1, {'@': 288}), 81: (1, {'@': 288}), 68: (1, {'@': 288}), 84: (1, {'@': 288}), 69: (1, {'@': 288}), 86: (1, {'@': 288}), 71: (1, {'@': 288}), 87: (1, {'@': 288}), 89: (1, {'@': 288}), 12: (1, {'@': 288}), 93: (1, {'@': 288}), 60: (1, {'@': 288}), 63: (1, {'@': 288}), 62: (1, {'@': 288}), 97: (1, {'@': 288}), 99: (1, {'@': 288}), 100: (1, {'@': 288}), 102: (1, {'@': 288}), 43: (1, {'@': 288}), 103: (1, {'@': 288}), 105: (1, {'@': 288}), 107: (1, {'@': 288}), 108: (1, {'@': 288}), 112: (1, {'@': 288}), 70: (1, {'@': 288}), 64: (1, {'@': 288}), 67: (1, {'@': 288}), 3: (1, {'@': 288}), 9: (1, {'@': 288}), 115: (1, {'@': 288}), 116: (1, {'@': 288}), 5: (1, {'@': 288}), 125: (1, {'@': 288}), 59: (1, {'@': 288}), 120: (1, {'@': 288}), 65: (1, {'@': 288}), 6: (1, {'@': 288}), 54: (1, {'@': 288}), 123: (1, {'@': 288}), 73: (1, {'@': 288}), 74: (1, {'@': 288}), 75: (1, {'@': 288}), 76: (1, {'@': 288}), 77: (1, {'@': 288}), 79: (1, {'@': 288}), 61: (1, {'@': 288}), 82: (1, {'@': 288}), 10: (1, {'@': 288}), 66: (1, {'@': 288}), 83: (1, {'@': 288}), 85: (1, {'@': 288}), 11: (1, {'@': 288}), 88: (1, {'@': 288}), 90: (1, {'@': 288}), 91: (1, {'@': 288}), 92: (1, {'@': 288}), 94: (1, {'@': 288}), 47: (1, {'@': 288}), 95: (1, {'@': 288}), 96: (1, {'@': 288}), 4: (1, {'@': 288}), 55: (1, {'@': 288}), 98: (1, {'@': 288}), 101: (1, {'@': 288}), 13: (1, {'@': 288}), 104: (1, {'@': 288}), 106: (1, {'@': 288}), 58: (1, {'@': 288}), 109: (1, {'@': 288}), 1: (1, {'@': 288}), 110: (1, {'@': 288}), 111: (1, {'@': 288}), 113: (1, {'@': 288}), 114: (1, {'@': 288}), 32: (1, {'@': 288}), 57: (1, {'@': 288}), 117: (1, {'@': 288}), 118: (1, {'@': 288}), 119: (1, {'@': 288}), 121: (1, {'@': 288}), 122: (1, {'@': 288}), 124: (1, {'@': 288})}, 371: {205: (0, 410), 206: (0, 294)}, 372: {11: (1, {'@': 72}), 13: (1, {'@': 72}), 12: (1, {'@': 72})}, 373: {9: (0, 785)}, 374: {1: (0, 185), 2: (0, 4), 0: (0, 226), 3: (0, 264)}, 375: {72: (1, {'@': 304}), 56: (1, {'@': 304}), 78: (1, {'@': 304}), 80: (1, {'@': 304}), 81: (1, {'@': 304}), 68: (1, {'@': 304}), 84: (1, {'@': 304}), 69: (1, {'@': 304}), 86: (1, {'@': 304}), 71: (1, {'@': 304}), 87: (1, {'@': 304}), 89: (1, {'@': 304}), 12: (1, {'@': 304}), 93: (1, {'@': 304}), 60: (1, {'@': 304}), 63: (1, {'@': 304}), 62: (1, {'@': 304}), 97: (1, {'@': 304}), 99: (1, {'@': 304}), 100: (1, {'@': 304}), 102: (1, {'@': 304}), 43: (1, {'@': 304}), 103: (1, {'@': 304}), 105: (1, {'@': 304}), 107: (1, {'@': 304}), 108: (1, {'@': 304}), 112: (1, {'@': 304}), 70: (1, {'@': 304}), 64: (1, {'@': 304}), 67: (1, {'@': 304}), 3: (1, {'@': 304}), 9: (1, {'@': 304}), 115: (1, {'@': 304}), 116: (1, {'@': 304}), 5: (1, {'@': 304}), 125: (1, {'@': 304}), 59: (1, {'@': 304}), 120: (1, {'@': 304}), 65: (1, {'@': 304}), 6: (1, {'@': 304}), 54: (1, {'@': 304}), 123: (1, {'@': 304}), 73: (1, {'@': 304}), 74: (1, {'@': 304}), 75: (1, {'@': 304}), 76: (1, {'@': 304}), 77: (1, {'@': 304}), 79: (1, {'@': 304}), 61: (1, {'@': 304}), 82: (1, {'@': 304}), 10: (1, {'@': 304}), 66: (1, {'@': 304}), 83: (1, {'@': 304}), 85: (1, {'@': 304}), 11: (1, {'@': 304}), 88: (1, {'@': 304}), 90: (1, {'@': 304}), 91: (1, {'@': 304}), 92: (1, {'@': 304}), 94: (1, {'@': 304}), 47: (1, {'@': 304}), 95: (1, {'@': 304}), 96: (1, {'@': 304}), 4: (1, {'@': 304}), 55: (1, {'@': 304}), 98: (1, {'@': 304}), 101: (1, {'@': 304}), 13: (1, {'@': 304}), 104: (1, {'@': 304}), 106: (1, {'@': 304}), 58: (1, {'@': 304}), 109: (1, {'@': 304}), 1: (1, {'@': 304}), 110: (1, {'@': 304}), 111: (1, {'@': 304}), 113: (1, {'@': 304}), 114: (1, {'@': 304}), 32: (1, {'@': 304}), 57: (1, {'@': 304}), 117: (1, {'@': 304}), 118: (1, {'@': 304}), 119: (1, {'@': 304}), 121: (1, {'@': 304}), 122: (1, {'@': 304}), 124: (1, {'@': 304})}, 376: {11: (1, {'@': 135}), 13: (1, {'@': 135}), 12: (1, {'@': 135})}, 377: {181: (0, 540), 179: (0, 104), 209: (0, 97), 180: (0, 67), 183: (0, 200), 176: (0, 189), 182: (0, 178), 3: (0, 307), 178: (0, 123), 210: (0, 190), 184: (0, 111)}, 378: {5: (0, 500)}, 379: {11: (1, {'@': 80}), 13: (1, {'@': 80}), 12: (1, {'@': 80})}, 380: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 100), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 381: {11: (1, {'@': 322}), 13: (1, {'@': 322}), 12: (1, {'@': 322})}, 382: {11: (1, {'@': 33}), 13: (1, {'@': 33}), 12: (1, {'@': 33})}, 383: {72: (1, {'@': 301}), 56: (1, {'@': 301}), 78: (1, {'@': 301}), 80: (1, {'@': 301}), 81: (1, {'@': 301}), 68: (1, {'@': 301}), 84: (1, {'@': 301}), 69: (1, {'@': 301}), 86: (1, {'@': 301}), 71: (1, {'@': 301}), 87: (1, {'@': 301}), 89: (1, {'@': 301}), 12: (1, {'@': 301}), 93: (1, {'@': 301}), 60: (1, {'@': 301}), 63: (1, {'@': 301}), 62: (1, {'@': 301}), 97: (1, {'@': 301}), 99: (1, {'@': 301}), 100: (1, {'@': 301}), 102: (1, {'@': 301}), 43: (1, {'@': 301}), 103: (1, {'@': 301}), 105: (1, {'@': 301}), 107: (1, {'@': 301}), 108: (1, {'@': 301}), 112: (1, {'@': 301}), 70: (1, {'@': 301}), 64: (1, {'@': 301}), 67: (1, {'@': 301}), 3: (1, {'@': 301}), 9: (1, {'@': 301}), 115: (1, {'@': 301}), 116: (1, {'@': 301}), 5: (1, {'@': 301}), 125: (1, {'@': 301}), 59: (1, {'@': 301}), 120: (1, {'@': 301}), 65: (1, {'@': 301}), 6: (1, {'@': 301}), 54: (1, {'@': 301}), 123: (1, {'@': 301}), 73: (1, {'@': 301}), 74: (1, {'@': 301}), 75: (1, {'@': 301}), 76: (1, {'@': 301}), 77: (1, {'@': 301}), 79: (1, {'@': 301}), 61: (1, {'@': 301}), 82: (1, {'@': 301}), 10: (1, {'@': 301}), 66: (1, {'@': 301}), 83: (1, {'@': 301}), 85: (1, {'@': 301}), 11: (1, {'@': 301}), 88: (1, {'@': 301}), 90: (1, {'@': 301}), 91: (1, {'@': 301}), 92: (1, {'@': 301}), 94: (1, {'@': 301}), 47: (1, {'@': 301}), 95: (1, {'@': 301}), 96: (1, {'@': 301}), 4: (1, {'@': 301}), 55: (1, {'@': 301}), 98: (1, {'@': 301}), 101: (1, {'@': 301}), 13: (1, {'@': 301}), 104: (1, {'@': 301}), 106: (1, {'@': 301}), 58: (1, {'@': 301}), 109: (1, {'@': 301}), 1: (1, {'@': 301}), 110: (1, {'@': 301}), 111: (1, {'@': 301}), 113: (1, {'@': 301}), 114: (1, {'@': 301}), 32: (1, {'@': 301}), 57: (1, {'@': 301}), 117: (1, {'@': 301}), 118: (1, {'@': 301}), 119: (1, {'@': 301}), 121: (1, {'@': 301}), 122: (1, {'@': 301}), 124: (1, {'@': 301})}, 384: {11: (1, {'@': 38}), 13: (1, {'@': 38}), 12: (1, {'@': 38})}, 385: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 131: (0, 17), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 386: {11: (1, {'@': 133}), 13: (1, {'@': 133}), 12: (1, {'@': 133})}, 387: {7: (1, {'@': 333}), 11: (1, {'@': 333}), 12: (1, {'@': 333}), 13: (1, {'@': 333})}, 388: {10: (0, 396)}, 389: {5: (1, {'@': 235}), 11: (1, {'@': 235}), 12: (1, {'@': 235}), 43: (1, {'@': 235}), 13: (1, {'@': 235})}, 390: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 54: (0, 222), 15: (0, 205), 32: (0, 504), 30: (0, 194), 131: (0, 13), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 391: {11: (1, {'@': 218}), 12: (1, {'@': 218}), 13: (1, {'@': 218})}, 392: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 150: (0, 81), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 156: (0, 747), 163: (0, 748), 11: (0, 73), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 167: (0, 594), 112: (0, 522), 13: (0, 627), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 393: {9: (0, 329), 11: (1, {'@': 120}), 13: (1, {'@': 120}), 12: (1, {'@': 120})}, 394: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 131: (0, 151), 37: (0, 494), 48: (0, 486)}, 395: {4: (0, 452)}, 396: {11: (1, {'@': 106}), 13: (1, {'@': 106}), 12: (1, {'@': 106})}, 397: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 23: (0, 219), 29: (0, 474), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 24: (0, 408), 40: (0, 503), 26: (0, 405), 53: (0, 621), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 398: {72: (1, {'@': 313}), 73: (1, {'@': 313}), 56: (1, {'@': 313}), 74: (1, {'@': 313}), 75: (1, {'@': 313}), 76: (1, {'@': 313}), 77: (1, {'@': 313}), 78: (1, {'@': 313}), 79: (1, {'@': 313}), 80: (1, {'@': 313}), 61: (1, {'@': 313}), 81: (1, {'@': 313}), 82: (1, {'@': 313}), 10: (1, {'@': 313}), 66: (1, {'@': 313}), 83: (1, {'@': 313}), 68: (1, {'@': 313}), 84: (1, {'@': 313}), 69: (1, {'@': 313}), 85: (1, {'@': 313}), 11: (1, {'@': 313}), 86: (1, {'@': 313}), 71: (1, {'@': 313}), 87: (1, {'@': 313}), 88: (1, {'@': 313}), 89: (1, {'@': 313}), 12: (1, {'@': 313}), 90: (1, {'@': 313}), 91: (1, {'@': 313}), 92: (1, {'@': 313}), 93: (1, {'@': 313}), 94: (1, {'@': 313}), 60: (1, {'@': 313}), 63: (1, {'@': 313}), 62: (1, {'@': 313}), 47: (1, {'@': 313}), 95: (1, {'@': 313}), 96: (1, {'@': 313}), 97: (1, {'@': 313}), 4: (1, {'@': 313}), 55: (1, {'@': 313}), 98: (1, {'@': 313}), 99: (1, {'@': 313}), 100: (1, {'@': 313}), 102: (1, {'@': 313}), 43: (1, {'@': 313}), 101: (1, {'@': 313}), 13: (1, {'@': 313}), 103: (1, {'@': 313}), 105: (1, {'@': 313}), 104: (1, {'@': 313}), 106: (1, {'@': 313}), 107: (1, {'@': 313}), 58: (1, {'@': 313}), 108: (1, {'@': 313}), 109: (1, {'@': 313}), 1: (1, {'@': 313}), 110: (1, {'@': 313}), 111: (1, {'@': 313}), 112: (1, {'@': 313}), 70: (1, {'@': 313}), 64: (1, {'@': 313}), 67: (1, {'@': 313}), 113: (1, {'@': 313}), 114: (1, {'@': 313}), 3: (1, {'@': 313}), 9: (1, {'@': 313}), 32: (1, {'@': 313}), 115: (1, {'@': 313}), 116: (1, {'@': 313}), 5: (1, {'@': 313}), 125: (1, {'@': 313}), 57: (1, {'@': 313}), 117: (1, {'@': 313}), 118: (1, {'@': 313}), 119: (1, {'@': 313}), 59: (1, {'@': 313}), 120: (1, {'@': 313}), 65: (1, {'@': 313}), 121: (1, {'@': 313}), 6: (1, {'@': 313}), 54: (1, {'@': 313}), 122: (1, {'@': 313}), 123: (1, {'@': 313}), 124: (1, {'@': 313})}, 399: {11: (1, {'@': 184}), 12: (1, {'@': 184}), 13: (1, {'@': 184})}, 400: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 131: (0, 144), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 401: {11: (1, {'@': 29}), 13: (1, {'@': 29}), 12: (1, {'@': 29})}, 402: {11: (1, {'@': 51}), 13: (1, {'@': 51}), 12: (1, {'@': 51})}, 403: {125: (0, 751), 55: (1, {'@': 413}), 56: (1, {'@': 413}), 57: (1, {'@': 413}), 58: (1, {'@': 413}), 59: (1, {'@': 413}), 60: (1, {'@': 413}), 61: (1, {'@': 413}), 47: (1, {'@': 413}), 62: (1, {'@': 413}), 63: (1, {'@': 413}), 64: (1, {'@': 413}), 65: (1, {'@': 413}), 66: (1, {'@': 413}), 67: (1, {'@': 413}), 68: (1, {'@': 413}), 54: (1, {'@': 413}), 69: (1, {'@': 413}), 70: (1, {'@': 413}), 4: (1, {'@': 413}), 71: (1, {'@': 413}), 12: (1, {'@': 413}), 13: (1, {'@': 413}), 11: (1, {'@': 413}), 72: (1, {'@': 413}), 73: (1, {'@': 413}), 74: (1, {'@': 413}), 75: (1, {'@': 413}), 76: (1, {'@': 413}), 77: (1, {'@': 413}), 78: (1, {'@': 413}), 79: (1, {'@': 413}), 80: (1, {'@': 413}), 81: (1, {'@': 413}), 10: (1, {'@': 413}), 82: (1, {'@': 413}), 83: (1, {'@': 413}), 84: (1, {'@': 413}), 85: (1, {'@': 413}), 86: (1, {'@': 413}), 87: (1, {'@': 413}), 88: (1, {'@': 413}), 89: (1, {'@': 413}), 90: (1, {'@': 413}), 91: (1, {'@': 413}), 92: (1, {'@': 413}), 93: (1, {'@': 413}), 94: (1, {'@': 413}), 95: (1, {'@': 413}), 96: (1, {'@': 413}), 97: (1, {'@': 413}), 98: (1, {'@': 413}), 99: (1, {'@': 413}), 100: (1, {'@': 413}), 101: (1, {'@': 413}), 102: (1, {'@': 413}), 43: (1, {'@': 413}), 103: (1, {'@': 413}), 104: (1, {'@': 413}), 105: (1, {'@': 413}), 106: (1, {'@': 413}), 107: (1, {'@': 413}), 108: (1, {'@': 413}), 109: (1, {'@': 413}), 1: (1, {'@': 413}), 110: (1, {'@': 413}), 111: (1, {'@': 413}), 112: (1, {'@': 413}), 113: (1, {'@': 413}), 114: (1, {'@': 413}), 3: (1, {'@': 413}), 32: (1, {'@': 413}), 115: (1, {'@': 413}), 5: (1, {'@': 413}), 116: (1, {'@': 413}), 117: (1, {'@': 413}), 118: (1, {'@': 413}), 119: (1, {'@': 413}), 120: (1, {'@': 413}), 121: (1, {'@': 413}), 6: (1, {'@': 413}), 122: (1, {'@': 413}), 123: (1, {'@': 413}), 124: (1, {'@': 413})}, 404: {11: (1, {'@': 92}), 13: (1, {'@': 92}), 12: (1, {'@': 92})}, 405: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 23: (0, 219), 29: (0, 474), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 53: (0, 734), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 24: (0, 408), 40: (0, 503), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 406: {11: (1, {'@': 83}), 13: (1, {'@': 83}), 12: (1, {'@': 83})}, 407: {3: (0, 5), 11: (1, {'@': 215}), 12: (1, {'@': 215}), 13: (1, {'@': 215})}, 408: {14: (1, {'@': 376}), 15: (1, {'@': 376}), 16: (1, {'@': 376}), 17: (1, {'@': 376}), 18: (1, {'@': 376}), 19: (1, {'@': 376}), 20: (1, {'@': 376}), 21: (1, {'@': 376}), 22: (1, {'@': 376}), 23: (1, {'@': 376}), 24: (1, {'@': 376}), 25: (1, {'@': 376}), 1: (1, {'@': 376}), 26: (1, {'@': 376}), 27: (1, {'@': 376}), 28: (1, {'@': 376}), 29: (1, {'@': 376}), 30: (1, {'@': 376}), 3: (1, {'@': 376}), 31: (1, {'@': 376}), 9: (1, {'@': 376}), 32: (1, {'@': 376}), 33: (1, {'@': 376}), 34: (1, {'@': 376}), 35: (1, {'@': 376}), 36: (1, {'@': 376}), 37: (1, {'@': 376}), 38: (1, {'@': 376}), 39: (1, {'@': 376}), 40: (1, {'@': 376}), 41: (1, {'@': 376}), 42: (1, {'@': 376})}, 409: {11: (1, {'@': 203}), 12: (1, {'@': 203}), 13: (1, {'@': 203})}, 410: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 211: (0, 488), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 212: (0, 491), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 205: (0, 410), 47: (0, 237), 20: (0, 247), 206: (0, 495), 138: (0, 287), 131: (0, 498), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 411: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 131: (0, 581), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 412: {72: (1, {'@': 298}), 56: (1, {'@': 298}), 78: (1, {'@': 298}), 80: (1, {'@': 298}), 81: (1, {'@': 298}), 68: (1, {'@': 298}), 84: (1, {'@': 298}), 69: (1, {'@': 298}), 86: (1, {'@': 298}), 71: (1, {'@': 298}), 87: (1, {'@': 298}), 89: (1, {'@': 298}), 12: (1, {'@': 298}), 93: (1, {'@': 298}), 60: (1, {'@': 298}), 63: (1, {'@': 298}), 62: (1, {'@': 298}), 97: (1, {'@': 298}), 99: (1, {'@': 298}), 100: (1, {'@': 298}), 102: (1, {'@': 298}), 43: (1, {'@': 298}), 103: (1, {'@': 298}), 105: (1, {'@': 298}), 107: (1, {'@': 298}), 108: (1, {'@': 298}), 112: (1, {'@': 298}), 70: (1, {'@': 298}), 64: (1, {'@': 298}), 67: (1, {'@': 298}), 3: (1, {'@': 298}), 9: (1, {'@': 298}), 115: (1, {'@': 298}), 116: (1, {'@': 298}), 5: (1, {'@': 298}), 125: (1, {'@': 298}), 59: (1, {'@': 298}), 120: (1, {'@': 298}), 65: (1, {'@': 298}), 6: (1, {'@': 298}), 54: (1, {'@': 298}), 123: (1, {'@': 298}), 73: (1, {'@': 298}), 74: (1, {'@': 298}), 75: (1, {'@': 298}), 76: (1, {'@': 298}), 77: (1, {'@': 298}), 79: (1, {'@': 298}), 61: (1, {'@': 298}), 82: (1, {'@': 298}), 10: (1, {'@': 298}), 66: (1, {'@': 298}), 83: (1, {'@': 298}), 85: (1, {'@': 298}), 11: (1, {'@': 298}), 88: (1, {'@': 298}), 90: (1, {'@': 298}), 91: (1, {'@': 298}), 92: (1, {'@': 298}), 94: (1, {'@': 298}), 47: (1, {'@': 298}), 95: (1, {'@': 298}), 96: (1, {'@': 298}), 4: (1, {'@': 298}), 55: (1, {'@': 298}), 98: (1, {'@': 298}), 101: (1, {'@': 298}), 13: (1, {'@': 298}), 104: (1, {'@': 298}), 106: (1, {'@': 298}), 58: (1, {'@': 298}), 109: (1, {'@': 298}), 1: (1, {'@': 298}), 110: (1, {'@': 298}), 111: (1, {'@': 298}), 113: (1, {'@': 298}), 114: (1, {'@': 298}), 32: (1, {'@': 298}), 57: (1, {'@': 298}), 117: (1, {'@': 298}), 118: (1, {'@': 298}), 119: (1, {'@': 298}), 121: (1, {'@': 298}), 122: (1, {'@': 298}), 124: (1, {'@': 298})}, 413: {3: (0, 129), 33: (0, 91)}, 414: {11: (0, 75), 12: (1, {'@': 187}), 13: (1, {'@': 187})}, 415: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 131: (0, 46), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 416: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 131: (0, 175), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 417: {5: (1, {'@': 320}), 10: (1, {'@': 320}), 54: (1, {'@': 320}), 12: (1, {'@': 320}), 13: (1, {'@': 320}), 11: (1, {'@': 320})}, 418: {11: (1, {'@': 24}), 13: (1, {'@': 24}), 12: (1, {'@': 24})}, 419: {32: (0, 520), 81: (0, 702), 3: (0, 728)}, 420: {11: (1, {'@': 107}), 13: (1, {'@': 107}), 12: (1, {'@': 107})}, 421: {11: (1, {'@': 28}), 13: (1, {'@': 28}), 12: (1, {'@': 28})}, 422: {72: (1, {'@': 277}), 56: (1, {'@': 277}), 78: (1, {'@': 277}), 80: (1, {'@': 277}), 81: (1, {'@': 277}), 68: (1, {'@': 277}), 84: (1, {'@': 277}), 69: (1, {'@': 277}), 86: (1, {'@': 277}), 71: (1, {'@': 277}), 87: (1, {'@': 277}), 89: (1, {'@': 277}), 12: (1, {'@': 277}), 93: (1, {'@': 277}), 60: (1, {'@': 277}), 63: (1, {'@': 277}), 62: (1, {'@': 277}), 97: (1, {'@': 277}), 99: (1, {'@': 277}), 100: (1, {'@': 277}), 102: (1, {'@': 277}), 43: (1, {'@': 277}), 103: (1, {'@': 277}), 105: (1, {'@': 277}), 107: (1, {'@': 277}), 108: (1, {'@': 277}), 112: (1, {'@': 277}), 70: (1, {'@': 277}), 64: (1, {'@': 277}), 67: (1, {'@': 277}), 3: (1, {'@': 277}), 9: (1, {'@': 277}), 115: (1, {'@': 277}), 116: (1, {'@': 277}), 5: (1, {'@': 277}), 125: (1, {'@': 277}), 59: (1, {'@': 277}), 120: (1, {'@': 277}), 65: (1, {'@': 277}), 6: (1, {'@': 277}), 54: (1, {'@': 277}), 123: (1, {'@': 277}), 73: (1, {'@': 277}), 74: (1, {'@': 277}), 75: (1, {'@': 277}), 76: (1, {'@': 277}), 77: (1, {'@': 277}), 79: (1, {'@': 277}), 61: (1, {'@': 277}), 82: (1, {'@': 277}), 10: (1, {'@': 277}), 66: (1, {'@': 277}), 83: (1, {'@': 277}), 85: (1, {'@': 277}), 11: (1, {'@': 277}), 88: (1, {'@': 277}), 90: (1, {'@': 277}), 91: (1, {'@': 277}), 92: (1, {'@': 277}), 94: (1, {'@': 277}), 47: (1, {'@': 277}), 95: (1, {'@': 277}), 96: (1, {'@': 277}), 4: (1, {'@': 277}), 55: (1, {'@': 277}), 98: (1, {'@': 277}), 101: (1, {'@': 277}), 13: (1, {'@': 277}), 104: (1, {'@': 277}), 106: (1, {'@': 277}), 58: (1, {'@': 277}), 109: (1, {'@': 277}), 1: (1, {'@': 277}), 110: (1, {'@': 277}), 111: (1, {'@': 277}), 113: (1, {'@': 277}), 114: (1, {'@': 277}), 32: (1, {'@': 277}), 57: (1, {'@': 277}), 117: (1, {'@': 277}), 118: (1, {'@': 277}), 119: (1, {'@': 277}), 121: (1, {'@': 277}), 122: (1, {'@': 277}), 124: (1, {'@': 277})}, 423: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 131: (0, 163), 38: (0, 50), 37: (0, 494), 48: (0, 486), 10: (0, 179)}, 424: {4: (0, 590)}, 425: {5: (1, {'@': 335}), 10: (1, {'@': 335})}, 426: {72: (1, {'@': 325}), 74: (1, {'@': 325}), 98: (1, {'@': 325}), 75: (1, {'@': 325}), 99: (1, {'@': 325}), 100: (1, {'@': 325}), 101: (1, {'@': 325}), 102: (1, {'@': 325}), 76: (1, {'@': 325}), 13: (1, {'@': 325}), 103: (1, {'@': 325}), 104: (1, {'@': 325}), 105: (1, {'@': 325}), 78: (1, {'@': 325}), 77: (1, {'@': 325}), 106: (1, {'@': 325}), 107: (1, {'@': 325}), 79: (1, {'@': 325}), 108: (1, {'@': 325}), 80: (1, {'@': 325}), 81: (1, {'@': 325}), 82: (1, {'@': 325}), 109: (1, {'@': 325}), 83: (1, {'@': 325}), 1: (1, {'@': 325}), 110: (1, {'@': 325}), 111: (1, {'@': 325}), 84: (1, {'@': 325}), 112: (1, {'@': 325}), 85: (1, {'@': 325}), 11: (1, {'@': 325}), 86: (1, {'@': 325}), 113: (1, {'@': 325}), 114: (1, {'@': 325}), 163: (1, {'@': 325}), 87: (1, {'@': 325}), 3: (1, {'@': 325}), 88: (1, {'@': 325}), 89: (1, {'@': 325}), 115: (1, {'@': 325}), 116: (1, {'@': 325}), 117: (1, {'@': 325}), 118: (1, {'@': 325}), 91: (1, {'@': 325}), 119: (1, {'@': 325}), 92: (1, {'@': 325}), 93: (1, {'@': 325}), 94: (1, {'@': 325}), 120: (1, {'@': 325}), 95: (1, {'@': 325}), 96: (1, {'@': 325}), 121: (1, {'@': 325}), 123: (1, {'@': 325}), 97: (1, {'@': 325}), 124: (1, {'@': 325}), 167: (1, {'@': 325})}, 427: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 131: (0, 21), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 428: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 23: (0, 219), 29: (0, 474), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 53: (0, 744), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 24: (0, 408), 40: (0, 503), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 429: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 131: (0, 145), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 430: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 131: (0, 109), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 431: {11: (1, {'@': 32}), 13: (1, {'@': 32}), 12: (1, {'@': 32})}, 432: {5: (1, {'@': 249}), 11: (1, {'@': 249}), 12: (1, {'@': 249}), 43: (1, {'@': 249}), 13: (1, {'@': 249})}, 433: {63: (0, 538), 54: (1, {'@': 380}), 11: (1, {'@': 380}), 12: (1, {'@': 380}), 13: (1, {'@': 380}), 72: (1, {'@': 380}), 74: (1, {'@': 380}), 75: (1, {'@': 380}), 76: (1, {'@': 380}), 77: (1, {'@': 380}), 78: (1, {'@': 380}), 79: (1, {'@': 380}), 80: (1, {'@': 380}), 81: (1, {'@': 380}), 82: (1, {'@': 380}), 83: (1, {'@': 380}), 84: (1, {'@': 380}), 85: (1, {'@': 380}), 86: (1, {'@': 380}), 87: (1, {'@': 380}), 88: (1, {'@': 380}), 89: (1, {'@': 380}), 90: (1, {'@': 380}), 91: (1, {'@': 380}), 92: (1, {'@': 380}), 93: (1, {'@': 380}), 94: (1, {'@': 380}), 95: (1, {'@': 380}), 96: (1, {'@': 380}), 97: (1, {'@': 380}), 98: (1, {'@': 380}), 99: (1, {'@': 380}), 100: (1, {'@': 380}), 101: (1, {'@': 380}), 102: (1, {'@': 380}), 103: (1, {'@': 380}), 104: (1, {'@': 380}), 105: (1, {'@': 380}), 106: (1, {'@': 380}), 107: (1, {'@': 380}), 108: (1, {'@': 380}), 109: (1, {'@': 380}), 1: (1, {'@': 380}), 110: (1, {'@': 380}), 111: (1, {'@': 380}), 112: (1, {'@': 380}), 113: (1, {'@': 380}), 114: (1, {'@': 380}), 3: (1, {'@': 380}), 115: (1, {'@': 380}), 116: (1, {'@': 380}), 117: (1, {'@': 380}), 118: (1, {'@': 380}), 119: (1, {'@': 380}), 120: (1, {'@': 380}), 121: (1, {'@': 380}), 123: (1, {'@': 380}), 124: (1, {'@': 380}), 5: (1, {'@': 380}), 10: (1, {'@': 380}), 43: (1, {'@': 380}), 73: (1, {'@': 380}), 6: (1, {'@': 380}), 122: (1, {'@': 380}), 32: (1, {'@': 380})}, 434: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 131: (0, 666), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 435: {5: (0, 201), 10: (0, 316)}, 436: {11: (1, {'@': 197}), 12: (1, {'@': 197}), 13: (1, {'@': 197})}, 437: {11: (1, {'@': 171}), 12: (1, {'@': 171}), 13: (1, {'@': 171}), 5: (1, {'@': 171}), 6: (1, {'@': 171})}, 438: {11: (1, {'@': 134}), 13: (1, {'@': 134}), 12: (1, {'@': 134})}, 439: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 131: (0, 372), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 440: {11: (1, {'@': 202}), 12: (1, {'@': 202}), 13: (1, {'@': 202})}, 441: {5: (1, {'@': 341}), 10: (1, {'@': 341})}, 442: {11: (1, {'@': 132}), 13: (1, {'@': 132}), 12: (1, {'@': 132})}, 443: {14: (1, {'@': 260}), 15: (1, {'@': 260}), 16: (1, {'@': 260}), 17: (1, {'@': 260}), 18: (1, {'@': 260}), 19: (1, {'@': 260}), 20: (1, {'@': 260}), 21: (1, {'@': 260}), 22: (1, {'@': 260}), 23: (1, {'@': 260}), 24: (1, {'@': 260}), 25: (1, {'@': 260}), 1: (1, {'@': 260}), 26: (1, {'@': 260}), 27: (1, {'@': 260}), 28: (1, {'@': 260}), 46: (1, {'@': 260}), 29: (1, {'@': 260}), 30: (1, {'@': 260}), 3: (1, {'@': 260}), 31: (1, {'@': 260}), 9: (1, {'@': 260}), 32: (1, {'@': 260}), 34: (1, {'@': 260}), 33: (1, {'@': 260}), 5: (1, {'@': 260}), 35: (1, {'@': 260}), 36: (1, {'@': 260}), 37: (1, {'@': 260}), 47: (1, {'@': 260}), 38: (1, {'@': 260}), 48: (1, {'@': 260}), 39: (1, {'@': 260}), 49: (1, {'@': 260}), 40: (1, {'@': 260}), 41: (1, {'@': 260}), 42: (1, {'@': 260})}, 444: {11: (1, {'@': 139}), 13: (1, {'@': 139}), 12: (1, {'@': 139})}, 445: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 135: (0, 483), 33: (0, 356), 129: (0, 497), 136: (0, 403), 23: (0, 219), 133: (0, 664), 29: (0, 474), 130: (0, 169), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 27: (0, 462), 28: (0, 42), 21: (0, 305), 49: (0, 373), 46: (0, 445), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 446: {11: (1, {'@': 194}), 12: (1, {'@': 194}), 13: (1, {'@': 194})}, 447: {5: (1, {'@': 176}), 6: (1, {'@': 176})}, 448: {11: (1, {'@': 110}), 13: (1, {'@': 110}), 12: (1, {'@': 110})}, 449: {4: (0, 182)}, 450: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 131: (0, 726), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 451: {206: (0, 447), 205: (0, 410)}, 452: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 131: (0, 168), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 453: {11: (1, {'@': 141}), 13: (1, {'@': 141}), 12: (1, {'@': 141})}, 454: {5: (1, {'@': 236}), 11: (1, {'@': 236}), 12: (1, {'@': 236}), 43: (1, {'@': 236}), 13: (1, {'@': 236})}, 455: {11: (1, {'@': 136}), 13: (1, {'@': 136}), 12: (1, {'@': 136})}, 456: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 23: (0, 219), 29: (0, 474), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 24: (0, 408), 40: (0, 503), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 53: (0, 585), 20: (0, 247)}, 457: {186: (0, 274), 73: (0, 679), 72: (1, {'@': 213}), 74: (1, {'@': 213}), 75: (1, {'@': 213}), 76: (1, {'@': 213}), 77: (1, {'@': 213}), 78: (1, {'@': 213}), 79: (1, {'@': 213}), 80: (1, {'@': 213}), 81: (1, {'@': 213}), 82: (1, {'@': 213}), 83: (1, {'@': 213}), 84: (1, {'@': 213}), 85: (1, {'@': 213}), 11: (1, {'@': 213}), 86: (1, {'@': 213}), 87: (1, {'@': 213}), 88: (1, {'@': 213}), 89: (1, {'@': 213}), 91: (1, {'@': 213}), 92: (1, {'@': 213}), 93: (1, {'@': 213}), 94: (1, {'@': 213}), 95: (1, {'@': 213}), 96: (1, {'@': 213}), 97: (1, {'@': 213}), 98: (1, {'@': 213}), 99: (1, {'@': 213}), 100: (1, {'@': 213}), 101: (1, {'@': 213}), 102: (1, {'@': 213}), 13: (1, {'@': 213}), 103: (1, {'@': 213}), 104: (1, {'@': 213}), 105: (1, {'@': 213}), 106: (1, {'@': 213}), 107: (1, {'@': 213}), 108: (1, {'@': 213}), 109: (1, {'@': 213}), 1: (1, {'@': 213}), 110: (1, {'@': 213}), 111: (1, {'@': 213}), 112: (1, {'@': 213}), 113: (1, {'@': 213}), 114: (1, {'@': 213}), 3: (1, {'@': 213}), 115: (1, {'@': 213}), 116: (1, {'@': 213}), 117: (1, {'@': 213}), 118: (1, {'@': 213}), 119: (1, {'@': 213}), 120: (1, {'@': 213}), 121: (1, {'@': 213}), 123: (1, {'@': 213}), 124: (1, {'@': 213})}, 458: {11: (1, {'@': 198}), 12: (1, {'@': 198}), 13: (1, {'@': 198})}, 459: {14: (1, {'@': 258}), 15: (1, {'@': 258}), 16: (1, {'@': 258}), 17: (1, {'@': 258}), 18: (1, {'@': 258}), 19: (1, {'@': 258}), 20: (1, {'@': 258}), 21: (1, {'@': 258}), 22: (1, {'@': 258}), 23: (1, {'@': 258}), 24: (1, {'@': 258}), 25: (1, {'@': 258}), 1: (1, {'@': 258}), 26: (1, {'@': 258}), 27: (1, {'@': 258}), 28: (1, {'@': 258}), 46: (1, {'@': 258}), 29: (1, {'@': 258}), 30: (1, {'@': 258}), 3: (1, {'@': 258}), 31: (1, {'@': 258}), 9: (1, {'@': 258}), 32: (1, {'@': 258}), 34: (1, {'@': 258}), 33: (1, {'@': 258}), 35: (1, {'@': 258}), 36: (1, {'@': 258}), 37: (1, {'@': 258}), 47: (1, {'@': 258}), 38: (1, {'@': 258}), 48: (1, {'@': 258}), 39: (1, {'@': 258}), 49: (1, {'@': 258}), 40: (1, {'@': 258}), 41: (1, {'@': 258}), 42: (1, {'@': 258})}, 460: {11: (1, {'@': 94}), 13: (1, {'@': 94}), 12: (1, {'@': 94})}, 461: {72: (1, {'@': 310}), 56: (1, {'@': 310}), 78: (1, {'@': 310}), 80: (1, {'@': 310}), 81: (1, {'@': 310}), 68: (1, {'@': 310}), 84: (1, {'@': 310}), 69: (1, {'@': 310}), 86: (1, {'@': 310}), 71: (1, {'@': 310}), 87: (1, {'@': 310}), 89: (1, {'@': 310}), 12: (1, {'@': 310}), 93: (1, {'@': 310}), 60: (1, {'@': 310}), 63: (1, {'@': 310}), 62: (1, {'@': 310}), 97: (1, {'@': 310}), 99: (1, {'@': 310}), 100: (1, {'@': 310}), 102: (1, {'@': 310}), 43: (1, {'@': 310}), 103: (1, {'@': 310}), 105: (1, {'@': 310}), 107: (1, {'@': 310}), 108: (1, {'@': 310}), 112: (1, {'@': 310}), 70: (1, {'@': 310}), 67: (1, {'@': 310}), 64: (1, {'@': 310}), 3: (1, {'@': 310}), 9: (1, {'@': 310}), 115: (1, {'@': 310}), 5: (1, {'@': 310}), 116: (1, {'@': 310}), 125: (1, {'@': 310}), 59: (1, {'@': 310}), 120: (1, {'@': 310}), 65: (1, {'@': 310}), 6: (1, {'@': 310}), 54: (1, {'@': 310}), 123: (1, {'@': 310}), 73: (1, {'@': 310}), 74: (1, {'@': 310}), 75: (1, {'@': 310}), 76: (1, {'@': 310}), 77: (1, {'@': 310}), 79: (1, {'@': 310}), 61: (1, {'@': 310}), 10: (1, {'@': 310}), 82: (1, {'@': 310}), 66: (1, {'@': 310}), 83: (1, {'@': 310}), 85: (1, {'@': 310}), 11: (1, {'@': 310}), 88: (1, {'@': 310}), 90: (1, {'@': 310}), 91: (1, {'@': 310}), 92: (1, {'@': 310}), 94: (1, {'@': 310}), 47: (1, {'@': 310}), 95: (1, {'@': 310}), 96: (1, {'@': 310}), 4: (1, {'@': 310}), 55: (1, {'@': 310}), 98: (1, {'@': 310}), 101: (1, {'@': 310}), 13: (1, {'@': 310}), 104: (1, {'@': 310}), 106: (1, {'@': 310}), 58: (1, {'@': 310}), 109: (1, {'@': 310}), 1: (1, {'@': 310}), 110: (1, {'@': 310}), 111: (1, {'@': 310}), 113: (1, {'@': 310}), 114: (1, {'@': 310}), 32: (1, {'@': 310}), 57: (1, {'@': 310}), 117: (1, {'@': 310}), 118: (1, {'@': 310}), 119: (1, {'@': 310}), 121: (1, {'@': 310}), 122: (1, {'@': 310}), 124: (1, {'@': 310})}, 462: {9: (0, 640)}, 463: {11: (1, {'@': 195}), 12: (1, {'@': 195}), 13: (1, {'@': 195})}, 464: {5: (1, {'@': 318}), 10: (1, {'@': 318}), 54: (1, {'@': 318}), 11: (1, {'@': 318}), 12: (1, {'@': 318}), 13: (1, {'@': 318})}, 465: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 131: (0, 455), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 466: {14: (1, {'@': 259}), 15: (1, {'@': 259}), 16: (1, {'@': 259}), 17: (1, {'@': 259}), 18: (1, {'@': 259}), 19: (1, {'@': 259}), 20: (1, {'@': 259}), 21: (1, {'@': 259}), 22: (1, {'@': 259}), 23: (1, {'@': 259}), 24: (1, {'@': 259}), 25: (1, {'@': 259}), 1: (1, {'@': 259}), 26: (1, {'@': 259}), 27: (1, {'@': 259}), 28: (1, {'@': 259}), 46: (1, {'@': 259}), 29: (1, {'@': 259}), 30: (1, {'@': 259}), 3: (1, {'@': 259}), 31: (1, {'@': 259}), 9: (1, {'@': 259}), 32: (1, {'@': 259}), 34: (1, {'@': 259}), 33: (1, {'@': 259}), 5: (1, {'@': 259}), 35: (1, {'@': 259}), 36: (1, {'@': 259}), 37: (1, {'@': 259}), 47: (1, {'@': 259}), 38: (1, {'@': 259}), 48: (1, {'@': 259}), 39: (1, {'@': 259}), 49: (1, {'@': 259}), 40: (1, {'@': 259}), 41: (1, {'@': 259}), 42: (1, {'@': 259})}, 467: {11: (1, {'@': 71}), 13: (1, {'@': 71}), 12: (1, {'@': 71})}, 468: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 131: (0, 217), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 469: {11: (1, {'@': 79}), 13: (1, {'@': 79}), 12: (1, {'@': 79})}, 470: {11: (1, {'@': 19}), 13: (1, {'@': 19}), 12: (1, {'@': 19})}, 471: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 199: (0, 620), 191: (0, 223), 149: (0, 779), 148: (0, 769), 13: (0, 707), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 11: (0, 632), 83: (0, 720), 175: (0, 593), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 213: (0, 241), 208: (0, 244), 86: (0, 656), 151: (0, 717), 72: (0, 555), 192: (0, 253), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 74: (0, 597), 113: (0, 712), 153: (0, 715), 154: (0, 780), 107: (0, 591), 193: (0, 624), 114: (0, 759), 116: (0, 774), 155: (0, 651), 200: (0, 750), 75: (0, 654), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 201: (0, 618), 117: (0, 626), 202: (0, 637), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 194: (0, 721), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 150: (0, 741), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 472: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 165: (0, 586), 156: (0, 747), 93: (0, 630), 164: (0, 531), 98: (0, 544), 11: (0, 602), 118: (0, 548), 185: (0, 636), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 175: (0, 619), 159: (0, 595), 150: (0, 705), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 12: (1, {'@': 188}), 13: (1, {'@': 188})}, 473: {72: (1, {'@': 221}), 74: (1, {'@': 221}), 75: (1, {'@': 221}), 76: (1, {'@': 221}), 77: (1, {'@': 221}), 78: (1, {'@': 221}), 79: (1, {'@': 221}), 80: (1, {'@': 221}), 81: (1, {'@': 221}), 82: (1, {'@': 221}), 83: (1, {'@': 221}), 84: (1, {'@': 221}), 85: (1, {'@': 221}), 11: (1, {'@': 221}), 86: (1, {'@': 221}), 87: (1, {'@': 221}), 88: (1, {'@': 221}), 89: (1, {'@': 221}), 91: (1, {'@': 221}), 92: (1, {'@': 221}), 93: (1, {'@': 221}), 94: (1, {'@': 221}), 95: (1, {'@': 221}), 96: (1, {'@': 221}), 97: (1, {'@': 221}), 98: (1, {'@': 221}), 99: (1, {'@': 221}), 100: (1, {'@': 221}), 101: (1, {'@': 221}), 102: (1, {'@': 221}), 13: (1, {'@': 221}), 103: (1, {'@': 221}), 104: (1, {'@': 221}), 105: (1, {'@': 221}), 106: (1, {'@': 221}), 107: (1, {'@': 221}), 108: (1, {'@': 221}), 109: (1, {'@': 221}), 1: (1, {'@': 221}), 110: (1, {'@': 221}), 111: (1, {'@': 221}), 112: (1, {'@': 221}), 113: (1, {'@': 221}), 114: (1, {'@': 221}), 3: (1, {'@': 221}), 115: (1, {'@': 221}), 116: (1, {'@': 221}), 117: (1, {'@': 221}), 118: (1, {'@': 221}), 119: (1, {'@': 221}), 120: (1, {'@': 221}), 121: (1, {'@': 221}), 123: (1, {'@': 221}), 124: (1, {'@': 221})}, 474: {9: (0, 753), 72: (1, {'@': 278}), 56: (1, {'@': 278}), 78: (1, {'@': 278}), 80: (1, {'@': 278}), 81: (1, {'@': 278}), 68: (1, {'@': 278}), 84: (1, {'@': 278}), 69: (1, {'@': 278}), 86: (1, {'@': 278}), 71: (1, {'@': 278}), 87: (1, {'@': 278}), 89: (1, {'@': 278}), 12: (1, {'@': 278}), 93: (1, {'@': 278}), 60: (1, {'@': 278}), 63: (1, {'@': 278}), 62: (1, {'@': 278}), 97: (1, {'@': 278}), 99: (1, {'@': 278}), 100: (1, {'@': 278}), 102: (1, {'@': 278}), 43: (1, {'@': 278}), 103: (1, {'@': 278}), 105: (1, {'@': 278}), 107: (1, {'@': 278}), 108: (1, {'@': 278}), 112: (1, {'@': 278}), 70: (1, {'@': 278}), 64: (1, {'@': 278}), 67: (1, {'@': 278}), 3: (1, {'@': 278}), 115: (1, {'@': 278}), 116: (1, {'@': 278}), 5: (1, {'@': 278}), 125: (1, {'@': 278}), 59: (1, {'@': 278}), 120: (1, {'@': 278}), 65: (1, {'@': 278}), 6: (1, {'@': 278}), 54: (1, {'@': 278}), 123: (1, {'@': 278}), 73: (1, {'@': 278}), 74: (1, {'@': 278}), 75: (1, {'@': 278}), 76: (1, {'@': 278}), 77: (1, {'@': 278}), 79: (1, {'@': 278}), 61: (1, {'@': 278}), 82: (1, {'@': 278}), 10: (1, {'@': 278}), 66: (1, {'@': 278}), 83: (1, {'@': 278}), 85: (1, {'@': 278}), 11: (1, {'@': 278}), 88: (1, {'@': 278}), 90: (1, {'@': 278}), 91: (1, {'@': 278}), 92: (1, {'@': 278}), 94: (1, {'@': 278}), 47: (1, {'@': 278}), 95: (1, {'@': 278}), 96: (1, {'@': 278}), 4: (1, {'@': 278}), 55: (1, {'@': 278}), 98: (1, {'@': 278}), 101: (1, {'@': 278}), 13: (1, {'@': 278}), 104: (1, {'@': 278}), 106: (1, {'@': 278}), 58: (1, {'@': 278}), 109: (1, {'@': 278}), 1: (1, {'@': 278}), 110: (1, {'@': 278}), 111: (1, {'@': 278}), 113: (1, {'@': 278}), 114: (1, {'@': 278}), 32: (1, {'@': 278}), 57: (1, {'@': 278}), 117: (1, {'@': 278}), 118: (1, {'@': 278}), 119: (1, {'@': 278}), 121: (1, {'@': 278}), 122: (1, {'@': 278}), 124: (1, {'@': 278})}, 475: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 131: (0, 2), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 476: {73: (0, 679), 186: (0, 333), 72: (1, {'@': 213}), 74: (1, {'@': 213}), 75: (1, {'@': 213}), 76: (1, {'@': 213}), 77: (1, {'@': 213}), 78: (1, {'@': 213}), 79: (1, {'@': 213}), 80: (1, {'@': 213}), 81: (1, {'@': 213}), 82: (1, {'@': 213}), 83: (1, {'@': 213}), 84: (1, {'@': 213}), 85: (1, {'@': 213}), 11: (1, {'@': 213}), 86: (1, {'@': 213}), 87: (1, {'@': 213}), 88: (1, {'@': 213}), 89: (1, {'@': 213}), 91: (1, {'@': 213}), 92: (1, {'@': 213}), 93: (1, {'@': 213}), 94: (1, {'@': 213}), 95: (1, {'@': 213}), 96: (1, {'@': 213}), 97: (1, {'@': 213}), 98: (1, {'@': 213}), 99: (1, {'@': 213}), 100: (1, {'@': 213}), 101: (1, {'@': 213}), 102: (1, {'@': 213}), 13: (1, {'@': 213}), 103: (1, {'@': 213}), 104: (1, {'@': 213}), 105: (1, {'@': 213}), 106: (1, {'@': 213}), 107: (1, {'@': 213}), 108: (1, {'@': 213}), 109: (1, {'@': 213}), 1: (1, {'@': 213}), 110: (1, {'@': 213}), 111: (1, {'@': 213}), 112: (1, {'@': 213}), 113: (1, {'@': 213}), 114: (1, {'@': 213}), 3: (1, {'@': 213}), 115: (1, {'@': 213}), 116: (1, {'@': 213}), 117: (1, {'@': 213}), 118: (1, {'@': 213}), 119: (1, {'@': 213}), 120: (1, {'@': 213}), 121: (1, {'@': 213}), 123: (1, {'@': 213}), 124: (1, {'@': 213})}, 477: {11: (1, {'@': 138}), 13: (1, {'@': 138}), 12: (1, {'@': 138})}, 478: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 473), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 479: {11: (1, {'@': 140}), 13: (1, {'@': 140}), 12: (1, {'@': 140})}, 480: {11: (0, 300), 13: (1, {'@': 144})}, 481: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 131: (0, 711), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 482: {11: (1, {'@': 137}), 13: (1, {'@': 137}), 12: (1, {'@': 137})}, 483: {55: (1, {'@': 411}), 56: (1, {'@': 411}), 57: (1, {'@': 411}), 58: (1, {'@': 411}), 59: (1, {'@': 411}), 60: (1, {'@': 411}), 61: (1, {'@': 411}), 47: (1, {'@': 411}), 62: (1, {'@': 411}), 63: (1, {'@': 411}), 64: (1, {'@': 411}), 65: (1, {'@': 411}), 66: (1, {'@': 411}), 67: (1, {'@': 411}), 68: (1, {'@': 411}), 54: (1, {'@': 411}), 69: (1, {'@': 411}), 70: (1, {'@': 411}), 4: (1, {'@': 411}), 71: (1, {'@': 411}), 12: (1, {'@': 411}), 13: (1, {'@': 411}), 11: (1, {'@': 411}), 72: (1, {'@': 411}), 73: (1, {'@': 411}), 74: (1, {'@': 411}), 75: (1, {'@': 411}), 76: (1, {'@': 411}), 77: (1, {'@': 411}), 78: (1, {'@': 411}), 79: (1, {'@': 411}), 80: (1, {'@': 411}), 81: (1, {'@': 411}), 10: (1, {'@': 411}), 82: (1, {'@': 411}), 83: (1, {'@': 411}), 84: (1, {'@': 411}), 85: (1, {'@': 411}), 86: (1, {'@': 411}), 87: (1, {'@': 411}), 88: (1, {'@': 411}), 89: (1, {'@': 411}), 90: (1, {'@': 411}), 91: (1, {'@': 411}), 92: (1, {'@': 411}), 93: (1, {'@': 411}), 94: (1, {'@': 411}), 95: (1, {'@': 411}), 96: (1, {'@': 411}), 97: (1, {'@': 411}), 98: (1, {'@': 411}), 99: (1, {'@': 411}), 100: (1, {'@': 411}), 101: (1, {'@': 411}), 102: (1, {'@': 411}), 43: (1, {'@': 411}), 103: (1, {'@': 411}), 104: (1, {'@': 411}), 105: (1, {'@': 411}), 106: (1, {'@': 411}), 107: (1, {'@': 411}), 108: (1, {'@': 411}), 109: (1, {'@': 411}), 1: (1, {'@': 411}), 110: (1, {'@': 411}), 111: (1, {'@': 411}), 112: (1, {'@': 411}), 113: (1, {'@': 411}), 114: (1, {'@': 411}), 3: (1, {'@': 411}), 32: (1, {'@': 411}), 115: (1, {'@': 411}), 5: (1, {'@': 411}), 116: (1, {'@': 411}), 117: (1, {'@': 411}), 118: (1, {'@': 411}), 119: (1, {'@': 411}), 120: (1, {'@': 411}), 121: (1, {'@': 411}), 6: (1, {'@': 411}), 122: (1, {'@': 411}), 123: (1, {'@': 411}), 124: (1, {'@': 411})}, 484: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 150: (0, 81), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 165: (0, 586), 156: (0, 747), 11: (0, 73), 93: (0, 630), 164: (0, 531), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 175: (0, 619), 159: (0, 595), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 185: (0, 197), 162: (0, 470), 121: (0, 434), 76: (0, 459), 13: (1, {'@': 146})}, 485: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 191: (0, 484), 81: (0, 786), 11: (0, 632), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 165: (0, 348), 92: (0, 604), 164: (0, 399), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 116: (0, 774), 155: (0, 651), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 192: (0, 492), 193: (0, 480), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 194: (0, 361), 97: (0, 700), 106: (0, 762), 108: (0, 518), 119: (0, 674), 150: (0, 741), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 13: (1, {'@': 148})}, 486: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 139: (0, 560), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 487: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 131: (0, 761), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 488: {5: (0, 451), 6: (0, 437)}, 489: {11: (1, {'@': 47}), 13: (1, {'@': 47}), 12: (1, {'@': 47})}, 490: {4: (0, 364)}, 491: {6: (0, 161), 5: (0, 475)}, 492: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 165: (0, 586), 156: (0, 747), 93: (0, 630), 164: (0, 531), 98: (0, 544), 11: (0, 602), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 185: (0, 199), 175: (0, 619), 94: (0, 584), 82: (0, 568), 159: (0, 595), 150: (0, 705), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 13: (1, {'@': 147})}, 493: {9: (0, 612)}, 494: {14: (1, {'@': 372}), 15: (1, {'@': 372}), 16: (1, {'@': 372}), 17: (1, {'@': 372}), 18: (1, {'@': 372}), 19: (1, {'@': 372}), 20: (1, {'@': 372}), 21: (1, {'@': 372}), 22: (1, {'@': 372}), 23: (1, {'@': 372}), 24: (1, {'@': 372}), 25: (1, {'@': 372}), 1: (1, {'@': 372}), 26: (1, {'@': 372}), 27: (1, {'@': 372}), 28: (1, {'@': 372}), 29: (1, {'@': 372}), 30: (1, {'@': 372}), 3: (1, {'@': 372}), 31: (1, {'@': 372}), 9: (1, {'@': 372}), 32: (1, {'@': 372}), 33: (1, {'@': 372}), 34: (1, {'@': 372}), 35: (1, {'@': 372}), 36: (1, {'@': 372}), 37: (1, {'@': 372}), 38: (1, {'@': 372}), 39: (1, {'@': 372}), 40: (1, {'@': 372}), 41: (1, {'@': 372}), 42: (1, {'@': 372})}, 495: {5: (1, {'@': 175}), 6: (1, {'@': 175})}, 496: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 131: (0, 271), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 497: {61: (0, 212), 70: (0, 362), 55: (1, {'@': 408}), 56: (1, {'@': 408}), 57: (1, {'@': 408}), 58: (1, {'@': 408}), 59: (1, {'@': 408}), 60: (1, {'@': 408}), 47: (1, {'@': 408}), 62: (1, {'@': 408}), 63: (1, {'@': 408}), 65: (1, {'@': 408}), 66: (1, {'@': 408}), 68: (1, {'@': 408}), 54: (1, {'@': 408}), 4: (1, {'@': 408}), 69: (1, {'@': 408}), 67: (1, {'@': 408}), 64: (1, {'@': 408}), 71: (1, {'@': 408}), 12: (1, {'@': 408}), 13: (1, {'@': 408}), 11: (1, {'@': 408}), 72: (1, {'@': 408}), 73: (1, {'@': 408}), 74: (1, {'@': 408}), 75: (1, {'@': 408}), 76: (1, {'@': 408}), 77: (1, {'@': 408}), 78: (1, {'@': 408}), 79: (1, {'@': 408}), 80: (1, {'@': 408}), 81: (1, {'@': 408}), 10: (1, {'@': 408}), 82: (1, {'@': 408}), 83: (1, {'@': 408}), 84: (1, {'@': 408}), 85: (1, {'@': 408}), 86: (1, {'@': 408}), 87: (1, {'@': 408}), 88: (1, {'@': 408}), 89: (1, {'@': 408}), 90: (1, {'@': 408}), 91: (1, {'@': 408}), 92: (1, {'@': 408}), 93: (1, {'@': 408}), 94: (1, {'@': 408}), 95: (1, {'@': 408}), 96: (1, {'@': 408}), 97: (1, {'@': 408}), 98: (1, {'@': 408}), 99: (1, {'@': 408}), 100: (1, {'@': 408}), 101: (1, {'@': 408}), 102: (1, {'@': 408}), 43: (1, {'@': 408}), 103: (1, {'@': 408}), 104: (1, {'@': 408}), 105: (1, {'@': 408}), 106: (1, {'@': 408}), 107: (1, {'@': 408}), 108: (1, {'@': 408}), 109: (1, {'@': 408}), 1: (1, {'@': 408}), 110: (1, {'@': 408}), 111: (1, {'@': 408}), 112: (1, {'@': 408}), 113: (1, {'@': 408}), 114: (1, {'@': 408}), 3: (1, {'@': 408}), 32: (1, {'@': 408}), 115: (1, {'@': 408}), 5: (1, {'@': 408}), 116: (1, {'@': 408}), 117: (1, {'@': 408}), 118: (1, {'@': 408}), 119: (1, {'@': 408}), 120: (1, {'@': 408}), 121: (1, {'@': 408}), 6: (1, {'@': 408}), 122: (1, {'@': 408}), 123: (1, {'@': 408}), 124: (1, {'@': 408})}, 498: {5: (1, {'@': 173}), 6: (1, {'@': 173})}, 499: {72: (1, {'@': 4}), 74: (1, {'@': 4}), 75: (1, {'@': 4}), 76: (1, {'@': 4}), 77: (1, {'@': 4}), 78: (1, {'@': 4}), 166: (1, {'@': 4}), 79: (1, {'@': 4}), 80: (1, {'@': 4}), 81: (1, {'@': 4}), 82: (1, {'@': 4}), 44: (1, {'@': 4}), 83: (1, {'@': 4}), 84: (1, {'@': 4}), 85: (1, {'@': 4}), 11: (1, {'@': 4}), 86: (1, {'@': 4}), 163: (1, {'@': 4}), 45: (1, {'@': 4}), 87: (1, {'@': 4}), 88: (1, {'@': 4}), 89: (1, {'@': 4}), 12: (1, {'@': 4}), 91: (1, {'@': 4}), 92: (1, {'@': 4}), 93: (1, {'@': 4}), 94: (1, {'@': 4}), 95: (1, {'@': 4}), 96: (1, {'@': 4}), 164: (1, {'@': 4}), 165: (1, {'@': 4}), 97: (1, {'@': 4}), 167: (1, {'@': 4}), 98: (1, {'@': 4}), 99: (1, {'@': 4}), 100: (1, {'@': 4}), 101: (1, {'@': 4}), 102: (1, {'@': 4}), 13: (1, {'@': 4}), 103: (1, {'@': 4}), 105: (1, {'@': 4}), 104: (1, {'@': 4}), 106: (1, {'@': 4}), 107: (1, {'@': 4}), 108: (1, {'@': 4}), 109: (1, {'@': 4}), 1: (1, {'@': 4}), 110: (1, {'@': 4}), 111: (1, {'@': 4}), 112: (1, {'@': 4}), 113: (1, {'@': 4}), 114: (1, {'@': 4}), 3: (1, {'@': 4}), 115: (1, {'@': 4}), 116: (1, {'@': 4}), 117: (1, {'@': 4}), 118: (1, {'@': 4}), 119: (1, {'@': 4}), 120: (1, {'@': 4}), 121: (1, {'@': 4}), 122: (1, {'@': 4}), 123: (1, {'@': 4}), 124: (1, {'@': 4}), 195: (1, {'@': 4}), 168: (1, {'@': 4})}, 500: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 131: (0, 311), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 501: {9: (0, 390), 172: (0, 395), 4: (0, 385)}, 502: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 9: (0, 690), 51: (0, 456), 52: (0, 354), 33: (0, 356), 23: (0, 219), 29: (0, 474), 14: (0, 493), 15: (0, 205), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 24: (0, 408), 40: (0, 503), 26: (0, 405), 38: (0, 50), 53: (0, 603), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 503: {14: (1, {'@': 374}), 15: (1, {'@': 374}), 16: (1, {'@': 374}), 17: (1, {'@': 374}), 18: (1, {'@': 374}), 19: (1, {'@': 374}), 20: (1, {'@': 374}), 21: (1, {'@': 374}), 22: (1, {'@': 374}), 23: (1, {'@': 374}), 24: (1, {'@': 374}), 25: (1, {'@': 374}), 1: (1, {'@': 374}), 26: (1, {'@': 374}), 27: (1, {'@': 374}), 28: (1, {'@': 374}), 29: (1, {'@': 374}), 30: (1, {'@': 374}), 3: (1, {'@': 374}), 31: (1, {'@': 374}), 9: (1, {'@': 374}), 32: (1, {'@': 374}), 33: (1, {'@': 374}), 34: (1, {'@': 374}), 35: (1, {'@': 374}), 36: (1, {'@': 374}), 37: (1, {'@': 374}), 38: (1, {'@': 374}), 39: (1, {'@': 374}), 40: (1, {'@': 374}), 41: (1, {'@': 374}), 42: (1, {'@': 374})}, 504: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 53: (0, 665), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 23: (0, 219), 29: (0, 474), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 24: (0, 408), 40: (0, 503), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 505: {57: (1, {'@': 386}), 56: (1, {'@': 386}), 54: (1, {'@': 386}), 63: (1, {'@': 386}), 11: (1, {'@': 386}), 12: (1, {'@': 386}), 13: (1, {'@': 386}), 72: (1, {'@': 386}), 74: (1, {'@': 386}), 98: (1, {'@': 386}), 75: (1, {'@': 386}), 99: (1, {'@': 386}), 100: (1, {'@': 386}), 76: (1, {'@': 386}), 101: (1, {'@': 386}), 102: (1, {'@': 386}), 103: (1, {'@': 386}), 77: (1, {'@': 386}), 78: (1, {'@': 386}), 104: (1, {'@': 386}), 105: (1, {'@': 386}), 106: (1, {'@': 386}), 107: (1, {'@': 386}), 79: (1, {'@': 386}), 108: (1, {'@': 386}), 80: (1, {'@': 386}), 81: (1, {'@': 386}), 82: (1, {'@': 386}), 109: (1, {'@': 386}), 83: (1, {'@': 386}), 1: (1, {'@': 386}), 110: (1, {'@': 386}), 111: (1, {'@': 386}), 84: (1, {'@': 386}), 112: (1, {'@': 386}), 85: (1, {'@': 386}), 86: (1, {'@': 386}), 113: (1, {'@': 386}), 114: (1, {'@': 386}), 87: (1, {'@': 386}), 3: (1, {'@': 386}), 88: (1, {'@': 386}), 89: (1, {'@': 386}), 115: (1, {'@': 386}), 90: (1, {'@': 386}), 116: (1, {'@': 386}), 117: (1, {'@': 386}), 118: (1, {'@': 386}), 91: (1, {'@': 386}), 119: (1, {'@': 386}), 92: (1, {'@': 386}), 93: (1, {'@': 386}), 94: (1, {'@': 386}), 120: (1, {'@': 386}), 95: (1, {'@': 386}), 96: (1, {'@': 386}), 121: (1, {'@': 386}), 123: (1, {'@': 386}), 97: (1, {'@': 386}), 124: (1, {'@': 386}), 5: (1, {'@': 386}), 10: (1, {'@': 386}), 43: (1, {'@': 386}), 73: (1, {'@': 386}), 6: (1, {'@': 386}), 32: (1, {'@': 386}), 122: (1, {'@': 386})}, 506: {11: (1, {'@': 52}), 13: (1, {'@': 52}), 12: (1, {'@': 52})}, 507: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 420), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 508: {5: (1, {'@': 251}), 11: (1, {'@': 251}), 12: (1, {'@': 251}), 43: (1, {'@': 251}), 13: (1, {'@': 251})}, 509: {8: (0, 218), 7: (0, 377), 12: (1, {'@': 352}), 13: (1, {'@': 352}), 4: (1, {'@': 352}), 11: (1, {'@': 352}), 169: (1, {'@': 352}), 170: (1, {'@': 352})}, 510: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 131: (0, 417), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 511: {214: (0, 173), 215: (0, 266), 216: (0, 250), 3: (1, {'@': 329})}, 512: {11: (1, {'@': 56}), 13: (1, {'@': 56}), 12: (1, {'@': 56})}, 513: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 135: (0, 483), 33: (0, 356), 129: (0, 497), 136: (0, 403), 23: (0, 219), 29: (0, 474), 130: (0, 169), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 133: (0, 196), 41: (0, 273), 27: (0, 462), 28: (0, 42), 21: (0, 305), 49: (0, 373), 46: (0, 445), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 514: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 165: (0, 586), 156: (0, 747), 93: (0, 630), 164: (0, 531), 98: (0, 544), 11: (0, 602), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 175: (0, 619), 159: (0, 595), 150: (0, 705), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 160: (0, 746), 185: (0, 409), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 12: (1, {'@': 205}), 13: (1, {'@': 205})}, 515: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 135: (0, 483), 33: (0, 356), 129: (0, 497), 136: (0, 403), 23: (0, 219), 29: (0, 474), 130: (0, 251), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 516: {11: (1, {'@': 93}), 13: (1, {'@': 93}), 12: (1, {'@': 93})}, 517: {72: (1, {'@': 225}), 74: (1, {'@': 225}), 98: (1, {'@': 225}), 75: (1, {'@': 225}), 99: (1, {'@': 225}), 100: (1, {'@': 225}), 101: (1, {'@': 225}), 102: (1, {'@': 225}), 76: (1, {'@': 225}), 13: (1, {'@': 225}), 103: (1, {'@': 225}), 104: (1, {'@': 225}), 105: (1, {'@': 225}), 78: (1, {'@': 225}), 77: (1, {'@': 225}), 106: (1, {'@': 225}), 107: (1, {'@': 225}), 79: (1, {'@': 225}), 108: (1, {'@': 225}), 80: (1, {'@': 225}), 81: (1, {'@': 225}), 82: (1, {'@': 225}), 109: (1, {'@': 225}), 83: (1, {'@': 225}), 1: (1, {'@': 225}), 110: (1, {'@': 225}), 111: (1, {'@': 225}), 84: (1, {'@': 225}), 112: (1, {'@': 225}), 85: (1, {'@': 225}), 11: (1, {'@': 225}), 86: (1, {'@': 225}), 113: (1, {'@': 225}), 114: (1, {'@': 225}), 87: (1, {'@': 225}), 3: (1, {'@': 225}), 88: (1, {'@': 225}), 89: (1, {'@': 225}), 115: (1, {'@': 225}), 116: (1, {'@': 225}), 117: (1, {'@': 225}), 118: (1, {'@': 225}), 91: (1, {'@': 225}), 119: (1, {'@': 225}), 92: (1, {'@': 225}), 93: (1, {'@': 225}), 94: (1, {'@': 225}), 120: (1, {'@': 225}), 95: (1, {'@': 225}), 96: (1, {'@': 225}), 121: (1, {'@': 225}), 122: (1, {'@': 225}), 123: (1, {'@': 225}), 97: (1, {'@': 225}), 124: (1, {'@': 225})}, 518: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 131: (0, 697), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 519: {5: (0, 691)}, 520: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 131: (0, 140), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486), 11: (1, {'@': 123}), 13: (1, {'@': 123}), 12: (1, {'@': 123})}, 521: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 171: (0, 398), 9: (0, 183), 172: (0, 533), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 217: (0, 510), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 23: (0, 219), 29: (0, 474), 14: (0, 493), 15: (0, 205), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 24: (0, 408), 40: (0, 503), 53: (0, 643), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247), 55: (1, {'@': 271}), 56: (1, {'@': 271}), 57: (1, {'@': 271}), 5: (1, {'@': 271}), 125: (1, {'@': 271}), 58: (1, {'@': 271}), 59: (1, {'@': 271}), 60: (1, {'@': 271}), 61: (1, {'@': 271}), 47: (1, {'@': 271}), 63: (1, {'@': 271}), 62: (1, {'@': 271}), 64: (1, {'@': 271}), 10: (1, {'@': 271}), 65: (1, {'@': 271}), 66: (1, {'@': 271}), 70: (1, {'@': 271}), 68: (1, {'@': 271}), 54: (1, {'@': 271}), 69: (1, {'@': 271}), 67: (1, {'@': 271}), 4: (1, {'@': 271}), 71: (1, {'@': 271}), 12: (1, {'@': 271}), 13: (1, {'@': 271}), 11: (1, {'@': 271})}, 522: {14: (1, {'@': 257}), 15: (1, {'@': 257}), 16: (1, {'@': 257}), 17: (1, {'@': 257}), 18: (1, {'@': 257}), 19: (1, {'@': 257}), 20: (1, {'@': 257}), 21: (1, {'@': 257}), 22: (1, {'@': 257}), 23: (1, {'@': 257}), 24: (1, {'@': 257}), 25: (1, {'@': 257}), 1: (1, {'@': 257}), 26: (1, {'@': 257}), 27: (1, {'@': 257}), 28: (1, {'@': 257}), 46: (1, {'@': 257}), 29: (1, {'@': 257}), 30: (1, {'@': 257}), 3: (1, {'@': 257}), 31: (1, {'@': 257}), 9: (1, {'@': 257}), 32: (1, {'@': 257}), 34: (1, {'@': 257}), 33: (1, {'@': 257}), 35: (1, {'@': 257}), 36: (1, {'@': 257}), 37: (1, {'@': 257}), 47: (1, {'@': 257}), 38: (1, {'@': 257}), 48: (1, {'@': 257}), 39: (1, {'@': 257}), 49: (1, {'@': 257}), 40: (1, {'@': 257}), 41: (1, {'@': 257}), 42: (1, {'@': 257})}, 523: {216: (0, 56), 214: (0, 173), 215: (0, 266), 3: (1, {'@': 329})}, 524: {11: (1, {'@': 87}), 13: (1, {'@': 87}), 12: (1, {'@': 87})}, 525: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 191: (0, 337), 81: (0, 786), 11: (0, 632), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 166: (0, 363), 165: (0, 348), 92: (0, 604), 164: (0, 399), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 116: (0, 774), 155: (0, 651), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 192: (0, 92), 193: (0, 480), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 194: (0, 361), 97: (0, 700), 106: (0, 762), 108: (0, 518), 119: (0, 674), 150: (0, 741), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 13: (1, {'@': 148})}, 526: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 131: (0, 309), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 527: {11: (1, {'@': 344}), 12: (1, {'@': 344}), 13: (1, {'@': 344})}, 528: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 131: (0, 301), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 529: {72: (1, {'@': 279}), 56: (1, {'@': 279}), 78: (1, {'@': 279}), 80: (1, {'@': 279}), 81: (1, {'@': 279}), 68: (1, {'@': 279}), 84: (1, {'@': 279}), 69: (1, {'@': 279}), 86: (1, {'@': 279}), 71: (1, {'@': 279}), 87: (1, {'@': 279}), 89: (1, {'@': 279}), 12: (1, {'@': 279}), 93: (1, {'@': 279}), 60: (1, {'@': 279}), 63: (1, {'@': 279}), 62: (1, {'@': 279}), 97: (1, {'@': 279}), 99: (1, {'@': 279}), 100: (1, {'@': 279}), 102: (1, {'@': 279}), 43: (1, {'@': 279}), 103: (1, {'@': 279}), 105: (1, {'@': 279}), 107: (1, {'@': 279}), 108: (1, {'@': 279}), 112: (1, {'@': 279}), 70: (1, {'@': 279}), 64: (1, {'@': 279}), 67: (1, {'@': 279}), 3: (1, {'@': 279}), 9: (1, {'@': 279}), 115: (1, {'@': 279}), 116: (1, {'@': 279}), 5: (1, {'@': 279}), 125: (1, {'@': 279}), 59: (1, {'@': 279}), 120: (1, {'@': 279}), 65: (1, {'@': 279}), 6: (1, {'@': 279}), 54: (1, {'@': 279}), 123: (1, {'@': 279}), 73: (1, {'@': 279}), 74: (1, {'@': 279}), 75: (1, {'@': 279}), 76: (1, {'@': 279}), 77: (1, {'@': 279}), 79: (1, {'@': 279}), 61: (1, {'@': 279}), 82: (1, {'@': 279}), 10: (1, {'@': 279}), 66: (1, {'@': 279}), 83: (1, {'@': 279}), 85: (1, {'@': 279}), 11: (1, {'@': 279}), 88: (1, {'@': 279}), 90: (1, {'@': 279}), 91: (1, {'@': 279}), 92: (1, {'@': 279}), 94: (1, {'@': 279}), 47: (1, {'@': 279}), 95: (1, {'@': 279}), 96: (1, {'@': 279}), 4: (1, {'@': 279}), 55: (1, {'@': 279}), 98: (1, {'@': 279}), 101: (1, {'@': 279}), 13: (1, {'@': 279}), 104: (1, {'@': 279}), 106: (1, {'@': 279}), 58: (1, {'@': 279}), 109: (1, {'@': 279}), 1: (1, {'@': 279}), 110: (1, {'@': 279}), 111: (1, {'@': 279}), 113: (1, {'@': 279}), 114: (1, {'@': 279}), 32: (1, {'@': 279}), 57: (1, {'@': 279}), 117: (1, {'@': 279}), 118: (1, {'@': 279}), 119: (1, {'@': 279}), 121: (1, {'@': 279}), 122: (1, {'@': 279}), 124: (1, {'@': 279})}, 530: {72: (1, {'@': 303}), 56: (1, {'@': 303}), 78: (1, {'@': 303}), 80: (1, {'@': 303}), 81: (1, {'@': 303}), 68: (1, {'@': 303}), 84: (1, {'@': 303}), 69: (1, {'@': 303}), 86: (1, {'@': 303}), 71: (1, {'@': 303}), 87: (1, {'@': 303}), 89: (1, {'@': 303}), 12: (1, {'@': 303}), 93: (1, {'@': 303}), 60: (1, {'@': 303}), 63: (1, {'@': 303}), 62: (1, {'@': 303}), 97: (1, {'@': 303}), 99: (1, {'@': 303}), 100: (1, {'@': 303}), 102: (1, {'@': 303}), 43: (1, {'@': 303}), 103: (1, {'@': 303}), 105: (1, {'@': 303}), 107: (1, {'@': 303}), 108: (1, {'@': 303}), 112: (1, {'@': 303}), 70: (1, {'@': 303}), 64: (1, {'@': 303}), 67: (1, {'@': 303}), 3: (1, {'@': 303}), 9: (1, {'@': 303}), 115: (1, {'@': 303}), 116: (1, {'@': 303}), 5: (1, {'@': 303}), 125: (1, {'@': 303}), 59: (1, {'@': 303}), 120: (1, {'@': 303}), 65: (1, {'@': 303}), 6: (1, {'@': 303}), 54: (1, {'@': 303}), 123: (1, {'@': 303}), 73: (1, {'@': 303}), 74: (1, {'@': 303}), 75: (1, {'@': 303}), 76: (1, {'@': 303}), 77: (1, {'@': 303}), 79: (1, {'@': 303}), 61: (1, {'@': 303}), 82: (1, {'@': 303}), 10: (1, {'@': 303}), 66: (1, {'@': 303}), 83: (1, {'@': 303}), 85: (1, {'@': 303}), 11: (1, {'@': 303}), 88: (1, {'@': 303}), 90: (1, {'@': 303}), 91: (1, {'@': 303}), 92: (1, {'@': 303}), 94: (1, {'@': 303}), 47: (1, {'@': 303}), 95: (1, {'@': 303}), 96: (1, {'@': 303}), 4: (1, {'@': 303}), 55: (1, {'@': 303}), 98: (1, {'@': 303}), 101: (1, {'@': 303}), 13: (1, {'@': 303}), 104: (1, {'@': 303}), 106: (1, {'@': 303}), 58: (1, {'@': 303}), 109: (1, {'@': 303}), 1: (1, {'@': 303}), 110: (1, {'@': 303}), 111: (1, {'@': 303}), 113: (1, {'@': 303}), 114: (1, {'@': 303}), 32: (1, {'@': 303}), 57: (1, {'@': 303}), 117: (1, {'@': 303}), 118: (1, {'@': 303}), 119: (1, {'@': 303}), 121: (1, {'@': 303}), 122: (1, {'@': 303}), 124: (1, {'@': 303})}, 531: {11: (1, {'@': 183}), 12: (1, {'@': 183}), 13: (1, {'@': 183})}, 532: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 131: (0, 457), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 533: {72: (1, {'@': 302}), 56: (1, {'@': 302}), 78: (1, {'@': 302}), 80: (1, {'@': 302}), 81: (1, {'@': 302}), 68: (1, {'@': 302}), 84: (1, {'@': 302}), 69: (1, {'@': 302}), 86: (1, {'@': 302}), 71: (1, {'@': 302}), 87: (1, {'@': 302}), 89: (1, {'@': 302}), 12: (1, {'@': 302}), 93: (1, {'@': 302}), 60: (1, {'@': 302}), 63: (1, {'@': 302}), 62: (1, {'@': 302}), 97: (1, {'@': 302}), 99: (1, {'@': 302}), 100: (1, {'@': 302}), 102: (1, {'@': 302}), 43: (1, {'@': 302}), 103: (1, {'@': 302}), 105: (1, {'@': 302}), 107: (1, {'@': 302}), 108: (1, {'@': 302}), 112: (1, {'@': 302}), 70: (1, {'@': 302}), 64: (1, {'@': 302}), 67: (1, {'@': 302}), 3: (1, {'@': 302}), 9: (1, {'@': 302}), 115: (1, {'@': 302}), 116: (1, {'@': 302}), 5: (1, {'@': 302}), 125: (1, {'@': 302}), 59: (1, {'@': 302}), 120: (1, {'@': 302}), 65: (1, {'@': 302}), 6: (1, {'@': 302}), 54: (1, {'@': 302}), 123: (1, {'@': 302}), 73: (1, {'@': 302}), 74: (1, {'@': 302}), 75: (1, {'@': 302}), 76: (1, {'@': 302}), 77: (1, {'@': 302}), 79: (1, {'@': 302}), 61: (1, {'@': 302}), 82: (1, {'@': 302}), 10: (1, {'@': 302}), 66: (1, {'@': 302}), 83: (1, {'@': 302}), 85: (1, {'@': 302}), 11: (1, {'@': 302}), 88: (1, {'@': 302}), 90: (1, {'@': 302}), 91: (1, {'@': 302}), 92: (1, {'@': 302}), 94: (1, {'@': 302}), 47: (1, {'@': 302}), 95: (1, {'@': 302}), 96: (1, {'@': 302}), 4: (1, {'@': 302}), 55: (1, {'@': 302}), 98: (1, {'@': 302}), 101: (1, {'@': 302}), 13: (1, {'@': 302}), 104: (1, {'@': 302}), 106: (1, {'@': 302}), 58: (1, {'@': 302}), 109: (1, {'@': 302}), 1: (1, {'@': 302}), 110: (1, {'@': 302}), 111: (1, {'@': 302}), 113: (1, {'@': 302}), 114: (1, {'@': 302}), 32: (1, {'@': 302}), 57: (1, {'@': 302}), 117: (1, {'@': 302}), 118: (1, {'@': 302}), 119: (1, {'@': 302}), 121: (1, {'@': 302}), 122: (1, {'@': 302}), 124: (1, {'@': 302})}, 534: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 131: (0, 418), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 535: {72: (1, {'@': 274}), 56: (1, {'@': 274}), 78: (1, {'@': 274}), 80: (1, {'@': 274}), 81: (1, {'@': 274}), 68: (1, {'@': 274}), 84: (1, {'@': 274}), 69: (1, {'@': 274}), 86: (1, {'@': 274}), 71: (1, {'@': 274}), 87: (1, {'@': 274}), 89: (1, {'@': 274}), 12: (1, {'@': 274}), 93: (1, {'@': 274}), 60: (1, {'@': 274}), 63: (1, {'@': 274}), 62: (1, {'@': 274}), 97: (1, {'@': 274}), 99: (1, {'@': 274}), 100: (1, {'@': 274}), 102: (1, {'@': 274}), 43: (1, {'@': 274}), 103: (1, {'@': 274}), 105: (1, {'@': 274}), 107: (1, {'@': 274}), 108: (1, {'@': 274}), 112: (1, {'@': 274}), 70: (1, {'@': 274}), 64: (1, {'@': 274}), 67: (1, {'@': 274}), 3: (1, {'@': 274}), 9: (1, {'@': 274}), 115: (1, {'@': 274}), 116: (1, {'@': 274}), 5: (1, {'@': 274}), 125: (1, {'@': 274}), 59: (1, {'@': 274}), 120: (1, {'@': 274}), 65: (1, {'@': 274}), 6: (1, {'@': 274}), 54: (1, {'@': 274}), 123: (1, {'@': 274}), 73: (1, {'@': 274}), 74: (1, {'@': 274}), 75: (1, {'@': 274}), 76: (1, {'@': 274}), 77: (1, {'@': 274}), 79: (1, {'@': 274}), 61: (1, {'@': 274}), 82: (1, {'@': 274}), 10: (1, {'@': 274}), 66: (1, {'@': 274}), 83: (1, {'@': 274}), 85: (1, {'@': 274}), 11: (1, {'@': 274}), 88: (1, {'@': 274}), 90: (1, {'@': 274}), 91: (1, {'@': 274}), 92: (1, {'@': 274}), 94: (1, {'@': 274}), 47: (1, {'@': 274}), 95: (1, {'@': 274}), 96: (1, {'@': 274}), 4: (1, {'@': 274}), 55: (1, {'@': 274}), 98: (1, {'@': 274}), 101: (1, {'@': 274}), 13: (1, {'@': 274}), 104: (1, {'@': 274}), 106: (1, {'@': 274}), 58: (1, {'@': 274}), 109: (1, {'@': 274}), 1: (1, {'@': 274}), 110: (1, {'@': 274}), 111: (1, {'@': 274}), 113: (1, {'@': 274}), 114: (1, {'@': 274}), 32: (1, {'@': 274}), 57: (1, {'@': 274}), 117: (1, {'@': 274}), 118: (1, {'@': 274}), 119: (1, {'@': 274}), 121: (1, {'@': 274}), 122: (1, {'@': 274}), 124: (1, {'@': 274})}, 536: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 131: (0, 234), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 537: {11: (1, {'@': 58}), 13: (1, {'@': 58}), 12: (1, {'@': 58})}, 538: {31: (0, 3), 126: (0, 96), 50: (0, 125), 17: (0, 369), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 128: (0, 295), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 539: {4: (0, 400)}, 540: {10: (1, {'@': 360}), 12: (1, {'@': 360}), 13: (1, {'@': 360}), 5: (1, {'@': 360}), 4: (1, {'@': 360}), 11: (1, {'@': 360}), 169: (1, {'@': 360}), 170: (1, {'@': 360})}, 541: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 45), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 542: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 131: (0, 672), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 543: {11: (1, {'@': 60}), 13: (1, {'@': 60}), 12: (1, {'@': 60})}, 544: {121: (0, 764), 116: (0, 404), 103: (0, 103)}, 545: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 357), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 546: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 131: (0, 360), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 547: {11: (1, {'@': 49}), 13: (1, {'@': 49}), 12: (1, {'@': 49})}, 548: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 218: (0, 736), 19: (0, 150), 131: (0, 701), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 95: (0, 542), 20: (0, 247), 101: (0, 265), 138: (0, 287), 82: (0, 278), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 108: (0, 487), 139: (0, 505), 22: (0, 11), 42: (0, 233), 110: (0, 450), 41: (0, 273), 21: (0, 305), 219: (0, 653), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 100: (0, 314), 48: (0, 486)}, 549: {44: (0, 12), 45: (0, 32), 175: (0, 8), 220: (0, 572), 120: (0, 550)}, 550: {122: (1, {'@': 142}), 165: (1, {'@': 142}), 164: (1, {'@': 142}), 12: (1, {'@': 142}), 166: (1, {'@': 142}), 72: (1, {'@': 142}), 98: (1, {'@': 142}), 99: (1, {'@': 142}), 100: (1, {'@': 142}), 101: (1, {'@': 142}), 102: (1, {'@': 142}), 76: (1, {'@': 142}), 13: (1, {'@': 142}), 103: (1, {'@': 142}), 105: (1, {'@': 142}), 104: (1, {'@': 142}), 78: (1, {'@': 142}), 77: (1, {'@': 142}), 106: (1, {'@': 142}), 107: (1, {'@': 142}), 79: (1, {'@': 142}), 108: (1, {'@': 142}), 80: (1, {'@': 142}), 81: (1, {'@': 142}), 82: (1, {'@': 142}), 44: (1, {'@': 142}), 109: (1, {'@': 142}), 83: (1, {'@': 142}), 1: (1, {'@': 142}), 110: (1, {'@': 142}), 111: (1, {'@': 142}), 84: (1, {'@': 142}), 112: (1, {'@': 142}), 85: (1, {'@': 142}), 11: (1, {'@': 142}), 86: (1, {'@': 142}), 114: (1, {'@': 142}), 163: (1, {'@': 142}), 45: (1, {'@': 142}), 87: (1, {'@': 142}), 3: (1, {'@': 142}), 88: (1, {'@': 142}), 89: (1, {'@': 142}), 115: (1, {'@': 142}), 116: (1, {'@': 142}), 117: (1, {'@': 142}), 118: (1, {'@': 142}), 91: (1, {'@': 142}), 119: (1, {'@': 142}), 92: (1, {'@': 142}), 93: (1, {'@': 142}), 94: (1, {'@': 142}), 120: (1, {'@': 142}), 95: (1, {'@': 142}), 96: (1, {'@': 142}), 121: (1, {'@': 142}), 123: (1, {'@': 142}), 97: (1, {'@': 142}), 124: (1, {'@': 142}), 167: (1, {'@': 142}), 168: (1, {'@': 142})}, 551: {81: (0, 574), 1: (0, 649), 32: (0, 545), 3: (0, 684)}, 552: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 13: (0, 707), 199: (0, 620), 105: (0, 588), 149: (0, 779), 148: (0, 769), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 11: (0, 632), 175: (0, 593), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 208: (0, 244), 86: (0, 656), 151: (0, 717), 72: (0, 555), 92: (0, 604), 213: (0, 622), 101: (0, 699), 122: (0, 524), 100: (0, 685), 152: (0, 536), 74: (0, 597), 192: (0, 553), 113: (0, 712), 153: (0, 715), 154: (0, 780), 107: (0, 591), 193: (0, 624), 191: (0, 757), 114: (0, 759), 200: (0, 750), 116: (0, 774), 155: (0, 651), 75: (0, 654), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 201: (0, 618), 117: (0, 626), 202: (0, 637), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 194: (0, 721), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 150: (0, 741), 111: (0, 783), 161: (0, 729), 160: (0, 746), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 553: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 156: (0, 747), 93: (0, 630), 13: (0, 599), 98: (0, 544), 11: (0, 602), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 150: (0, 705), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 122: (0, 112), 106: (0, 762), 108: (0, 518), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 554: {12: (0, 645), 187: (0, 44), 175: (0, 663), 120: (0, 550), 166: (0, 532), 188: (0, 592), 185: (0, 758), 165: (0, 586), 189: (0, 512), 164: (0, 531), 190: (0, 37)}, 555: {33: (0, 667), 3: (0, 609), 11: (1, {'@': 75}), 13: (1, {'@': 75}), 12: (1, {'@': 75})}, 556: {}, 557: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 150: (0, 81), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 185: (0, 261), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 165: (0, 586), 156: (0, 747), 11: (0, 73), 93: (0, 630), 164: (0, 531), 98: (0, 544), 118: (0, 548), 157: (0, 552), 13: (0, 627), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 175: (0, 619), 94: (0, 584), 82: (0, 568), 159: (0, 595), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 558: {9: (0, 561)}, 559: {31: (0, 3), 25: (0, 203), 126: (0, 96), 22: (0, 11), 50: (0, 125), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 19: (0, 150), 1: (0, 324), 35: (0, 397), 42: (0, 233), 138: (0, 267), 51: (0, 456), 52: (0, 354), 135: (0, 483), 33: (0, 356), 129: (0, 497), 136: (0, 403), 23: (0, 219), 29: (0, 474), 130: (0, 169), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 133: (0, 160), 41: (0, 273), 27: (0, 462), 28: (0, 42), 21: (0, 305), 49: (0, 373), 46: (0, 445), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 560: {57: (1, {'@': 387}), 56: (1, {'@': 387}), 54: (1, {'@': 387}), 63: (1, {'@': 387}), 11: (1, {'@': 387}), 12: (1, {'@': 387}), 13: (1, {'@': 387}), 72: (1, {'@': 387}), 73: (1, {'@': 387}), 74: (1, {'@': 387}), 75: (1, {'@': 387}), 76: (1, {'@': 387}), 77: (1, {'@': 387}), 78: (1, {'@': 387}), 79: (1, {'@': 387}), 80: (1, {'@': 387}), 81: (1, {'@': 387}), 10: (1, {'@': 387}), 82: (1, {'@': 387}), 83: (1, {'@': 387}), 84: (1, {'@': 387}), 85: (1, {'@': 387}), 86: (1, {'@': 387}), 87: (1, {'@': 387}), 88: (1, {'@': 387}), 89: (1, {'@': 387}), 90: (1, {'@': 387}), 91: (1, {'@': 387}), 92: (1, {'@': 387}), 93: (1, {'@': 387}), 94: (1, {'@': 387}), 95: (1, {'@': 387}), 96: (1, {'@': 387}), 97: (1, {'@': 387}), 98: (1, {'@': 387}), 99: (1, {'@': 387}), 100: (1, {'@': 387}), 101: (1, {'@': 387}), 102: (1, {'@': 387}), 43: (1, {'@': 387}), 103: (1, {'@': 387}), 104: (1, {'@': 387}), 105: (1, {'@': 387}), 106: (1, {'@': 387}), 107: (1, {'@': 387}), 108: (1, {'@': 387}), 109: (1, {'@': 387}), 1: (1, {'@': 387}), 110: (1, {'@': 387}), 111: (1, {'@': 387}), 112: (1, {'@': 387}), 113: (1, {'@': 387}), 114: (1, {'@': 387}), 3: (1, {'@': 387}), 32: (1, {'@': 387}), 115: (1, {'@': 387}), 5: (1, {'@': 387}), 116: (1, {'@': 387}), 117: (1, {'@': 387}), 118: (1, {'@': 387}), 119: (1, {'@': 387}), 120: (1, {'@': 387}), 121: (1, {'@': 387}), 6: (1, {'@': 387}), 122: (1, {'@': 387}), 123: (1, {'@': 387}), 124: (1, {'@': 387})}, 561: {3: (0, 283)}, 562: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 135: (0, 483), 33: (0, 356), 129: (0, 497), 136: (0, 403), 23: (0, 219), 29: (0, 474), 130: (0, 169), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 133: (0, 74), 41: (0, 273), 27: (0, 462), 28: (0, 42), 21: (0, 305), 49: (0, 373), 46: (0, 445), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 563: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 521), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 196: (0, 180), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 197: (0, 708), 1: (0, 614), 25: (0, 203), 134: (0, 433), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 10: (0, 209), 20: (0, 247), 138: (0, 287), 54: (0, 222), 15: (0, 205), 32: (0, 504), 30: (0, 194), 131: (0, 210), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 564: {33: (0, 71), 3: (0, 489)}, 565: {11: (0, 75), 12: (1, {'@': 207}), 13: (1, {'@': 207})}, 566: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 521), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 196: (0, 180), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 197: (0, 708), 1: (0, 614), 25: (0, 203), 134: (0, 433), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 10: (0, 209), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 66), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 567: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 165: (0, 586), 156: (0, 747), 93: (0, 630), 164: (0, 531), 13: (0, 599), 98: (0, 544), 11: (0, 602), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 175: (0, 619), 159: (0, 595), 150: (0, 705), 102: (0, 601), 79: (0, 589), 117: (0, 626), 185: (0, 402), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 568: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 131: (0, 727), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 569: {4: (0, 468)}, 570: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 150: (0, 81), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 165: (0, 586), 156: (0, 747), 11: (0, 73), 93: (0, 630), 164: (0, 531), 98: (0, 544), 118: (0, 548), 157: (0, 552), 185: (0, 440), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 175: (0, 619), 94: (0, 584), 82: (0, 568), 159: (0, 595), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 12: (1, {'@': 204}), 13: (1, {'@': 204})}, 571: {221: (0, 174), 33: (0, 342), 3: (0, 188)}, 572: {11: (1, {'@': 89}), 13: (1, {'@': 89}), 12: (1, {'@': 89})}, 573: {9: (0, 288), 171: (0, 740), 4: (0, 760)}, 574: {3: (0, 393), 11: (1, {'@': 119}), 13: (1, {'@': 119}), 12: (1, {'@': 119})}, 575: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 222: (0, 432), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 223: (0, 659), 87: (0, 615), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 95: (0, 542), 224: (0, 481), 20: (0, 247), 101: (0, 265), 131: (0, 7), 138: (0, 287), 82: (0, 278), 15: (0, 205), 219: (0, 454), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 108: (0, 487), 139: (0, 505), 22: (0, 11), 42: (0, 233), 110: (0, 450), 41: (0, 273), 21: (0, 305), 225: (0, 389), 170: (0, 411), 99: (0, 297), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 100: (0, 314), 48: (0, 486), 5: (1, {'@': 239}), 11: (1, {'@': 239}), 12: (1, {'@': 239}), 43: (1, {'@': 239}), 13: (1, {'@': 239})}, 576: {72: (1, {'@': 272}), 56: (1, {'@': 272}), 78: (1, {'@': 272}), 80: (1, {'@': 272}), 81: (1, {'@': 272}), 68: (1, {'@': 272}), 84: (1, {'@': 272}), 69: (1, {'@': 272}), 86: (1, {'@': 272}), 71: (1, {'@': 272}), 87: (1, {'@': 272}), 89: (1, {'@': 272}), 12: (1, {'@': 272}), 93: (1, {'@': 272}), 60: (1, {'@': 272}), 63: (1, {'@': 272}), 62: (1, {'@': 272}), 97: (1, {'@': 272}), 99: (1, {'@': 272}), 100: (1, {'@': 272}), 102: (1, {'@': 272}), 43: (1, {'@': 272}), 103: (1, {'@': 272}), 105: (1, {'@': 272}), 107: (1, {'@': 272}), 108: (1, {'@': 272}), 112: (1, {'@': 272}), 70: (1, {'@': 272}), 64: (1, {'@': 272}), 67: (1, {'@': 272}), 3: (1, {'@': 272}), 9: (1, {'@': 272}), 115: (1, {'@': 272}), 116: (1, {'@': 272}), 5: (1, {'@': 272}), 125: (1, {'@': 272}), 59: (1, {'@': 272}), 120: (1, {'@': 272}), 65: (1, {'@': 272}), 6: (1, {'@': 272}), 54: (1, {'@': 272}), 123: (1, {'@': 272}), 73: (1, {'@': 272}), 74: (1, {'@': 272}), 75: (1, {'@': 272}), 76: (1, {'@': 272}), 77: (1, {'@': 272}), 79: (1, {'@': 272}), 61: (1, {'@': 272}), 82: (1, {'@': 272}), 10: (1, {'@': 272}), 66: (1, {'@': 272}), 83: (1, {'@': 272}), 85: (1, {'@': 272}), 11: (1, {'@': 272}), 88: (1, {'@': 272}), 90: (1, {'@': 272}), 91: (1, {'@': 272}), 92: (1, {'@': 272}), 94: (1, {'@': 272}), 47: (1, {'@': 272}), 95: (1, {'@': 272}), 96: (1, {'@': 272}), 4: (1, {'@': 272}), 55: (1, {'@': 272}), 98: (1, {'@': 272}), 101: (1, {'@': 272}), 13: (1, {'@': 272}), 104: (1, {'@': 272}), 106: (1, {'@': 272}), 58: (1, {'@': 272}), 109: (1, {'@': 272}), 1: (1, {'@': 272}), 110: (1, {'@': 272}), 111: (1, {'@': 272}), 113: (1, {'@': 272}), 114: (1, {'@': 272}), 32: (1, {'@': 272}), 57: (1, {'@': 272}), 117: (1, {'@': 272}), 118: (1, {'@': 272}), 119: (1, {'@': 272}), 121: (1, {'@': 272}), 122: (1, {'@': 272}), 124: (1, {'@': 272})}, 577: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 156: (0, 747), 93: (0, 630), 13: (0, 599), 98: (0, 544), 11: (0, 602), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 150: (0, 705), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 120: (1, {'@': 151}), 165: (1, {'@': 151}), 164: (1, {'@': 151}), 122: (1, {'@': 151}), 12: (1, {'@': 151}), 166: (1, {'@': 151}), 45: (1, {'@': 151}), 44: (1, {'@': 151}), 168: (1, {'@': 151}), 163: (1, {'@': 151}), 167: (1, {'@': 151})}, 578: {3: (0, 298)}, 579: {96: (0, 772), 88: (0, 650), 158: (0, 571), 79: (0, 589)}, 580: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 13: (0, 707), 199: (0, 620), 105: (0, 588), 149: (0, 779), 148: (0, 769), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 11: (0, 632), 175: (0, 211), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 208: (0, 244), 86: (0, 656), 151: (0, 717), 213: (0, 85), 72: (0, 555), 92: (0, 604), 101: (0, 699), 174: (0, 80), 100: (0, 685), 152: (0, 536), 74: (0, 597), 113: (0, 712), 153: (0, 715), 154: (0, 780), 107: (0, 591), 193: (0, 624), 191: (0, 757), 114: (0, 759), 200: (0, 750), 116: (0, 774), 155: (0, 651), 75: (0, 654), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 201: (0, 618), 117: (0, 626), 202: (0, 637), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 192: (0, 686), 194: (0, 721), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 150: (0, 741), 111: (0, 783), 161: (0, 729), 160: (0, 746), 87: (0, 719), 115: (0, 710), 85: (0, 680), 122: (0, 714), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 581: {5: (0, 660)}, 582: {11: (1, {'@': 326}), 13: (1, {'@': 326}), 12: (1, {'@': 326})}, 583: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 131: (0, 773), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 584: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 131: (0, 713), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486), 11: (1, {'@': 98}), 13: (1, {'@': 98}), 12: (1, {'@': 98})}, 585: {72: (1, {'@': 294}), 56: (1, {'@': 294}), 78: (1, {'@': 294}), 80: (1, {'@': 294}), 81: (1, {'@': 294}), 68: (1, {'@': 294}), 84: (1, {'@': 294}), 69: (1, {'@': 294}), 86: (1, {'@': 294}), 71: (1, {'@': 294}), 87: (1, {'@': 294}), 89: (1, {'@': 294}), 12: (1, {'@': 294}), 93: (1, {'@': 294}), 60: (1, {'@': 294}), 63: (1, {'@': 294}), 62: (1, {'@': 294}), 97: (1, {'@': 294}), 99: (1, {'@': 294}), 100: (1, {'@': 294}), 102: (1, {'@': 294}), 43: (1, {'@': 294}), 103: (1, {'@': 294}), 105: (1, {'@': 294}), 107: (1, {'@': 294}), 108: (1, {'@': 294}), 112: (1, {'@': 294}), 70: (1, {'@': 294}), 64: (1, {'@': 294}), 67: (1, {'@': 294}), 3: (1, {'@': 294}), 9: (1, {'@': 294}), 115: (1, {'@': 294}), 116: (1, {'@': 294}), 5: (1, {'@': 294}), 125: (1, {'@': 294}), 59: (1, {'@': 294}), 120: (1, {'@': 294}), 65: (1, {'@': 294}), 6: (1, {'@': 294}), 54: (1, {'@': 294}), 123: (1, {'@': 294}), 73: (1, {'@': 294}), 74: (1, {'@': 294}), 75: (1, {'@': 294}), 76: (1, {'@': 294}), 77: (1, {'@': 294}), 79: (1, {'@': 294}), 61: (1, {'@': 294}), 82: (1, {'@': 294}), 10: (1, {'@': 294}), 66: (1, {'@': 294}), 83: (1, {'@': 294}), 85: (1, {'@': 294}), 11: (1, {'@': 294}), 88: (1, {'@': 294}), 90: (1, {'@': 294}), 91: (1, {'@': 294}), 92: (1, {'@': 294}), 94: (1, {'@': 294}), 47: (1, {'@': 294}), 95: (1, {'@': 294}), 96: (1, {'@': 294}), 4: (1, {'@': 294}), 55: (1, {'@': 294}), 98: (1, {'@': 294}), 101: (1, {'@': 294}), 13: (1, {'@': 294}), 104: (1, {'@': 294}), 106: (1, {'@': 294}), 58: (1, {'@': 294}), 109: (1, {'@': 294}), 1: (1, {'@': 294}), 110: (1, {'@': 294}), 111: (1, {'@': 294}), 113: (1, {'@': 294}), 114: (1, {'@': 294}), 32: (1, {'@': 294}), 57: (1, {'@': 294}), 117: (1, {'@': 294}), 118: (1, {'@': 294}), 119: (1, {'@': 294}), 121: (1, {'@': 294}), 122: (1, {'@': 294}), 124: (1, {'@': 294})}, 586: {11: (1, {'@': 185}), 12: (1, {'@': 185}), 13: (1, {'@': 185})}, 587: {5: (1, {'@': 238}), 11: (1, {'@': 238}), 12: (1, {'@': 238}), 43: (1, {'@': 238}), 13: (1, {'@': 238})}, 588: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 131: (0, 519), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 589: {33: (1, {'@': 182}), 3: (1, {'@': 182})}, 590: {176: (0, 302), 209: (0, 291), 3: (0, 286)}, 591: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 131: (0, 155), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 592: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 13: (0, 707), 199: (0, 620), 105: (0, 588), 149: (0, 779), 148: (0, 769), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 11: (0, 632), 175: (0, 593), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 208: (0, 244), 86: (0, 656), 151: (0, 717), 72: (0, 555), 92: (0, 604), 101: (0, 699), 100: (0, 685), 152: (0, 536), 74: (0, 597), 213: (0, 162), 113: (0, 712), 153: (0, 715), 154: (0, 780), 107: (0, 591), 193: (0, 624), 191: (0, 757), 114: (0, 759), 200: (0, 750), 116: (0, 774), 155: (0, 651), 75: (0, 654), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 201: (0, 618), 117: (0, 626), 202: (0, 637), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 192: (0, 686), 194: (0, 721), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 150: (0, 741), 111: (0, 783), 161: (0, 729), 160: (0, 746), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 593: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 191: (0, 62), 81: (0, 786), 11: (0, 632), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 116: (0, 774), 155: (0, 651), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 192: (0, 92), 193: (0, 480), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 194: (0, 361), 97: (0, 700), 106: (0, 762), 108: (0, 518), 119: (0, 674), 150: (0, 741), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 163: (1, {'@': 148}), 120: (1, {'@': 148}), 45: (1, {'@': 148}), 44: (1, {'@': 148}), 12: (1, {'@': 148}), 13: (1, {'@': 148}), 164: (1, {'@': 148}), 122: (1, {'@': 148}), 165: (1, {'@': 148}), 166: (1, {'@': 148}), 167: (1, {'@': 148})}, 594: {11: (1, {'@': 346}), 12: (1, {'@': 346}), 13: (1, {'@': 346})}, 595: {170: (0, 126), 11: (1, {'@': 157}), 13: (1, {'@': 157}), 12: (1, {'@': 157})}, 596: {5: (0, 507)}, 597: {3: (0, 424), 226: (0, 346), 227: (0, 558)}, 598: {11: (1, {'@': 65}), 13: (1, {'@': 65}), 12: (1, {'@': 65})}, 599: {72: (1, {'@': 8}), 74: (1, {'@': 8}), 75: (1, {'@': 8}), 76: (1, {'@': 8}), 77: (1, {'@': 8}), 78: (1, {'@': 8}), 166: (1, {'@': 8}), 79: (1, {'@': 8}), 80: (1, {'@': 8}), 81: (1, {'@': 8}), 82: (1, {'@': 8}), 44: (1, {'@': 8}), 83: (1, {'@': 8}), 84: (1, {'@': 8}), 85: (1, {'@': 8}), 11: (1, {'@': 8}), 86: (1, {'@': 8}), 163: (1, {'@': 8}), 45: (1, {'@': 8}), 87: (1, {'@': 8}), 88: (1, {'@': 8}), 89: (1, {'@': 8}), 12: (1, {'@': 8}), 91: (1, {'@': 8}), 92: (1, {'@': 8}), 93: (1, {'@': 8}), 94: (1, {'@': 8}), 95: (1, {'@': 8}), 96: (1, {'@': 8}), 164: (1, {'@': 8}), 165: (1, {'@': 8}), 97: (1, {'@': 8}), 167: (1, {'@': 8}), 98: (1, {'@': 8}), 99: (1, {'@': 8}), 100: (1, {'@': 8}), 101: (1, {'@': 8}), 102: (1, {'@': 8}), 13: (1, {'@': 8}), 103: (1, {'@': 8}), 105: (1, {'@': 8}), 104: (1, {'@': 8}), 106: (1, {'@': 8}), 107: (1, {'@': 8}), 108: (1, {'@': 8}), 109: (1, {'@': 8}), 1: (1, {'@': 8}), 110: (1, {'@': 8}), 111: (1, {'@': 8}), 112: (1, {'@': 8}), 113: (1, {'@': 8}), 114: (1, {'@': 8}), 3: (1, {'@': 8}), 115: (1, {'@': 8}), 116: (1, {'@': 8}), 117: (1, {'@': 8}), 118: (1, {'@': 8}), 119: (1, {'@': 8}), 120: (1, {'@': 8}), 121: (1, {'@': 8}), 122: (1, {'@': 8}), 123: (1, {'@': 8}), 124: (1, {'@': 8}), 195: (1, {'@': 8}), 168: (1, {'@': 8})}, 600: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 131: (0, 367), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 601: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 131: (0, 694), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 602: {72: (1, {'@': 11}), 168: (1, {'@': 11}), 98: (1, {'@': 11}), 99: (1, {'@': 11}), 100: (1, {'@': 11}), 101: (1, {'@': 11}), 102: (1, {'@': 11}), 76: (1, {'@': 11}), 13: (1, {'@': 11}), 103: (1, {'@': 11}), 105: (1, {'@': 11}), 104: (1, {'@': 11}), 78: (1, {'@': 11}), 77: (1, {'@': 11}), 106: (1, {'@': 11}), 107: (1, {'@': 11}), 79: (1, {'@': 11}), 108: (1, {'@': 11}), 80: (1, {'@': 11}), 81: (1, {'@': 11}), 82: (1, {'@': 11}), 109: (1, {'@': 11}), 83: (1, {'@': 11}), 1: (1, {'@': 11}), 110: (1, {'@': 11}), 111: (1, {'@': 11}), 84: (1, {'@': 11}), 112: (1, {'@': 11}), 85: (1, {'@': 11}), 11: (1, {'@': 11}), 86: (1, {'@': 11}), 114: (1, {'@': 11}), 87: (1, {'@': 11}), 3: (1, {'@': 11}), 88: (1, {'@': 11}), 89: (1, {'@': 11}), 115: (1, {'@': 11}), 116: (1, {'@': 11}), 117: (1, {'@': 11}), 118: (1, {'@': 11}), 91: (1, {'@': 11}), 119: (1, {'@': 11}), 92: (1, {'@': 11}), 93: (1, {'@': 11}), 94: (1, {'@': 11}), 120: (1, {'@': 11}), 95: (1, {'@': 11}), 96: (1, {'@': 11}), 121: (1, {'@': 11}), 123: (1, {'@': 11}), 97: (1, {'@': 11}), 124: (1, {'@': 11}), 166: (1, {'@': 11}), 44: (1, {'@': 11}), 163: (1, {'@': 11}), 45: (1, {'@': 11}), 12: (1, {'@': 11}), 164: (1, {'@': 11}), 122: (1, {'@': 11}), 165: (1, {'@': 11}), 167: (1, {'@': 11})}, 603: {72: (1, {'@': 280}), 56: (1, {'@': 280}), 78: (1, {'@': 280}), 80: (1, {'@': 280}), 81: (1, {'@': 280}), 68: (1, {'@': 280}), 84: (1, {'@': 280}), 69: (1, {'@': 280}), 86: (1, {'@': 280}), 71: (1, {'@': 280}), 87: (1, {'@': 280}), 89: (1, {'@': 280}), 12: (1, {'@': 280}), 93: (1, {'@': 280}), 60: (1, {'@': 280}), 63: (1, {'@': 280}), 62: (1, {'@': 280}), 97: (1, {'@': 280}), 99: (1, {'@': 280}), 100: (1, {'@': 280}), 102: (1, {'@': 280}), 43: (1, {'@': 280}), 103: (1, {'@': 280}), 105: (1, {'@': 280}), 107: (1, {'@': 280}), 108: (1, {'@': 280}), 112: (1, {'@': 280}), 70: (1, {'@': 280}), 64: (1, {'@': 280}), 67: (1, {'@': 280}), 3: (1, {'@': 280}), 9: (1, {'@': 280}), 115: (1, {'@': 280}), 116: (1, {'@': 280}), 5: (1, {'@': 280}), 125: (1, {'@': 280}), 59: (1, {'@': 280}), 120: (1, {'@': 280}), 65: (1, {'@': 280}), 6: (1, {'@': 280}), 54: (1, {'@': 280}), 123: (1, {'@': 280}), 73: (1, {'@': 280}), 74: (1, {'@': 280}), 75: (1, {'@': 280}), 76: (1, {'@': 280}), 77: (1, {'@': 280}), 79: (1, {'@': 280}), 61: (1, {'@': 280}), 82: (1, {'@': 280}), 10: (1, {'@': 280}), 66: (1, {'@': 280}), 83: (1, {'@': 280}), 85: (1, {'@': 280}), 11: (1, {'@': 280}), 88: (1, {'@': 280}), 90: (1, {'@': 280}), 91: (1, {'@': 280}), 92: (1, {'@': 280}), 94: (1, {'@': 280}), 47: (1, {'@': 280}), 95: (1, {'@': 280}), 96: (1, {'@': 280}), 4: (1, {'@': 280}), 55: (1, {'@': 280}), 98: (1, {'@': 280}), 101: (1, {'@': 280}), 13: (1, {'@': 280}), 104: (1, {'@': 280}), 106: (1, {'@': 280}), 58: (1, {'@': 280}), 109: (1, {'@': 280}), 1: (1, {'@': 280}), 110: (1, {'@': 280}), 111: (1, {'@': 280}), 113: (1, {'@': 280}), 114: (1, {'@': 280}), 32: (1, {'@': 280}), 57: (1, {'@': 280}), 117: (1, {'@': 280}), 118: (1, {'@': 280}), 119: (1, {'@': 280}), 121: (1, {'@': 280}), 122: (1, {'@': 280}), 124: (1, {'@': 280})}, 604: {228: (0, 698), 1: (0, 185), 2: (0, 242), 3: (0, 264)}, 605: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 521), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 1: (0, 614), 25: (0, 203), 134: (0, 433), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 66), 197: (0, 464), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 606: {11: (1, {'@': 20}), 13: (1, {'@': 20}), 12: (1, {'@': 20})}, 607: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 130: (0, 255), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 135: (0, 483), 33: (0, 356), 129: (0, 497), 136: (0, 403), 23: (0, 219), 29: (0, 474), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 608: {72: (1, {'@': 212}), 98: (1, {'@': 212}), 99: (1, {'@': 212}), 100: (1, {'@': 212}), 101: (1, {'@': 212}), 102: (1, {'@': 212}), 76: (1, {'@': 212}), 13: (1, {'@': 212}), 103: (1, {'@': 212}), 104: (1, {'@': 212}), 105: (1, {'@': 212}), 78: (1, {'@': 212}), 77: (1, {'@': 212}), 106: (1, {'@': 212}), 107: (1, {'@': 212}), 79: (1, {'@': 212}), 108: (1, {'@': 212}), 80: (1, {'@': 212}), 81: (1, {'@': 212}), 82: (1, {'@': 212}), 109: (1, {'@': 212}), 83: (1, {'@': 212}), 1: (1, {'@': 212}), 110: (1, {'@': 212}), 111: (1, {'@': 212}), 84: (1, {'@': 212}), 112: (1, {'@': 212}), 85: (1, {'@': 212}), 11: (1, {'@': 212}), 86: (1, {'@': 212}), 114: (1, {'@': 212}), 87: (1, {'@': 212}), 3: (1, {'@': 212}), 88: (1, {'@': 212}), 89: (1, {'@': 212}), 115: (1, {'@': 212}), 116: (1, {'@': 212}), 117: (1, {'@': 212}), 118: (1, {'@': 212}), 91: (1, {'@': 212}), 119: (1, {'@': 212}), 92: (1, {'@': 212}), 93: (1, {'@': 212}), 94: (1, {'@': 212}), 95: (1, {'@': 212}), 96: (1, {'@': 212}), 121: (1, {'@': 212}), 123: (1, {'@': 212}), 97: (1, {'@': 212}), 124: (1, {'@': 212})}, 609: {11: (1, {'@': 76}), 13: (1, {'@': 76}), 12: (1, {'@': 76})}, 610: {4: (0, 366)}, 611: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 13: (0, 707), 199: (0, 620), 105: (0, 588), 149: (0, 779), 148: (0, 769), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 11: (0, 632), 175: (0, 485), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 208: (0, 244), 86: (0, 656), 155: (0, 651), 151: (0, 717), 72: (0, 555), 92: (0, 604), 101: (0, 699), 100: (0, 685), 152: (0, 536), 74: (0, 597), 113: (0, 712), 153: (0, 715), 154: (0, 780), 107: (0, 591), 193: (0, 624), 213: (0, 119), 191: (0, 128), 200: (0, 750), 114: (0, 759), 165: (0, 586), 75: (0, 654), 116: (0, 774), 156: (0, 747), 93: (0, 630), 164: (0, 531), 98: (0, 544), 118: (0, 548), 157: (0, 552), 192: (0, 142), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 94: (0, 584), 82: (0, 568), 159: (0, 595), 102: (0, 601), 79: (0, 589), 201: (0, 618), 117: (0, 626), 202: (0, 637), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 194: (0, 721), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 150: (0, 741), 111: (0, 783), 161: (0, 729), 160: (0, 746), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 185: (0, 156), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 612: {181: (0, 540), 179: (0, 104), 209: (0, 97), 180: (0, 67), 3: (0, 76), 1: (0, 99), 183: (0, 200), 176: (0, 189), 182: (0, 178), 178: (0, 123), 210: (0, 184), 184: (0, 111)}, 613: {10: (0, 347)}, 614: {9: (0, 566), 171: (0, 642), 5: (1, {'@': 321}), 10: (1, {'@': 321}), 54: (1, {'@': 321}), 12: (1, {'@': 321}), 13: (1, {'@': 321}), 11: (1, {'@': 321})}, 615: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 131: (0, 587), 37: (0, 494), 48: (0, 486)}, 616: {13: (1, {'@': 365})}, 617: {5: (0, 43)}, 618: {13: (0, 499)}, 619: {164: (0, 399), 165: (0, 348)}, 620: {13: (0, 176)}, 621: {72: (1, {'@': 295}), 56: (1, {'@': 295}), 78: (1, {'@': 295}), 80: (1, {'@': 295}), 81: (1, {'@': 295}), 68: (1, {'@': 295}), 84: (1, {'@': 295}), 69: (1, {'@': 295}), 86: (1, {'@': 295}), 71: (1, {'@': 295}), 87: (1, {'@': 295}), 89: (1, {'@': 295}), 12: (1, {'@': 295}), 93: (1, {'@': 295}), 60: (1, {'@': 295}), 63: (1, {'@': 295}), 62: (1, {'@': 295}), 97: (1, {'@': 295}), 99: (1, {'@': 295}), 100: (1, {'@': 295}), 102: (1, {'@': 295}), 43: (1, {'@': 295}), 103: (1, {'@': 295}), 105: (1, {'@': 295}), 107: (1, {'@': 295}), 108: (1, {'@': 295}), 112: (1, {'@': 295}), 70: (1, {'@': 295}), 64: (1, {'@': 295}), 67: (1, {'@': 295}), 3: (1, {'@': 295}), 9: (1, {'@': 295}), 115: (1, {'@': 295}), 116: (1, {'@': 295}), 5: (1, {'@': 295}), 125: (1, {'@': 295}), 59: (1, {'@': 295}), 120: (1, {'@': 295}), 65: (1, {'@': 295}), 6: (1, {'@': 295}), 54: (1, {'@': 295}), 123: (1, {'@': 295}), 73: (1, {'@': 295}), 74: (1, {'@': 295}), 75: (1, {'@': 295}), 76: (1, {'@': 295}), 77: (1, {'@': 295}), 79: (1, {'@': 295}), 61: (1, {'@': 295}), 82: (1, {'@': 295}), 10: (1, {'@': 295}), 66: (1, {'@': 295}), 83: (1, {'@': 295}), 85: (1, {'@': 295}), 11: (1, {'@': 295}), 88: (1, {'@': 295}), 90: (1, {'@': 295}), 91: (1, {'@': 295}), 92: (1, {'@': 295}), 94: (1, {'@': 295}), 47: (1, {'@': 295}), 95: (1, {'@': 295}), 96: (1, {'@': 295}), 4: (1, {'@': 295}), 55: (1, {'@': 295}), 98: (1, {'@': 295}), 101: (1, {'@': 295}), 13: (1, {'@': 295}), 104: (1, {'@': 295}), 106: (1, {'@': 295}), 58: (1, {'@': 295}), 109: (1, {'@': 295}), 1: (1, {'@': 295}), 110: (1, {'@': 295}), 111: (1, {'@': 295}), 113: (1, {'@': 295}), 114: (1, {'@': 295}), 32: (1, {'@': 295}), 57: (1, {'@': 295}), 117: (1, {'@': 295}), 118: (1, {'@': 295}), 119: (1, {'@': 295}), 121: (1, {'@': 295}), 122: (1, {'@': 295}), 124: (1, {'@': 295})}, 622: {122: (0, 87)}, 623: {13: (1, {'@': 369})}, 624: {11: (0, 300), 13: (0, 647)}, 625: {13: (1, {'@': 145}), 120: (1, {'@': 150}), 165: (1, {'@': 150}), 164: (1, {'@': 150}), 122: (1, {'@': 150}), 12: (1, {'@': 150}), 166: (1, {'@': 150}), 45: (1, {'@': 150}), 44: (1, {'@': 150}), 168: (1, {'@': 150}), 163: (1, {'@': 150}), 167: (1, {'@': 150})}, 626: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 131: (0, 598), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 627: {72: (1, {'@': 6}), 74: (1, {'@': 6}), 75: (1, {'@': 6}), 76: (1, {'@': 6}), 77: (1, {'@': 6}), 78: (1, {'@': 6}), 166: (1, {'@': 6}), 79: (1, {'@': 6}), 80: (1, {'@': 6}), 81: (1, {'@': 6}), 82: (1, {'@': 6}), 44: (1, {'@': 6}), 83: (1, {'@': 6}), 84: (1, {'@': 6}), 85: (1, {'@': 6}), 11: (1, {'@': 6}), 86: (1, {'@': 6}), 163: (1, {'@': 6}), 45: (1, {'@': 6}), 87: (1, {'@': 6}), 88: (1, {'@': 6}), 89: (1, {'@': 6}), 12: (1, {'@': 6}), 91: (1, {'@': 6}), 92: (1, {'@': 6}), 93: (1, {'@': 6}), 94: (1, {'@': 6}), 95: (1, {'@': 6}), 96: (1, {'@': 6}), 164: (1, {'@': 6}), 165: (1, {'@': 6}), 97: (1, {'@': 6}), 167: (1, {'@': 6}), 98: (1, {'@': 6}), 99: (1, {'@': 6}), 100: (1, {'@': 6}), 101: (1, {'@': 6}), 102: (1, {'@': 6}), 13: (1, {'@': 6}), 103: (1, {'@': 6}), 105: (1, {'@': 6}), 104: (1, {'@': 6}), 106: (1, {'@': 6}), 107: (1, {'@': 6}), 108: (1, {'@': 6}), 109: (1, {'@': 6}), 1: (1, {'@': 6}), 110: (1, {'@': 6}), 111: (1, {'@': 6}), 112: (1, {'@': 6}), 113: (1, {'@': 6}), 114: (1, {'@': 6}), 3: (1, {'@': 6}), 115: (1, {'@': 6}), 116: (1, {'@': 6}), 117: (1, {'@': 6}), 118: (1, {'@': 6}), 119: (1, {'@': 6}), 120: (1, {'@': 6}), 121: (1, {'@': 6}), 122: (1, {'@': 6}), 123: (1, {'@': 6}), 124: (1, {'@': 6}), 195: (1, {'@': 6}), 168: (1, {'@': 6})}, 628: {11: (1, {'@': 54}), 13: (1, {'@': 54}), 12: (1, {'@': 54})}, 629: {43: (1, {'@': 244}), 5: (1, {'@': 244}), 11: (1, {'@': 244}), 12: (1, {'@': 244}), 13: (1, {'@': 244})}, 630: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 218: (0, 787), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 131: (0, 641), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 95: (0, 542), 20: (0, 247), 101: (0, 265), 138: (0, 287), 82: (0, 278), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 108: (0, 487), 139: (0, 505), 22: (0, 11), 42: (0, 233), 110: (0, 450), 41: (0, 273), 21: (0, 305), 219: (0, 653), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 100: (0, 314), 48: (0, 486)}, 631: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 1: (0, 98), 25: (0, 203), 134: (0, 433), 35: (0, 397), 131: (0, 79), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 632: {72: (1, {'@': 12}), 168: (1, {'@': 12}), 98: (1, {'@': 12}), 99: (1, {'@': 12}), 100: (1, {'@': 12}), 101: (1, {'@': 12}), 102: (1, {'@': 12}), 76: (1, {'@': 12}), 13: (1, {'@': 12}), 103: (1, {'@': 12}), 105: (1, {'@': 12}), 104: (1, {'@': 12}), 78: (1, {'@': 12}), 77: (1, {'@': 12}), 106: (1, {'@': 12}), 107: (1, {'@': 12}), 79: (1, {'@': 12}), 108: (1, {'@': 12}), 80: (1, {'@': 12}), 81: (1, {'@': 12}), 82: (1, {'@': 12}), 109: (1, {'@': 12}), 83: (1, {'@': 12}), 1: (1, {'@': 12}), 110: (1, {'@': 12}), 111: (1, {'@': 12}), 84: (1, {'@': 12}), 112: (1, {'@': 12}), 85: (1, {'@': 12}), 11: (1, {'@': 12}), 86: (1, {'@': 12}), 114: (1, {'@': 12}), 87: (1, {'@': 12}), 3: (1, {'@': 12}), 88: (1, {'@': 12}), 89: (1, {'@': 12}), 115: (1, {'@': 12}), 116: (1, {'@': 12}), 117: (1, {'@': 12}), 118: (1, {'@': 12}), 91: (1, {'@': 12}), 119: (1, {'@': 12}), 92: (1, {'@': 12}), 93: (1, {'@': 12}), 94: (1, {'@': 12}), 120: (1, {'@': 12}), 95: (1, {'@': 12}), 96: (1, {'@': 12}), 121: (1, {'@': 12}), 123: (1, {'@': 12}), 97: (1, {'@': 12}), 124: (1, {'@': 12}), 166: (1, {'@': 12}), 44: (1, {'@': 12}), 163: (1, {'@': 12}), 45: (1, {'@': 12}), 12: (1, {'@': 12}), 164: (1, {'@': 12}), 122: (1, {'@': 12}), 165: (1, {'@': 12}), 167: (1, {'@': 12})}, 633: {5: (0, 718), 43: (0, 575), 11: (1, {'@': 96}), 13: (1, {'@': 96}), 12: (1, {'@': 96})}, 634: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 199: (0, 620), 148: (0, 769), 149: (0, 779), 13: (0, 707), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 11: (0, 632), 83: (0, 720), 175: (0, 164), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 200: (0, 731), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 74: (0, 597), 113: (0, 712), 153: (0, 715), 154: (0, 780), 107: (0, 591), 193: (0, 624), 191: (0, 658), 114: (0, 759), 116: (0, 774), 155: (0, 651), 75: (0, 654), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 192: (0, 577), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 201: (0, 618), 117: (0, 626), 202: (0, 625), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 194: (0, 721), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 150: (0, 741), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 168: (0, 231), 229: (0, 238), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 635: {43: (1, {'@': 242}), 5: (1, {'@': 242}), 11: (1, {'@': 242}), 12: (1, {'@': 242}), 13: (1, {'@': 242})}, 636: {11: (1, {'@': 55}), 13: (1, {'@': 55}), 12: (1, {'@': 55})}, 637: {13: (1, {'@': 145})}, 638: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 131: (0, 754), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 639: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 131: (0, 661), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486), 11: (1, {'@': 64}), 13: (1, {'@': 64}), 12: (1, {'@': 64})}, 640: {1: (0, 202)}, 641: {5: (0, 546)}, 642: {72: (1, {'@': 314}), 73: (1, {'@': 314}), 56: (1, {'@': 314}), 74: (1, {'@': 314}), 75: (1, {'@': 314}), 76: (1, {'@': 314}), 77: (1, {'@': 314}), 78: (1, {'@': 314}), 79: (1, {'@': 314}), 80: (1, {'@': 314}), 61: (1, {'@': 314}), 81: (1, {'@': 314}), 82: (1, {'@': 314}), 10: (1, {'@': 314}), 66: (1, {'@': 314}), 83: (1, {'@': 314}), 68: (1, {'@': 314}), 84: (1, {'@': 314}), 69: (1, {'@': 314}), 85: (1, {'@': 314}), 11: (1, {'@': 314}), 86: (1, {'@': 314}), 71: (1, {'@': 314}), 87: (1, {'@': 314}), 88: (1, {'@': 314}), 89: (1, {'@': 314}), 12: (1, {'@': 314}), 90: (1, {'@': 314}), 91: (1, {'@': 314}), 92: (1, {'@': 314}), 93: (1, {'@': 314}), 94: (1, {'@': 314}), 60: (1, {'@': 314}), 63: (1, {'@': 314}), 62: (1, {'@': 314}), 47: (1, {'@': 314}), 95: (1, {'@': 314}), 96: (1, {'@': 314}), 97: (1, {'@': 314}), 4: (1, {'@': 314}), 55: (1, {'@': 314}), 98: (1, {'@': 314}), 99: (1, {'@': 314}), 100: (1, {'@': 314}), 102: (1, {'@': 314}), 43: (1, {'@': 314}), 101: (1, {'@': 314}), 13: (1, {'@': 314}), 103: (1, {'@': 314}), 105: (1, {'@': 314}), 104: (1, {'@': 314}), 106: (1, {'@': 314}), 107: (1, {'@': 314}), 58: (1, {'@': 314}), 108: (1, {'@': 314}), 109: (1, {'@': 314}), 1: (1, {'@': 314}), 110: (1, {'@': 314}), 111: (1, {'@': 314}), 112: (1, {'@': 314}), 70: (1, {'@': 314}), 64: (1, {'@': 314}), 67: (1, {'@': 314}), 113: (1, {'@': 314}), 114: (1, {'@': 314}), 3: (1, {'@': 314}), 9: (1, {'@': 314}), 32: (1, {'@': 314}), 115: (1, {'@': 314}), 116: (1, {'@': 314}), 5: (1, {'@': 314}), 125: (1, {'@': 314}), 57: (1, {'@': 314}), 117: (1, {'@': 314}), 118: (1, {'@': 314}), 119: (1, {'@': 314}), 59: (1, {'@': 314}), 120: (1, {'@': 314}), 65: (1, {'@': 314}), 121: (1, {'@': 314}), 6: (1, {'@': 314}), 54: (1, {'@': 314}), 122: (1, {'@': 314}), 123: (1, {'@': 314}), 124: (1, {'@': 314})}, 643: {72: (1, {'@': 276}), 56: (1, {'@': 276}), 78: (1, {'@': 276}), 80: (1, {'@': 276}), 81: (1, {'@': 276}), 68: (1, {'@': 276}), 84: (1, {'@': 276}), 69: (1, {'@': 276}), 86: (1, {'@': 276}), 71: (1, {'@': 276}), 87: (1, {'@': 276}), 89: (1, {'@': 276}), 12: (1, {'@': 276}), 93: (1, {'@': 276}), 60: (1, {'@': 276}), 63: (1, {'@': 276}), 62: (1, {'@': 276}), 97: (1, {'@': 276}), 99: (1, {'@': 276}), 100: (1, {'@': 276}), 102: (1, {'@': 276}), 43: (1, {'@': 276}), 103: (1, {'@': 276}), 105: (1, {'@': 276}), 107: (1, {'@': 276}), 108: (1, {'@': 276}), 112: (1, {'@': 276}), 70: (1, {'@': 276}), 64: (1, {'@': 276}), 67: (1, {'@': 276}), 3: (1, {'@': 276}), 9: (1, {'@': 276}), 115: (1, {'@': 276}), 116: (1, {'@': 276}), 5: (1, {'@': 276}), 125: (1, {'@': 276}), 59: (1, {'@': 276}), 120: (1, {'@': 276}), 65: (1, {'@': 276}), 6: (1, {'@': 276}), 54: (1, {'@': 276}), 123: (1, {'@': 276}), 73: (1, {'@': 276}), 74: (1, {'@': 276}), 75: (1, {'@': 276}), 76: (1, {'@': 276}), 77: (1, {'@': 276}), 79: (1, {'@': 276}), 61: (1, {'@': 276}), 82: (1, {'@': 276}), 10: (1, {'@': 276}), 66: (1, {'@': 276}), 83: (1, {'@': 276}), 85: (1, {'@': 276}), 11: (1, {'@': 276}), 88: (1, {'@': 276}), 90: (1, {'@': 276}), 91: (1, {'@': 276}), 92: (1, {'@': 276}), 94: (1, {'@': 276}), 47: (1, {'@': 276}), 95: (1, {'@': 276}), 96: (1, {'@': 276}), 4: (1, {'@': 276}), 55: (1, {'@': 276}), 98: (1, {'@': 276}), 101: (1, {'@': 276}), 13: (1, {'@': 276}), 104: (1, {'@': 276}), 106: (1, {'@': 276}), 58: (1, {'@': 276}), 109: (1, {'@': 276}), 1: (1, {'@': 276}), 110: (1, {'@': 276}), 111: (1, {'@': 276}), 113: (1, {'@': 276}), 114: (1, {'@': 276}), 32: (1, {'@': 276}), 57: (1, {'@': 276}), 117: (1, {'@': 276}), 118: (1, {'@': 276}), 119: (1, {'@': 276}), 121: (1, {'@': 276}), 122: (1, {'@': 276}), 124: (1, {'@': 276})}, 644: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 131: (0, 681), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 645: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 193: (0, 775), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 11: (0, 632), 83: (0, 720), 95: (0, 638), 192: (0, 514), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 13: (0, 611), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 191: (0, 570), 114: (0, 759), 116: (0, 774), 155: (0, 651), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 194: (0, 565), 106: (0, 762), 108: (0, 518), 119: (0, 674), 150: (0, 741), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 646: {11: (1, {'@': 111}), 13: (1, {'@': 111}), 12: (1, {'@': 111})}, 647: {72: (1, {'@': 7}), 74: (1, {'@': 7}), 75: (1, {'@': 7}), 76: (1, {'@': 7}), 77: (1, {'@': 7}), 78: (1, {'@': 7}), 166: (1, {'@': 7}), 79: (1, {'@': 7}), 80: (1, {'@': 7}), 81: (1, {'@': 7}), 82: (1, {'@': 7}), 44: (1, {'@': 7}), 83: (1, {'@': 7}), 84: (1, {'@': 7}), 85: (1, {'@': 7}), 11: (1, {'@': 7}), 86: (1, {'@': 7}), 163: (1, {'@': 7}), 45: (1, {'@': 7}), 87: (1, {'@': 7}), 88: (1, {'@': 7}), 89: (1, {'@': 7}), 12: (1, {'@': 7}), 91: (1, {'@': 7}), 92: (1, {'@': 7}), 93: (1, {'@': 7}), 94: (1, {'@': 7}), 95: (1, {'@': 7}), 96: (1, {'@': 7}), 164: (1, {'@': 7}), 165: (1, {'@': 7}), 97: (1, {'@': 7}), 167: (1, {'@': 7}), 98: (1, {'@': 7}), 99: (1, {'@': 7}), 100: (1, {'@': 7}), 101: (1, {'@': 7}), 102: (1, {'@': 7}), 13: (1, {'@': 7}), 103: (1, {'@': 7}), 105: (1, {'@': 7}), 104: (1, {'@': 7}), 106: (1, {'@': 7}), 107: (1, {'@': 7}), 108: (1, {'@': 7}), 109: (1, {'@': 7}), 1: (1, {'@': 7}), 110: (1, {'@': 7}), 111: (1, {'@': 7}), 112: (1, {'@': 7}), 113: (1, {'@': 7}), 114: (1, {'@': 7}), 3: (1, {'@': 7}), 115: (1, {'@': 7}), 116: (1, {'@': 7}), 117: (1, {'@': 7}), 118: (1, {'@': 7}), 119: (1, {'@': 7}), 120: (1, {'@': 7}), 121: (1, {'@': 7}), 122: (1, {'@': 7}), 123: (1, {'@': 7}), 124: (1, {'@': 7}), 195: (1, {'@': 7}), 168: (1, {'@': 7})}, 648: {11: (1, {'@': 348}), 12: (1, {'@': 348}), 13: (1, {'@': 348})}, 649: {11: (1, {'@': 118}), 13: (1, {'@': 118}), 12: (1, {'@': 118})}, 650: {54: (0, 166), 124: (0, 53)}, 651: {9: (0, 19), 230: (0, 352), 7: (1, {'@': 332}), 11: (1, {'@': 332}), 12: (1, {'@': 332}), 13: (1, {'@': 332})}, 652: {1: (0, 306), 3: (0, 172)}, 653: {43: (0, 745)}, 654: {209: (0, 616)}, 655: {72: (1, {'@': 226}), 74: (1, {'@': 226}), 98: (1, {'@': 226}), 75: (1, {'@': 226}), 99: (1, {'@': 226}), 100: (1, {'@': 226}), 101: (1, {'@': 226}), 102: (1, {'@': 226}), 76: (1, {'@': 226}), 13: (1, {'@': 226}), 103: (1, {'@': 226}), 104: (1, {'@': 226}), 105: (1, {'@': 226}), 78: (1, {'@': 226}), 77: (1, {'@': 226}), 106: (1, {'@': 226}), 107: (1, {'@': 226}), 79: (1, {'@': 226}), 108: (1, {'@': 226}), 80: (1, {'@': 226}), 81: (1, {'@': 226}), 82: (1, {'@': 226}), 109: (1, {'@': 226}), 83: (1, {'@': 226}), 1: (1, {'@': 226}), 110: (1, {'@': 226}), 111: (1, {'@': 226}), 84: (1, {'@': 226}), 112: (1, {'@': 226}), 85: (1, {'@': 226}), 11: (1, {'@': 226}), 86: (1, {'@': 226}), 113: (1, {'@': 226}), 114: (1, {'@': 226}), 87: (1, {'@': 226}), 3: (1, {'@': 226}), 88: (1, {'@': 226}), 89: (1, {'@': 226}), 115: (1, {'@': 226}), 116: (1, {'@': 226}), 117: (1, {'@': 226}), 118: (1, {'@': 226}), 91: (1, {'@': 226}), 119: (1, {'@': 226}), 92: (1, {'@': 226}), 93: (1, {'@': 226}), 94: (1, {'@': 226}), 120: (1, {'@': 226}), 95: (1, {'@': 226}), 96: (1, {'@': 226}), 121: (1, {'@': 226}), 122: (1, {'@': 226}), 123: (1, {'@': 226}), 97: (1, {'@': 226}), 124: (1, {'@': 226})}, 656: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 131: (0, 117), 176: (0, 122), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 93), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 178: (0, 31), 179: (0, 9), 180: (0, 466), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 181: (0, 334), 20: (0, 247), 138: (0, 287), 182: (0, 248), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 183: (0, 443), 139: (0, 505), 22: (0, 11), 42: (0, 233), 184: (0, 313), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 177: (0, 143), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 657: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 13: (0, 707), 199: (0, 620), 105: (0, 588), 149: (0, 779), 148: (0, 769), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 11: (0, 632), 175: (0, 593), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 92: (0, 604), 200: (0, 731), 101: (0, 699), 100: (0, 685), 152: (0, 536), 74: (0, 597), 113: (0, 712), 153: (0, 715), 154: (0, 780), 107: (0, 591), 193: (0, 624), 191: (0, 757), 114: (0, 759), 116: (0, 774), 155: (0, 651), 75: (0, 654), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 201: (0, 618), 117: (0, 626), 202: (0, 637), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 192: (0, 686), 194: (0, 721), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 150: (0, 741), 111: (0, 783), 161: (0, 729), 160: (0, 746), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 195: (1, {'@': 0})}, 658: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 150: (0, 81), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 156: (0, 747), 11: (0, 73), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 13: (0, 627), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459), 120: (1, {'@': 152}), 165: (1, {'@': 152}), 164: (1, {'@': 152}), 122: (1, {'@': 152}), 12: (1, {'@': 152}), 166: (1, {'@': 152}), 45: (1, {'@': 152}), 44: (1, {'@': 152}), 168: (1, {'@': 152}), 163: (1, {'@': 152}), 167: (1, {'@': 152})}, 659: {5: (1, {'@': 234}), 11: (1, {'@': 234}), 12: (1, {'@': 234}), 43: (1, {'@': 234}), 13: (1, {'@': 234})}, 660: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 131: (0, 508), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 661: {11: (1, {'@': 63}), 13: (1, {'@': 63}), 12: (1, {'@': 63})}, 662: {11: (1, {'@': 349}), 12: (1, {'@': 349}), 13: (1, {'@': 349})}, 663: {164: (0, 399), 166: (0, 363), 12: (0, 471), 165: (0, 348)}, 664: {47: (0, 607), 66: (0, 515), 55: (1, {'@': 405}), 56: (1, {'@': 405}), 57: (1, {'@': 405}), 58: (1, {'@': 405}), 59: (1, {'@': 405}), 60: (1, {'@': 405}), 64: (1, {'@': 405}), 62: (1, {'@': 405}), 63: (1, {'@': 405}), 65: (1, {'@': 405}), 68: (1, {'@': 405}), 54: (1, {'@': 405}), 69: (1, {'@': 405}), 67: (1, {'@': 405}), 4: (1, {'@': 405}), 12: (1, {'@': 405}), 13: (1, {'@': 405}), 11: (1, {'@': 405}), 72: (1, {'@': 405}), 73: (1, {'@': 405}), 74: (1, {'@': 405}), 75: (1, {'@': 405}), 76: (1, {'@': 405}), 77: (1, {'@': 405}), 78: (1, {'@': 405}), 79: (1, {'@': 405}), 80: (1, {'@': 405}), 81: (1, {'@': 405}), 10: (1, {'@': 405}), 82: (1, {'@': 405}), 83: (1, {'@': 405}), 84: (1, {'@': 405}), 85: (1, {'@': 405}), 86: (1, {'@': 405}), 87: (1, {'@': 405}), 88: (1, {'@': 405}), 89: (1, {'@': 405}), 90: (1, {'@': 405}), 91: (1, {'@': 405}), 92: (1, {'@': 405}), 93: (1, {'@': 405}), 94: (1, {'@': 405}), 95: (1, {'@': 405}), 96: (1, {'@': 405}), 97: (1, {'@': 405}), 98: (1, {'@': 405}), 99: (1, {'@': 405}), 100: (1, {'@': 405}), 101: (1, {'@': 405}), 102: (1, {'@': 405}), 43: (1, {'@': 405}), 103: (1, {'@': 405}), 104: (1, {'@': 405}), 105: (1, {'@': 405}), 106: (1, {'@': 405}), 107: (1, {'@': 405}), 108: (1, {'@': 405}), 109: (1, {'@': 405}), 1: (1, {'@': 405}), 110: (1, {'@': 405}), 111: (1, {'@': 405}), 112: (1, {'@': 405}), 113: (1, {'@': 405}), 114: (1, {'@': 405}), 3: (1, {'@': 405}), 32: (1, {'@': 405}), 115: (1, {'@': 405}), 5: (1, {'@': 405}), 116: (1, {'@': 405}), 117: (1, {'@': 405}), 118: (1, {'@': 405}), 119: (1, {'@': 405}), 120: (1, {'@': 405}), 121: (1, {'@': 405}), 6: (1, {'@': 405}), 122: (1, {'@': 405}), 123: (1, {'@': 405}), 124: (1, {'@': 405})}, 665: {72: (1, {'@': 292}), 56: (1, {'@': 292}), 78: (1, {'@': 292}), 80: (1, {'@': 292}), 81: (1, {'@': 292}), 68: (1, {'@': 292}), 84: (1, {'@': 292}), 69: (1, {'@': 292}), 86: (1, {'@': 292}), 71: (1, {'@': 292}), 87: (1, {'@': 292}), 89: (1, {'@': 292}), 12: (1, {'@': 292}), 93: (1, {'@': 292}), 60: (1, {'@': 292}), 63: (1, {'@': 292}), 62: (1, {'@': 292}), 97: (1, {'@': 292}), 99: (1, {'@': 292}), 100: (1, {'@': 292}), 102: (1, {'@': 292}), 43: (1, {'@': 292}), 103: (1, {'@': 292}), 105: (1, {'@': 292}), 107: (1, {'@': 292}), 108: (1, {'@': 292}), 112: (1, {'@': 292}), 70: (1, {'@': 292}), 64: (1, {'@': 292}), 67: (1, {'@': 292}), 3: (1, {'@': 292}), 9: (1, {'@': 292}), 115: (1, {'@': 292}), 116: (1, {'@': 292}), 5: (1, {'@': 292}), 125: (1, {'@': 292}), 59: (1, {'@': 292}), 120: (1, {'@': 292}), 65: (1, {'@': 292}), 6: (1, {'@': 292}), 54: (1, {'@': 292}), 123: (1, {'@': 292}), 73: (1, {'@': 292}), 74: (1, {'@': 292}), 75: (1, {'@': 292}), 76: (1, {'@': 292}), 77: (1, {'@': 292}), 79: (1, {'@': 292}), 61: (1, {'@': 292}), 82: (1, {'@': 292}), 10: (1, {'@': 292}), 66: (1, {'@': 292}), 83: (1, {'@': 292}), 85: (1, {'@': 292}), 11: (1, {'@': 292}), 88: (1, {'@': 292}), 90: (1, {'@': 292}), 91: (1, {'@': 292}), 92: (1, {'@': 292}), 94: (1, {'@': 292}), 47: (1, {'@': 292}), 95: (1, {'@': 292}), 96: (1, {'@': 292}), 4: (1, {'@': 292}), 55: (1, {'@': 292}), 98: (1, {'@': 292}), 101: (1, {'@': 292}), 13: (1, {'@': 292}), 104: (1, {'@': 292}), 106: (1, {'@': 292}), 58: (1, {'@': 292}), 109: (1, {'@': 292}), 1: (1, {'@': 292}), 110: (1, {'@': 292}), 111: (1, {'@': 292}), 113: (1, {'@': 292}), 114: (1, {'@': 292}), 32: (1, {'@': 292}), 57: (1, {'@': 292}), 117: (1, {'@': 292}), 118: (1, {'@': 292}), 119: (1, {'@': 292}), 121: (1, {'@': 292}), 122: (1, {'@': 292}), 124: (1, {'@': 292})}, 666: {72: (1, {'@': 232}), 74: (1, {'@': 232}), 98: (1, {'@': 232}), 75: (1, {'@': 232}), 99: (1, {'@': 232}), 100: (1, {'@': 232}), 101: (1, {'@': 232}), 102: (1, {'@': 232}), 76: (1, {'@': 232}), 13: (1, {'@': 232}), 103: (1, {'@': 232}), 104: (1, {'@': 232}), 105: (1, {'@': 232}), 78: (1, {'@': 232}), 77: (1, {'@': 232}), 106: (1, {'@': 232}), 107: (1, {'@': 232}), 79: (1, {'@': 232}), 108: (1, {'@': 232}), 80: (1, {'@': 232}), 81: (1, {'@': 232}), 82: (1, {'@': 232}), 109: (1, {'@': 232}), 83: (1, {'@': 232}), 1: (1, {'@': 232}), 110: (1, {'@': 232}), 111: (1, {'@': 232}), 84: (1, {'@': 232}), 112: (1, {'@': 232}), 85: (1, {'@': 232}), 11: (1, {'@': 232}), 86: (1, {'@': 232}), 113: (1, {'@': 232}), 114: (1, {'@': 232}), 87: (1, {'@': 232}), 3: (1, {'@': 232}), 88: (1, {'@': 232}), 89: (1, {'@': 232}), 115: (1, {'@': 232}), 116: (1, {'@': 232}), 117: (1, {'@': 232}), 118: (1, {'@': 232}), 91: (1, {'@': 232}), 119: (1, {'@': 232}), 92: (1, {'@': 232}), 93: (1, {'@': 232}), 94: (1, {'@': 232}), 120: (1, {'@': 232}), 95: (1, {'@': 232}), 96: (1, {'@': 232}), 121: (1, {'@': 232}), 123: (1, {'@': 232}), 97: (1, {'@': 232}), 124: (1, {'@': 232})}, 667: {11: (1, {'@': 77}), 13: (1, {'@': 77}), 12: (1, {'@': 77})}, 668: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 135: (0, 483), 33: (0, 356), 129: (0, 497), 136: (0, 403), 23: (0, 219), 29: (0, 474), 130: (0, 169), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 133: (0, 78), 41: (0, 273), 27: (0, 462), 28: (0, 42), 21: (0, 305), 49: (0, 373), 46: (0, 445), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 669: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 213: (0, 554), 84: (0, 742), 104: (0, 735), 147: (0, 580), 13: (0, 707), 199: (0, 620), 105: (0, 588), 149: (0, 779), 148: (0, 769), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 11: (0, 632), 175: (0, 525), 188: (0, 592), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 208: (0, 244), 86: (0, 656), 151: (0, 717), 72: (0, 555), 92: (0, 604), 101: (0, 699), 100: (0, 685), 152: (0, 536), 192: (0, 567), 74: (0, 597), 113: (0, 712), 153: (0, 715), 154: (0, 780), 107: (0, 591), 193: (0, 624), 191: (0, 557), 114: (0, 759), 185: (0, 547), 200: (0, 750), 116: (0, 774), 75: (0, 654), 156: (0, 747), 155: (0, 651), 165: (0, 586), 93: (0, 630), 164: (0, 531), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 166: (0, 532), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 201: (0, 618), 117: (0, 626), 202: (0, 637), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 194: (0, 721), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 150: (0, 741), 111: (0, 783), 161: (0, 729), 160: (0, 746), 87: (0, 719), 115: (0, 710), 85: (0, 680), 189: (0, 22), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 670: {155: (0, 651), 78: (0, 511), 156: (0, 279), 124: (0, 523)}, 671: {13: (1, {'@': 364})}, 672: {43: (1, {'@': 243}), 5: (1, {'@': 243}), 11: (1, {'@': 243}), 12: (1, {'@': 243}), 13: (1, {'@': 243})}, 673: {31: (0, 3), 25: (0, 203), 126: (0, 96), 22: (0, 11), 50: (0, 125), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 19: (0, 150), 1: (0, 324), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 135: (0, 483), 33: (0, 356), 129: (0, 497), 136: (0, 403), 23: (0, 219), 29: (0, 474), 130: (0, 169), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 138: (0, 293), 133: (0, 160), 41: (0, 273), 27: (0, 462), 28: (0, 42), 21: (0, 305), 49: (0, 373), 46: (0, 445), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 674: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 521), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 196: (0, 782), 197: (0, 708), 1: (0, 614), 25: (0, 203), 134: (0, 433), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 66), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 675: {55: (1, {'@': 412}), 56: (1, {'@': 412}), 57: (1, {'@': 412}), 58: (1, {'@': 412}), 59: (1, {'@': 412}), 60: (1, {'@': 412}), 61: (1, {'@': 412}), 47: (1, {'@': 412}), 62: (1, {'@': 412}), 63: (1, {'@': 412}), 64: (1, {'@': 412}), 65: (1, {'@': 412}), 66: (1, {'@': 412}), 67: (1, {'@': 412}), 68: (1, {'@': 412}), 54: (1, {'@': 412}), 69: (1, {'@': 412}), 70: (1, {'@': 412}), 4: (1, {'@': 412}), 71: (1, {'@': 412}), 12: (1, {'@': 412}), 13: (1, {'@': 412}), 11: (1, {'@': 412}), 72: (1, {'@': 412}), 73: (1, {'@': 412}), 74: (1, {'@': 412}), 75: (1, {'@': 412}), 76: (1, {'@': 412}), 77: (1, {'@': 412}), 78: (1, {'@': 412}), 79: (1, {'@': 412}), 80: (1, {'@': 412}), 81: (1, {'@': 412}), 10: (1, {'@': 412}), 82: (1, {'@': 412}), 83: (1, {'@': 412}), 84: (1, {'@': 412}), 85: (1, {'@': 412}), 86: (1, {'@': 412}), 87: (1, {'@': 412}), 88: (1, {'@': 412}), 89: (1, {'@': 412}), 90: (1, {'@': 412}), 91: (1, {'@': 412}), 92: (1, {'@': 412}), 93: (1, {'@': 412}), 94: (1, {'@': 412}), 95: (1, {'@': 412}), 96: (1, {'@': 412}), 97: (1, {'@': 412}), 98: (1, {'@': 412}), 99: (1, {'@': 412}), 100: (1, {'@': 412}), 101: (1, {'@': 412}), 102: (1, {'@': 412}), 43: (1, {'@': 412}), 103: (1, {'@': 412}), 104: (1, {'@': 412}), 105: (1, {'@': 412}), 106: (1, {'@': 412}), 107: (1, {'@': 412}), 108: (1, {'@': 412}), 109: (1, {'@': 412}), 1: (1, {'@': 412}), 110: (1, {'@': 412}), 111: (1, {'@': 412}), 112: (1, {'@': 412}), 113: (1, {'@': 412}), 114: (1, {'@': 412}), 3: (1, {'@': 412}), 32: (1, {'@': 412}), 115: (1, {'@': 412}), 5: (1, {'@': 412}), 116: (1, {'@': 412}), 117: (1, {'@': 412}), 118: (1, {'@': 412}), 119: (1, {'@': 412}), 120: (1, {'@': 412}), 121: (1, {'@': 412}), 6: (1, {'@': 412}), 122: (1, {'@': 412}), 123: (1, {'@': 412}), 124: (1, {'@': 412})}, 676: {31: (0, 3), 25: (0, 203), 126: (0, 138), 22: (0, 11), 50: (0, 125), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 19: (0, 150), 1: (0, 324), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 135: (0, 483), 33: (0, 356), 129: (0, 497), 136: (0, 403), 23: (0, 219), 29: (0, 474), 130: (0, 169), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 133: (0, 160), 41: (0, 273), 27: (0, 462), 28: (0, 42), 21: (0, 305), 49: (0, 373), 46: (0, 445), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 677: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 521), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 196: (0, 180), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 197: (0, 708), 25: (0, 203), 1: (0, 614), 134: (0, 433), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 10: (0, 209), 20: (0, 247), 138: (0, 287), 131: (0, 331), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 678: {1: (0, 70)}, 679: {72: (1, {'@': 214}), 76: (1, {'@': 214}), 77: (1, {'@': 214}), 78: (1, {'@': 214}), 79: (1, {'@': 214}), 80: (1, {'@': 214}), 81: (1, {'@': 214}), 82: (1, {'@': 214}), 83: (1, {'@': 214}), 84: (1, {'@': 214}), 85: (1, {'@': 214}), 11: (1, {'@': 214}), 86: (1, {'@': 214}), 87: (1, {'@': 214}), 88: (1, {'@': 214}), 89: (1, {'@': 214}), 91: (1, {'@': 214}), 92: (1, {'@': 214}), 93: (1, {'@': 214}), 94: (1, {'@': 214}), 95: (1, {'@': 214}), 96: (1, {'@': 214}), 97: (1, {'@': 214}), 98: (1, {'@': 214}), 99: (1, {'@': 214}), 100: (1, {'@': 214}), 101: (1, {'@': 214}), 102: (1, {'@': 214}), 13: (1, {'@': 214}), 103: (1, {'@': 214}), 104: (1, {'@': 214}), 105: (1, {'@': 214}), 106: (1, {'@': 214}), 107: (1, {'@': 214}), 108: (1, {'@': 214}), 109: (1, {'@': 214}), 1: (1, {'@': 214}), 110: (1, {'@': 214}), 111: (1, {'@': 214}), 112: (1, {'@': 214}), 114: (1, {'@': 214}), 3: (1, {'@': 214}), 115: (1, {'@': 214}), 116: (1, {'@': 214}), 117: (1, {'@': 214}), 118: (1, {'@': 214}), 119: (1, {'@': 214}), 121: (1, {'@': 214}), 123: (1, {'@': 214}), 124: (1, {'@': 214}), 74: (1, {'@': 214}), 75: (1, {'@': 214}), 113: (1, {'@': 214}), 120: (1, {'@': 214})}, 680: {1: (0, 185), 228: (0, 319), 2: (0, 242), 3: (0, 264)}, 681: {11: (1, {'@': 109}), 13: (1, {'@': 109}), 12: (1, {'@': 109})}, 682: {72: (1, {'@': 227}), 74: (1, {'@': 227}), 98: (1, {'@': 227}), 75: (1, {'@': 227}), 99: (1, {'@': 227}), 100: (1, {'@': 227}), 101: (1, {'@': 227}), 102: (1, {'@': 227}), 76: (1, {'@': 227}), 13: (1, {'@': 227}), 103: (1, {'@': 227}), 104: (1, {'@': 227}), 105: (1, {'@': 227}), 78: (1, {'@': 227}), 77: (1, {'@': 227}), 106: (1, {'@': 227}), 107: (1, {'@': 227}), 79: (1, {'@': 227}), 108: (1, {'@': 227}), 80: (1, {'@': 227}), 81: (1, {'@': 227}), 82: (1, {'@': 227}), 109: (1, {'@': 227}), 83: (1, {'@': 227}), 1: (1, {'@': 227}), 110: (1, {'@': 227}), 111: (1, {'@': 227}), 84: (1, {'@': 227}), 112: (1, {'@': 227}), 85: (1, {'@': 227}), 11: (1, {'@': 227}), 86: (1, {'@': 227}), 113: (1, {'@': 227}), 114: (1, {'@': 227}), 87: (1, {'@': 227}), 3: (1, {'@': 227}), 88: (1, {'@': 227}), 89: (1, {'@': 227}), 115: (1, {'@': 227}), 116: (1, {'@': 227}), 117: (1, {'@': 227}), 118: (1, {'@': 227}), 91: (1, {'@': 227}), 119: (1, {'@': 227}), 92: (1, {'@': 227}), 93: (1, {'@': 227}), 94: (1, {'@': 227}), 120: (1, {'@': 227}), 95: (1, {'@': 227}), 96: (1, {'@': 227}), 121: (1, {'@': 227}), 122: (1, {'@': 227}), 123: (1, {'@': 227}), 97: (1, {'@': 227}), 124: (1, {'@': 227})}, 683: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 175: (0, 94), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 156: (0, 747), 93: (0, 630), 13: (0, 599), 98: (0, 544), 11: (0, 602), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 150: (0, 705), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 168: (0, 231), 229: (0, 63), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 684: {11: (1, {'@': 117}), 13: (1, {'@': 117}), 12: (1, {'@': 117})}, 685: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 131: (0, 146), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 686: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 156: (0, 747), 93: (0, 630), 13: (0, 599), 98: (0, 544), 11: (0, 602), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 150: (0, 705), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 687: {72: (1, {'@': 297}), 56: (1, {'@': 297}), 78: (1, {'@': 297}), 80: (1, {'@': 297}), 81: (1, {'@': 297}), 68: (1, {'@': 297}), 84: (1, {'@': 297}), 69: (1, {'@': 297}), 86: (1, {'@': 297}), 71: (1, {'@': 297}), 87: (1, {'@': 297}), 89: (1, {'@': 297}), 12: (1, {'@': 297}), 93: (1, {'@': 297}), 60: (1, {'@': 297}), 63: (1, {'@': 297}), 62: (1, {'@': 297}), 97: (1, {'@': 297}), 99: (1, {'@': 297}), 100: (1, {'@': 297}), 102: (1, {'@': 297}), 43: (1, {'@': 297}), 103: (1, {'@': 297}), 105: (1, {'@': 297}), 107: (1, {'@': 297}), 108: (1, {'@': 297}), 112: (1, {'@': 297}), 70: (1, {'@': 297}), 64: (1, {'@': 297}), 67: (1, {'@': 297}), 3: (1, {'@': 297}), 9: (1, {'@': 297}), 115: (1, {'@': 297}), 116: (1, {'@': 297}), 5: (1, {'@': 297}), 125: (1, {'@': 297}), 59: (1, {'@': 297}), 120: (1, {'@': 297}), 65: (1, {'@': 297}), 6: (1, {'@': 297}), 54: (1, {'@': 297}), 123: (1, {'@': 297}), 73: (1, {'@': 297}), 74: (1, {'@': 297}), 75: (1, {'@': 297}), 76: (1, {'@': 297}), 77: (1, {'@': 297}), 79: (1, {'@': 297}), 61: (1, {'@': 297}), 82: (1, {'@': 297}), 10: (1, {'@': 297}), 66: (1, {'@': 297}), 83: (1, {'@': 297}), 85: (1, {'@': 297}), 11: (1, {'@': 297}), 88: (1, {'@': 297}), 90: (1, {'@': 297}), 91: (1, {'@': 297}), 92: (1, {'@': 297}), 94: (1, {'@': 297}), 47: (1, {'@': 297}), 95: (1, {'@': 297}), 96: (1, {'@': 297}), 4: (1, {'@': 297}), 55: (1, {'@': 297}), 98: (1, {'@': 297}), 101: (1, {'@': 297}), 13: (1, {'@': 297}), 104: (1, {'@': 297}), 106: (1, {'@': 297}), 58: (1, {'@': 297}), 109: (1, {'@': 297}), 1: (1, {'@': 297}), 110: (1, {'@': 297}), 111: (1, {'@': 297}), 113: (1, {'@': 297}), 114: (1, {'@': 297}), 32: (1, {'@': 297}), 57: (1, {'@': 297}), 117: (1, {'@': 297}), 118: (1, {'@': 297}), 119: (1, {'@': 297}), 121: (1, {'@': 297}), 122: (1, {'@': 297}), 124: (1, {'@': 297})}, 688: {172: (0, 569), 31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 521), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 9: (0, 260), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 196: (0, 136), 197: (0, 708), 1: (0, 614), 25: (0, 203), 134: (0, 433), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 171: (0, 227), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 4: (0, 131), 42: (0, 233), 131: (0, 66), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486), 11: (1, {'@': 36}), 13: (1, {'@': 36}), 12: (1, {'@': 36})}, 689: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 95: (0, 542), 20: (0, 247), 101: (0, 265), 219: (0, 86), 138: (0, 287), 82: (0, 278), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 108: (0, 487), 139: (0, 505), 22: (0, 11), 42: (0, 233), 110: (0, 450), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 100: (0, 314), 131: (0, 34), 48: (0, 486)}, 690: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 176: (0, 122), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 178: (0, 31), 179: (0, 9), 180: (0, 466), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 181: (0, 334), 20: (0, 247), 138: (0, 287), 131: (0, 613), 182: (0, 248), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 183: (0, 443), 139: (0, 505), 22: (0, 11), 42: (0, 233), 184: (0, 313), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486), 177: (0, 106)}, 691: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 382), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 692: {11: (1, {'@': 18}), 13: (1, {'@': 18}), 12: (1, {'@': 18})}, 693: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 135: (0, 483), 33: (0, 356), 129: (0, 497), 136: (0, 403), 23: (0, 219), 29: (0, 474), 130: (0, 169), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 133: (0, 193), 41: (0, 273), 27: (0, 462), 28: (0, 42), 21: (0, 305), 49: (0, 373), 46: (0, 445), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 694: {11: (1, {'@': 100}), 13: (1, {'@': 100}), 12: (1, {'@': 100})}, 695: {72: (1, {'@': 316}), 56: (1, {'@': 316}), 78: (1, {'@': 316}), 80: (1, {'@': 316}), 81: (1, {'@': 316}), 68: (1, {'@': 316}), 84: (1, {'@': 316}), 69: (1, {'@': 316}), 86: (1, {'@': 316}), 71: (1, {'@': 316}), 87: (1, {'@': 316}), 89: (1, {'@': 316}), 12: (1, {'@': 316}), 93: (1, {'@': 316}), 60: (1, {'@': 316}), 63: (1, {'@': 316}), 62: (1, {'@': 316}), 97: (1, {'@': 316}), 99: (1, {'@': 316}), 100: (1, {'@': 316}), 102: (1, {'@': 316}), 43: (1, {'@': 316}), 103: (1, {'@': 316}), 105: (1, {'@': 316}), 107: (1, {'@': 316}), 108: (1, {'@': 316}), 112: (1, {'@': 316}), 67: (1, {'@': 316}), 70: (1, {'@': 316}), 64: (1, {'@': 316}), 3: (1, {'@': 316}), 9: (1, {'@': 316}), 115: (1, {'@': 316}), 5: (1, {'@': 316}), 116: (1, {'@': 316}), 125: (1, {'@': 316}), 59: (1, {'@': 316}), 120: (1, {'@': 316}), 65: (1, {'@': 316}), 6: (1, {'@': 316}), 54: (1, {'@': 316}), 123: (1, {'@': 316}), 73: (1, {'@': 316}), 74: (1, {'@': 316}), 75: (1, {'@': 316}), 76: (1, {'@': 316}), 77: (1, {'@': 316}), 79: (1, {'@': 316}), 61: (1, {'@': 316}), 10: (1, {'@': 316}), 82: (1, {'@': 316}), 66: (1, {'@': 316}), 83: (1, {'@': 316}), 85: (1, {'@': 316}), 11: (1, {'@': 316}), 88: (1, {'@': 316}), 90: (1, {'@': 316}), 91: (1, {'@': 316}), 92: (1, {'@': 316}), 94: (1, {'@': 316}), 47: (1, {'@': 316}), 95: (1, {'@': 316}), 96: (1, {'@': 316}), 4: (1, {'@': 316}), 55: (1, {'@': 316}), 98: (1, {'@': 316}), 101: (1, {'@': 316}), 13: (1, {'@': 316}), 104: (1, {'@': 316}), 106: (1, {'@': 316}), 58: (1, {'@': 316}), 109: (1, {'@': 316}), 1: (1, {'@': 316}), 110: (1, {'@': 316}), 111: (1, {'@': 316}), 113: (1, {'@': 316}), 114: (1, {'@': 316}), 32: (1, {'@': 316}), 57: (1, {'@': 316}), 117: (1, {'@': 316}), 118: (1, {'@': 316}), 119: (1, {'@': 316}), 121: (1, {'@': 316}), 122: (1, {'@': 316}), 124: (1, {'@': 316})}, 696: {72: (1, {'@': 307}), 56: (1, {'@': 307}), 78: (1, {'@': 307}), 80: (1, {'@': 307}), 81: (1, {'@': 307}), 68: (1, {'@': 307}), 84: (1, {'@': 307}), 69: (1, {'@': 307}), 86: (1, {'@': 307}), 71: (1, {'@': 307}), 87: (1, {'@': 307}), 89: (1, {'@': 307}), 12: (1, {'@': 307}), 93: (1, {'@': 307}), 60: (1, {'@': 307}), 63: (1, {'@': 307}), 62: (1, {'@': 307}), 97: (1, {'@': 307}), 99: (1, {'@': 307}), 100: (1, {'@': 307}), 102: (1, {'@': 307}), 43: (1, {'@': 307}), 103: (1, {'@': 307}), 105: (1, {'@': 307}), 107: (1, {'@': 307}), 108: (1, {'@': 307}), 112: (1, {'@': 307}), 70: (1, {'@': 307}), 64: (1, {'@': 307}), 67: (1, {'@': 307}), 3: (1, {'@': 307}), 9: (1, {'@': 307}), 115: (1, {'@': 307}), 116: (1, {'@': 307}), 5: (1, {'@': 307}), 125: (1, {'@': 307}), 59: (1, {'@': 307}), 120: (1, {'@': 307}), 65: (1, {'@': 307}), 6: (1, {'@': 307}), 54: (1, {'@': 307}), 123: (1, {'@': 307}), 73: (1, {'@': 307}), 74: (1, {'@': 307}), 75: (1, {'@': 307}), 76: (1, {'@': 307}), 77: (1, {'@': 307}), 79: (1, {'@': 307}), 61: (1, {'@': 307}), 82: (1, {'@': 307}), 10: (1, {'@': 307}), 66: (1, {'@': 307}), 83: (1, {'@': 307}), 85: (1, {'@': 307}), 11: (1, {'@': 307}), 88: (1, {'@': 307}), 90: (1, {'@': 307}), 91: (1, {'@': 307}), 92: (1, {'@': 307}), 94: (1, {'@': 307}), 47: (1, {'@': 307}), 95: (1, {'@': 307}), 96: (1, {'@': 307}), 4: (1, {'@': 307}), 55: (1, {'@': 307}), 98: (1, {'@': 307}), 101: (1, {'@': 307}), 13: (1, {'@': 307}), 104: (1, {'@': 307}), 106: (1, {'@': 307}), 58: (1, {'@': 307}), 109: (1, {'@': 307}), 1: (1, {'@': 307}), 110: (1, {'@': 307}), 111: (1, {'@': 307}), 113: (1, {'@': 307}), 114: (1, {'@': 307}), 32: (1, {'@': 307}), 57: (1, {'@': 307}), 117: (1, {'@': 307}), 118: (1, {'@': 307}), 119: (1, {'@': 307}), 121: (1, {'@': 307}), 122: (1, {'@': 307}), 124: (1, {'@': 307})}, 697: {11: (1, {'@': 112}), 13: (1, {'@': 112}), 12: (1, {'@': 112})}, 698: {5: (0, 343), 8: (0, 249), 7: (0, 377), 9: (0, 315), 4: (1, {'@': 352}), 11: (1, {'@': 352}), 12: (1, {'@': 352}), 170: (1, {'@': 352}), 13: (1, {'@': 352})}, 699: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 131: (0, 448), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 700: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 218: (0, 689), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 131: (0, 617), 95: (0, 542), 20: (0, 247), 101: (0, 265), 138: (0, 287), 82: (0, 278), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 108: (0, 487), 139: (0, 505), 22: (0, 11), 42: (0, 233), 110: (0, 450), 41: (0, 273), 21: (0, 305), 219: (0, 653), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 100: (0, 314), 48: (0, 486)}, 701: {5: (0, 600)}, 702: {3: (0, 29), 11: (1, {'@': 126}), 13: (1, {'@': 126}), 12: (1, {'@': 126})}, 703: {122: (0, 733)}, 704: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 521), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 196: (0, 115), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 197: (0, 708), 1: (0, 614), 25: (0, 203), 134: (0, 433), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 10: (0, 209), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 66), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 705: {11: (1, {'@': 13}), 13: (1, {'@': 13}), 12: (1, {'@': 13})}, 706: {72: (1, {'@': 308}), 56: (1, {'@': 308}), 78: (1, {'@': 308}), 80: (1, {'@': 308}), 81: (1, {'@': 308}), 68: (1, {'@': 308}), 84: (1, {'@': 308}), 69: (1, {'@': 308}), 86: (1, {'@': 308}), 71: (1, {'@': 308}), 87: (1, {'@': 308}), 89: (1, {'@': 308}), 12: (1, {'@': 308}), 93: (1, {'@': 308}), 60: (1, {'@': 308}), 63: (1, {'@': 308}), 62: (1, {'@': 308}), 97: (1, {'@': 308}), 99: (1, {'@': 308}), 100: (1, {'@': 308}), 102: (1, {'@': 308}), 43: (1, {'@': 308}), 103: (1, {'@': 308}), 105: (1, {'@': 308}), 107: (1, {'@': 308}), 108: (1, {'@': 308}), 112: (1, {'@': 308}), 70: (1, {'@': 308}), 64: (1, {'@': 308}), 67: (1, {'@': 308}), 3: (1, {'@': 308}), 9: (1, {'@': 308}), 115: (1, {'@': 308}), 116: (1, {'@': 308}), 5: (1, {'@': 308}), 125: (1, {'@': 308}), 59: (1, {'@': 308}), 120: (1, {'@': 308}), 65: (1, {'@': 308}), 6: (1, {'@': 308}), 54: (1, {'@': 308}), 123: (1, {'@': 308}), 73: (1, {'@': 308}), 74: (1, {'@': 308}), 75: (1, {'@': 308}), 76: (1, {'@': 308}), 77: (1, {'@': 308}), 79: (1, {'@': 308}), 61: (1, {'@': 308}), 82: (1, {'@': 308}), 10: (1, {'@': 308}), 66: (1, {'@': 308}), 83: (1, {'@': 308}), 85: (1, {'@': 308}), 11: (1, {'@': 308}), 88: (1, {'@': 308}), 90: (1, {'@': 308}), 91: (1, {'@': 308}), 92: (1, {'@': 308}), 94: (1, {'@': 308}), 47: (1, {'@': 308}), 95: (1, {'@': 308}), 96: (1, {'@': 308}), 4: (1, {'@': 308}), 55: (1, {'@': 308}), 98: (1, {'@': 308}), 101: (1, {'@': 308}), 13: (1, {'@': 308}), 104: (1, {'@': 308}), 106: (1, {'@': 308}), 58: (1, {'@': 308}), 109: (1, {'@': 308}), 1: (1, {'@': 308}), 110: (1, {'@': 308}), 111: (1, {'@': 308}), 113: (1, {'@': 308}), 114: (1, {'@': 308}), 32: (1, {'@': 308}), 57: (1, {'@': 308}), 117: (1, {'@': 308}), 118: (1, {'@': 308}), 119: (1, {'@': 308}), 121: (1, {'@': 308}), 122: (1, {'@': 308}), 124: (1, {'@': 308})}, 707: {72: (1, {'@': 9}), 74: (1, {'@': 9}), 75: (1, {'@': 9}), 76: (1, {'@': 9}), 77: (1, {'@': 9}), 78: (1, {'@': 9}), 166: (1, {'@': 9}), 79: (1, {'@': 9}), 80: (1, {'@': 9}), 81: (1, {'@': 9}), 82: (1, {'@': 9}), 44: (1, {'@': 9}), 83: (1, {'@': 9}), 84: (1, {'@': 9}), 85: (1, {'@': 9}), 11: (1, {'@': 9}), 86: (1, {'@': 9}), 163: (1, {'@': 9}), 45: (1, {'@': 9}), 87: (1, {'@': 9}), 88: (1, {'@': 9}), 89: (1, {'@': 9}), 12: (1, {'@': 9}), 91: (1, {'@': 9}), 92: (1, {'@': 9}), 93: (1, {'@': 9}), 94: (1, {'@': 9}), 95: (1, {'@': 9}), 96: (1, {'@': 9}), 164: (1, {'@': 9}), 165: (1, {'@': 9}), 97: (1, {'@': 9}), 167: (1, {'@': 9}), 98: (1, {'@': 9}), 99: (1, {'@': 9}), 100: (1, {'@': 9}), 101: (1, {'@': 9}), 102: (1, {'@': 9}), 13: (1, {'@': 9}), 103: (1, {'@': 9}), 105: (1, {'@': 9}), 104: (1, {'@': 9}), 106: (1, {'@': 9}), 107: (1, {'@': 9}), 108: (1, {'@': 9}), 109: (1, {'@': 9}), 1: (1, {'@': 9}), 110: (1, {'@': 9}), 111: (1, {'@': 9}), 112: (1, {'@': 9}), 113: (1, {'@': 9}), 114: (1, {'@': 9}), 3: (1, {'@': 9}), 115: (1, {'@': 9}), 116: (1, {'@': 9}), 117: (1, {'@': 9}), 118: (1, {'@': 9}), 119: (1, {'@': 9}), 120: (1, {'@': 9}), 121: (1, {'@': 9}), 122: (1, {'@': 9}), 123: (1, {'@': 9}), 124: (1, {'@': 9}), 195: (1, {'@': 9}), 168: (1, {'@': 9})}, 708: {5: (1, {'@': 317}), 10: (1, {'@': 317}), 54: (1, {'@': 317}), 11: (1, {'@': 317}), 12: (1, {'@': 317}), 13: (1, {'@': 317})}, 709: {72: (1, {'@': 287}), 56: (1, {'@': 287}), 78: (1, {'@': 287}), 80: (1, {'@': 287}), 81: (1, {'@': 287}), 68: (1, {'@': 287}), 84: (1, {'@': 287}), 69: (1, {'@': 287}), 86: (1, {'@': 287}), 71: (1, {'@': 287}), 87: (1, {'@': 287}), 89: (1, {'@': 287}), 12: (1, {'@': 287}), 93: (1, {'@': 287}), 60: (1, {'@': 287}), 63: (1, {'@': 287}), 62: (1, {'@': 287}), 97: (1, {'@': 287}), 99: (1, {'@': 287}), 100: (1, {'@': 287}), 102: (1, {'@': 287}), 43: (1, {'@': 287}), 103: (1, {'@': 287}), 105: (1, {'@': 287}), 107: (1, {'@': 287}), 108: (1, {'@': 287}), 112: (1, {'@': 287}), 70: (1, {'@': 287}), 64: (1, {'@': 287}), 67: (1, {'@': 287}), 3: (1, {'@': 287}), 9: (1, {'@': 287}), 115: (1, {'@': 287}), 116: (1, {'@': 287}), 5: (1, {'@': 287}), 125: (1, {'@': 287}), 59: (1, {'@': 287}), 120: (1, {'@': 287}), 65: (1, {'@': 287}), 6: (1, {'@': 287}), 54: (1, {'@': 287}), 123: (1, {'@': 287}), 73: (1, {'@': 287}), 74: (1, {'@': 287}), 75: (1, {'@': 287}), 76: (1, {'@': 287}), 77: (1, {'@': 287}), 79: (1, {'@': 287}), 61: (1, {'@': 287}), 82: (1, {'@': 287}), 10: (1, {'@': 287}), 66: (1, {'@': 287}), 83: (1, {'@': 287}), 85: (1, {'@': 287}), 11: (1, {'@': 287}), 88: (1, {'@': 287}), 90: (1, {'@': 287}), 91: (1, {'@': 287}), 92: (1, {'@': 287}), 94: (1, {'@': 287}), 47: (1, {'@': 287}), 95: (1, {'@': 287}), 96: (1, {'@': 287}), 4: (1, {'@': 287}), 55: (1, {'@': 287}), 98: (1, {'@': 287}), 101: (1, {'@': 287}), 13: (1, {'@': 287}), 104: (1, {'@': 287}), 106: (1, {'@': 287}), 58: (1, {'@': 287}), 109: (1, {'@': 287}), 1: (1, {'@': 287}), 110: (1, {'@': 287}), 111: (1, {'@': 287}), 113: (1, {'@': 287}), 114: (1, {'@': 287}), 32: (1, {'@': 287}), 57: (1, {'@': 287}), 117: (1, {'@': 287}), 118: (1, {'@': 287}), 119: (1, {'@': 287}), 121: (1, {'@': 287}), 122: (1, {'@': 287}), 124: (1, {'@': 287})}, 710: {11: (1, {'@': 30}), 13: (1, {'@': 30}), 12: (1, {'@': 30})}, 711: {5: (1, {'@': 252}), 11: (1, {'@': 252}), 12: (1, {'@': 252}), 43: (1, {'@': 252}), 13: (1, {'@': 252})}, 712: {3: (0, 671)}, 713: {11: (1, {'@': 99}), 13: (1, {'@': 99}), 12: (1, {'@': 99})}, 714: {173: (1, {'@': 223}), 11: (1, {'@': 223}), 12: (1, {'@': 223}), 13: (1, {'@': 223}), 121: (1, {'@': 223})}, 715: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 192: (0, 781), 147: (0, 580), 13: (0, 707), 199: (0, 620), 105: (0, 588), 149: (0, 779), 148: (0, 769), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 11: (0, 632), 175: (0, 593), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 208: (0, 244), 86: (0, 656), 151: (0, 717), 72: (0, 555), 92: (0, 604), 101: (0, 699), 100: (0, 685), 152: (0, 536), 74: (0, 597), 113: (0, 712), 153: (0, 715), 154: (0, 780), 107: (0, 591), 213: (0, 549), 193: (0, 624), 191: (0, 757), 114: (0, 759), 200: (0, 750), 116: (0, 774), 155: (0, 651), 75: (0, 654), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 201: (0, 618), 117: (0, 626), 202: (0, 637), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 194: (0, 721), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 150: (0, 741), 111: (0, 783), 161: (0, 729), 160: (0, 746), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 716: {103: (0, 460), 116: (0, 110), 121: (0, 516)}, 717: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 13: (0, 707), 199: (0, 620), 105: (0, 588), 149: (0, 779), 148: (0, 769), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 11: (0, 632), 175: (0, 593), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 208: (0, 244), 86: (0, 656), 213: (0, 339), 151: (0, 717), 72: (0, 555), 92: (0, 604), 101: (0, 699), 100: (0, 685), 152: (0, 536), 122: (0, 269), 74: (0, 597), 113: (0, 712), 153: (0, 715), 154: (0, 780), 107: (0, 591), 193: (0, 624), 191: (0, 757), 114: (0, 759), 200: (0, 750), 116: (0, 774), 155: (0, 651), 75: (0, 654), 156: (0, 747), 192: (0, 198), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 201: (0, 618), 117: (0, 626), 202: (0, 637), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 194: (0, 721), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 150: (0, 741), 111: (0, 783), 161: (0, 729), 160: (0, 746), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 718: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 223: (0, 659), 87: (0, 615), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 222: (0, 282), 95: (0, 542), 224: (0, 481), 20: (0, 247), 101: (0, 265), 131: (0, 7), 138: (0, 287), 82: (0, 278), 15: (0, 205), 219: (0, 454), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 108: (0, 487), 139: (0, 505), 22: (0, 11), 42: (0, 233), 110: (0, 450), 41: (0, 273), 21: (0, 305), 225: (0, 389), 170: (0, 411), 99: (0, 297), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 100: (0, 314), 48: (0, 486), 5: (1, {'@': 239}), 11: (1, {'@': 239}), 12: (1, {'@': 239}), 43: (1, {'@': 239}), 13: (1, {'@': 239})}, 719: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 131: (0, 766), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 720: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 231: (0, 633), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 223: (0, 659), 87: (0, 615), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 95: (0, 542), 224: (0, 481), 222: (0, 225), 20: (0, 247), 101: (0, 265), 131: (0, 7), 138: (0, 287), 82: (0, 278), 15: (0, 205), 219: (0, 454), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 108: (0, 487), 139: (0, 505), 22: (0, 11), 42: (0, 233), 110: (0, 450), 41: (0, 273), 21: (0, 305), 225: (0, 389), 170: (0, 411), 99: (0, 297), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 100: (0, 314), 48: (0, 486), 5: (1, {'@': 239}), 11: (1, {'@': 239}), 12: (1, {'@': 239}), 43: (1, {'@': 239}), 13: (1, {'@': 239})}, 721: {13: (0, 206), 11: (0, 75)}, 722: {31: (0, 3), 25: (0, 203), 126: (0, 96), 22: (0, 11), 50: (0, 125), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 19: (0, 150), 1: (0, 324), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 135: (0, 483), 33: (0, 356), 129: (0, 497), 136: (0, 403), 23: (0, 219), 29: (0, 474), 130: (0, 169), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 138: (0, 289), 133: (0, 160), 41: (0, 273), 27: (0, 462), 28: (0, 42), 21: (0, 305), 49: (0, 373), 46: (0, 445), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 723: {72: (1, {'@': 305}), 56: (1, {'@': 305}), 78: (1, {'@': 305}), 80: (1, {'@': 305}), 81: (1, {'@': 305}), 68: (1, {'@': 305}), 84: (1, {'@': 305}), 69: (1, {'@': 305}), 86: (1, {'@': 305}), 71: (1, {'@': 305}), 87: (1, {'@': 305}), 89: (1, {'@': 305}), 12: (1, {'@': 305}), 93: (1, {'@': 305}), 60: (1, {'@': 305}), 63: (1, {'@': 305}), 62: (1, {'@': 305}), 97: (1, {'@': 305}), 99: (1, {'@': 305}), 100: (1, {'@': 305}), 102: (1, {'@': 305}), 43: (1, {'@': 305}), 103: (1, {'@': 305}), 105: (1, {'@': 305}), 107: (1, {'@': 305}), 108: (1, {'@': 305}), 112: (1, {'@': 305}), 70: (1, {'@': 305}), 64: (1, {'@': 305}), 67: (1, {'@': 305}), 3: (1, {'@': 305}), 9: (1, {'@': 305}), 115: (1, {'@': 305}), 116: (1, {'@': 305}), 5: (1, {'@': 305}), 125: (1, {'@': 305}), 59: (1, {'@': 305}), 120: (1, {'@': 305}), 65: (1, {'@': 305}), 6: (1, {'@': 305}), 54: (1, {'@': 305}), 123: (1, {'@': 305}), 73: (1, {'@': 305}), 74: (1, {'@': 305}), 75: (1, {'@': 305}), 76: (1, {'@': 305}), 77: (1, {'@': 305}), 79: (1, {'@': 305}), 61: (1, {'@': 305}), 82: (1, {'@': 305}), 10: (1, {'@': 305}), 66: (1, {'@': 305}), 83: (1, {'@': 305}), 85: (1, {'@': 305}), 11: (1, {'@': 305}), 88: (1, {'@': 305}), 90: (1, {'@': 305}), 91: (1, {'@': 305}), 92: (1, {'@': 305}), 94: (1, {'@': 305}), 47: (1, {'@': 305}), 95: (1, {'@': 305}), 96: (1, {'@': 305}), 4: (1, {'@': 305}), 55: (1, {'@': 305}), 98: (1, {'@': 305}), 101: (1, {'@': 305}), 13: (1, {'@': 305}), 104: (1, {'@': 305}), 106: (1, {'@': 305}), 58: (1, {'@': 305}), 109: (1, {'@': 305}), 1: (1, {'@': 305}), 110: (1, {'@': 305}), 111: (1, {'@': 305}), 113: (1, {'@': 305}), 114: (1, {'@': 305}), 32: (1, {'@': 305}), 57: (1, {'@': 305}), 117: (1, {'@': 305}), 118: (1, {'@': 305}), 119: (1, {'@': 305}), 121: (1, {'@': 305}), 122: (1, {'@': 305}), 124: (1, {'@': 305})}, 724: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 131: (0, 646), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 725: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 135: (0, 483), 33: (0, 356), 129: (0, 240), 136: (0, 403), 23: (0, 219), 29: (0, 474), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 726: {43: (1, {'@': 245}), 5: (1, {'@': 245}), 11: (1, {'@': 245}), 12: (1, {'@': 245}), 13: (1, {'@': 245})}, 727: {11: (1, {'@': 114}), 13: (1, {'@': 114}), 12: (1, {'@': 114})}, 728: {11: (1, {'@': 122}), 13: (1, {'@': 122}), 12: (1, {'@': 122})}, 729: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 13: (0, 707), 199: (0, 620), 105: (0, 588), 149: (0, 779), 148: (0, 769), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 11: (0, 632), 175: (0, 593), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 208: (0, 634), 86: (0, 656), 151: (0, 717), 72: (0, 555), 92: (0, 604), 101: (0, 699), 100: (0, 685), 192: (0, 683), 152: (0, 536), 74: (0, 597), 113: (0, 712), 153: (0, 715), 154: (0, 780), 107: (0, 591), 193: (0, 624), 191: (0, 757), 114: (0, 759), 200: (0, 750), 116: (0, 774), 155: (0, 651), 75: (0, 654), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 213: (0, 784), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 201: (0, 618), 117: (0, 626), 202: (0, 637), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 194: (0, 721), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 150: (0, 741), 111: (0, 783), 161: (0, 729), 160: (0, 746), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 730: {43: (1, {'@': 247}), 5: (1, {'@': 247}), 11: (1, {'@': 247}), 12: (1, {'@': 247}), 13: (1, {'@': 247})}, 731: {72: (1, {'@': 2}), 74: (1, {'@': 2}), 98: (1, {'@': 2}), 75: (1, {'@': 2}), 99: (1, {'@': 2}), 100: (1, {'@': 2}), 101: (1, {'@': 2}), 102: (1, {'@': 2}), 76: (1, {'@': 2}), 13: (1, {'@': 2}), 103: (1, {'@': 2}), 104: (1, {'@': 2}), 105: (1, {'@': 2}), 78: (1, {'@': 2}), 77: (1, {'@': 2}), 106: (1, {'@': 2}), 107: (1, {'@': 2}), 79: (1, {'@': 2}), 108: (1, {'@': 2}), 80: (1, {'@': 2}), 81: (1, {'@': 2}), 82: (1, {'@': 2}), 109: (1, {'@': 2}), 83: (1, {'@': 2}), 1: (1, {'@': 2}), 110: (1, {'@': 2}), 111: (1, {'@': 2}), 84: (1, {'@': 2}), 112: (1, {'@': 2}), 85: (1, {'@': 2}), 11: (1, {'@': 2}), 86: (1, {'@': 2}), 113: (1, {'@': 2}), 114: (1, {'@': 2}), 87: (1, {'@': 2}), 3: (1, {'@': 2}), 88: (1, {'@': 2}), 195: (1, {'@': 2}), 89: (1, {'@': 2}), 115: (1, {'@': 2}), 116: (1, {'@': 2}), 117: (1, {'@': 2}), 118: (1, {'@': 2}), 91: (1, {'@': 2}), 119: (1, {'@': 2}), 92: (1, {'@': 2}), 93: (1, {'@': 2}), 94: (1, {'@': 2}), 120: (1, {'@': 2}), 95: (1, {'@': 2}), 96: (1, {'@': 2}), 121: (1, {'@': 2}), 123: (1, {'@': 2}), 97: (1, {'@': 2}), 124: (1, {'@': 2}), 164: (1, {'@': 2}), 165: (1, {'@': 2}), 122: (1, {'@': 2}), 166: (1, {'@': 2}), 12: (1, {'@': 2}), 44: (1, {'@': 2}), 45: (1, {'@': 2}), 168: (1, {'@': 2}), 163: (1, {'@': 2}), 167: (1, {'@': 2})}, 732: {11: (1, {'@': 66}), 13: (1, {'@': 66}), 12: (1, {'@': 66})}, 733: {173: (1, {'@': 222}), 11: (1, {'@': 222}), 12: (1, {'@': 222}), 13: (1, {'@': 222}), 121: (1, {'@': 222})}, 734: {72: (1, {'@': 296}), 56: (1, {'@': 296}), 78: (1, {'@': 296}), 80: (1, {'@': 296}), 81: (1, {'@': 296}), 68: (1, {'@': 296}), 84: (1, {'@': 296}), 69: (1, {'@': 296}), 86: (1, {'@': 296}), 71: (1, {'@': 296}), 87: (1, {'@': 296}), 89: (1, {'@': 296}), 12: (1, {'@': 296}), 93: (1, {'@': 296}), 60: (1, {'@': 296}), 63: (1, {'@': 296}), 62: (1, {'@': 296}), 97: (1, {'@': 296}), 99: (1, {'@': 296}), 100: (1, {'@': 296}), 102: (1, {'@': 296}), 43: (1, {'@': 296}), 103: (1, {'@': 296}), 105: (1, {'@': 296}), 107: (1, {'@': 296}), 108: (1, {'@': 296}), 112: (1, {'@': 296}), 70: (1, {'@': 296}), 64: (1, {'@': 296}), 67: (1, {'@': 296}), 3: (1, {'@': 296}), 9: (1, {'@': 296}), 115: (1, {'@': 296}), 116: (1, {'@': 296}), 5: (1, {'@': 296}), 125: (1, {'@': 296}), 59: (1, {'@': 296}), 120: (1, {'@': 296}), 65: (1, {'@': 296}), 6: (1, {'@': 296}), 54: (1, {'@': 296}), 123: (1, {'@': 296}), 73: (1, {'@': 296}), 74: (1, {'@': 296}), 75: (1, {'@': 296}), 76: (1, {'@': 296}), 77: (1, {'@': 296}), 79: (1, {'@': 296}), 61: (1, {'@': 296}), 82: (1, {'@': 296}), 10: (1, {'@': 296}), 66: (1, {'@': 296}), 83: (1, {'@': 296}), 85: (1, {'@': 296}), 11: (1, {'@': 296}), 88: (1, {'@': 296}), 90: (1, {'@': 296}), 91: (1, {'@': 296}), 92: (1, {'@': 296}), 94: (1, {'@': 296}), 47: (1, {'@': 296}), 95: (1, {'@': 296}), 96: (1, {'@': 296}), 4: (1, {'@': 296}), 55: (1, {'@': 296}), 98: (1, {'@': 296}), 101: (1, {'@': 296}), 13: (1, {'@': 296}), 104: (1, {'@': 296}), 106: (1, {'@': 296}), 58: (1, {'@': 296}), 109: (1, {'@': 296}), 1: (1, {'@': 296}), 110: (1, {'@': 296}), 111: (1, {'@': 296}), 113: (1, {'@': 296}), 114: (1, {'@': 296}), 32: (1, {'@': 296}), 57: (1, {'@': 296}), 117: (1, {'@': 296}), 118: (1, {'@': 296}), 119: (1, {'@': 296}), 121: (1, {'@': 296}), 122: (1, {'@': 296}), 124: (1, {'@': 296})}, 735: {131: (0, 551), 31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 736: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 95: (0, 542), 20: (0, 247), 101: (0, 265), 219: (0, 86), 138: (0, 287), 131: (0, 65), 82: (0, 278), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 108: (0, 487), 139: (0, 505), 22: (0, 11), 42: (0, 233), 110: (0, 450), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 100: (0, 314), 48: (0, 486)}, 737: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 131: (0, 18), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 738: {72: (1, {'@': 293}), 56: (1, {'@': 293}), 78: (1, {'@': 293}), 80: (1, {'@': 293}), 81: (1, {'@': 293}), 68: (1, {'@': 293}), 84: (1, {'@': 293}), 69: (1, {'@': 293}), 86: (1, {'@': 293}), 71: (1, {'@': 293}), 87: (1, {'@': 293}), 89: (1, {'@': 293}), 12: (1, {'@': 293}), 93: (1, {'@': 293}), 60: (1, {'@': 293}), 63: (1, {'@': 293}), 62: (1, {'@': 293}), 97: (1, {'@': 293}), 99: (1, {'@': 293}), 100: (1, {'@': 293}), 102: (1, {'@': 293}), 43: (1, {'@': 293}), 103: (1, {'@': 293}), 105: (1, {'@': 293}), 107: (1, {'@': 293}), 108: (1, {'@': 293}), 112: (1, {'@': 293}), 70: (1, {'@': 293}), 64: (1, {'@': 293}), 67: (1, {'@': 293}), 3: (1, {'@': 293}), 9: (1, {'@': 293}), 115: (1, {'@': 293}), 116: (1, {'@': 293}), 5: (1, {'@': 293}), 125: (1, {'@': 293}), 59: (1, {'@': 293}), 120: (1, {'@': 293}), 65: (1, {'@': 293}), 6: (1, {'@': 293}), 54: (1, {'@': 293}), 123: (1, {'@': 293}), 73: (1, {'@': 293}), 74: (1, {'@': 293}), 75: (1, {'@': 293}), 76: (1, {'@': 293}), 77: (1, {'@': 293}), 79: (1, {'@': 293}), 61: (1, {'@': 293}), 82: (1, {'@': 293}), 10: (1, {'@': 293}), 66: (1, {'@': 293}), 83: (1, {'@': 293}), 85: (1, {'@': 293}), 11: (1, {'@': 293}), 88: (1, {'@': 293}), 90: (1, {'@': 293}), 91: (1, {'@': 293}), 92: (1, {'@': 293}), 94: (1, {'@': 293}), 47: (1, {'@': 293}), 95: (1, {'@': 293}), 96: (1, {'@': 293}), 4: (1, {'@': 293}), 55: (1, {'@': 293}), 98: (1, {'@': 293}), 101: (1, {'@': 293}), 13: (1, {'@': 293}), 104: (1, {'@': 293}), 106: (1, {'@': 293}), 58: (1, {'@': 293}), 109: (1, {'@': 293}), 1: (1, {'@': 293}), 110: (1, {'@': 293}), 111: (1, {'@': 293}), 113: (1, {'@': 293}), 114: (1, {'@': 293}), 32: (1, {'@': 293}), 57: (1, {'@': 293}), 117: (1, {'@': 293}), 118: (1, {'@': 293}), 119: (1, {'@': 293}), 121: (1, {'@': 293}), 122: (1, {'@': 293}), 124: (1, {'@': 293})}, 739: {31: (0, 3), 25: (0, 203), 126: (0, 96), 22: (0, 11), 50: (0, 125), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 19: (0, 150), 1: (0, 324), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 135: (0, 483), 33: (0, 356), 129: (0, 497), 136: (0, 403), 23: (0, 219), 29: (0, 474), 130: (0, 169), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 133: (0, 160), 41: (0, 273), 27: (0, 462), 28: (0, 42), 21: (0, 305), 49: (0, 373), 46: (0, 445), 24: (0, 408), 40: (0, 503), 138: (0, 290), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 740: {9: (0, 51), 4: (0, 118), 172: (0, 130)}, 741: {11: (1, {'@': 16}), 13: (1, {'@': 16}), 12: (1, {'@': 16})}, 742: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 131: (0, 579), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 743: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 131: (0, 379), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 744: {72: (1, {'@': 282}), 56: (1, {'@': 282}), 78: (1, {'@': 282}), 80: (1, {'@': 282}), 81: (1, {'@': 282}), 68: (1, {'@': 282}), 84: (1, {'@': 282}), 69: (1, {'@': 282}), 86: (1, {'@': 282}), 71: (1, {'@': 282}), 87: (1, {'@': 282}), 89: (1, {'@': 282}), 12: (1, {'@': 282}), 93: (1, {'@': 282}), 60: (1, {'@': 282}), 63: (1, {'@': 282}), 62: (1, {'@': 282}), 97: (1, {'@': 282}), 99: (1, {'@': 282}), 100: (1, {'@': 282}), 102: (1, {'@': 282}), 43: (1, {'@': 282}), 103: (1, {'@': 282}), 105: (1, {'@': 282}), 107: (1, {'@': 282}), 108: (1, {'@': 282}), 112: (1, {'@': 282}), 70: (1, {'@': 282}), 64: (1, {'@': 282}), 67: (1, {'@': 282}), 3: (1, {'@': 282}), 9: (1, {'@': 282}), 115: (1, {'@': 282}), 116: (1, {'@': 282}), 5: (1, {'@': 282}), 125: (1, {'@': 282}), 59: (1, {'@': 282}), 120: (1, {'@': 282}), 65: (1, {'@': 282}), 6: (1, {'@': 282}), 54: (1, {'@': 282}), 123: (1, {'@': 282}), 73: (1, {'@': 282}), 74: (1, {'@': 282}), 75: (1, {'@': 282}), 76: (1, {'@': 282}), 77: (1, {'@': 282}), 79: (1, {'@': 282}), 61: (1, {'@': 282}), 82: (1, {'@': 282}), 10: (1, {'@': 282}), 66: (1, {'@': 282}), 83: (1, {'@': 282}), 85: (1, {'@': 282}), 11: (1, {'@': 282}), 88: (1, {'@': 282}), 90: (1, {'@': 282}), 91: (1, {'@': 282}), 92: (1, {'@': 282}), 94: (1, {'@': 282}), 47: (1, {'@': 282}), 95: (1, {'@': 282}), 96: (1, {'@': 282}), 4: (1, {'@': 282}), 55: (1, {'@': 282}), 98: (1, {'@': 282}), 101: (1, {'@': 282}), 13: (1, {'@': 282}), 104: (1, {'@': 282}), 106: (1, {'@': 282}), 58: (1, {'@': 282}), 109: (1, {'@': 282}), 1: (1, {'@': 282}), 110: (1, {'@': 282}), 111: (1, {'@': 282}), 113: (1, {'@': 282}), 114: (1, {'@': 282}), 32: (1, {'@': 282}), 57: (1, {'@': 282}), 117: (1, {'@': 282}), 118: (1, {'@': 282}), 119: (1, {'@': 282}), 121: (1, {'@': 282}), 122: (1, {'@': 282}), 124: (1, {'@': 282})}, 745: {14: (1, {'@': 240}), 15: (1, {'@': 240}), 16: (1, {'@': 240}), 17: (1, {'@': 240}), 18: (1, {'@': 240}), 101: (1, {'@': 240}), 100: (1, {'@': 240}), 19: (1, {'@': 240}), 20: (1, {'@': 240}), 21: (1, {'@': 240}), 22: (1, {'@': 240}), 23: (1, {'@': 240}), 108: (1, {'@': 240}), 82: (1, {'@': 240}), 24: (1, {'@': 240}), 25: (1, {'@': 240}), 1: (1, {'@': 240}), 110: (1, {'@': 240}), 26: (1, {'@': 240}), 27: (1, {'@': 240}), 28: (1, {'@': 240}), 46: (1, {'@': 240}), 29: (1, {'@': 240}), 30: (1, {'@': 240}), 3: (1, {'@': 240}), 31: (1, {'@': 240}), 9: (1, {'@': 240}), 32: (1, {'@': 240}), 34: (1, {'@': 240}), 33: (1, {'@': 240}), 35: (1, {'@': 240}), 36: (1, {'@': 240}), 37: (1, {'@': 240}), 47: (1, {'@': 240}), 38: (1, {'@': 240}), 95: (1, {'@': 240}), 48: (1, {'@': 240}), 39: (1, {'@': 240}), 49: (1, {'@': 240}), 40: (1, {'@': 240}), 41: (1, {'@': 240}), 42: (1, {'@': 240})}, 746: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 167: (0, 312), 147: (0, 580), 13: (0, 707), 199: (0, 620), 105: (0, 588), 149: (0, 779), 148: (0, 769), 123: (0, 776), 110: (0, 724), 192: (0, 325), 91: (0, 716), 81: (0, 786), 11: (0, 632), 83: (0, 720), 175: (0, 593), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 208: (0, 244), 86: (0, 656), 151: (0, 717), 72: (0, 555), 92: (0, 604), 101: (0, 699), 232: (0, 381), 100: (0, 685), 213: (0, 351), 152: (0, 536), 74: (0, 597), 113: (0, 712), 153: (0, 715), 154: (0, 780), 107: (0, 591), 193: (0, 624), 191: (0, 392), 114: (0, 759), 200: (0, 750), 116: (0, 774), 155: (0, 651), 75: (0, 654), 156: (0, 747), 93: (0, 630), 98: (0, 544), 118: (0, 548), 163: (0, 107), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 201: (0, 618), 117: (0, 626), 202: (0, 637), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 194: (0, 721), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 150: (0, 741), 111: (0, 783), 161: (0, 729), 160: (0, 746), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 747: {11: (0, 256), 13: (0, 426)}, 748: {11: (1, {'@': 347}), 12: (1, {'@': 347}), 13: (1, {'@': 347})}, 749: {9: (0, 566), 171: (0, 40), 72: (1, {'@': 161}), 56: (1, {'@': 161}), 78: (1, {'@': 161}), 80: (1, {'@': 161}), 81: (1, {'@': 161}), 68: (1, {'@': 161}), 84: (1, {'@': 161}), 69: (1, {'@': 161}), 86: (1, {'@': 161}), 71: (1, {'@': 161}), 87: (1, {'@': 161}), 89: (1, {'@': 161}), 12: (1, {'@': 161}), 93: (1, {'@': 161}), 60: (1, {'@': 161}), 63: (1, {'@': 161}), 62: (1, {'@': 161}), 97: (1, {'@': 161}), 99: (1, {'@': 161}), 100: (1, {'@': 161}), 102: (1, {'@': 161}), 43: (1, {'@': 161}), 103: (1, {'@': 161}), 105: (1, {'@': 161}), 107: (1, {'@': 161}), 108: (1, {'@': 161}), 112: (1, {'@': 161}), 70: (1, {'@': 161}), 64: (1, {'@': 161}), 67: (1, {'@': 161}), 3: (1, {'@': 161}), 115: (1, {'@': 161}), 116: (1, {'@': 161}), 5: (1, {'@': 161}), 125: (1, {'@': 161}), 59: (1, {'@': 161}), 120: (1, {'@': 161}), 65: (1, {'@': 161}), 6: (1, {'@': 161}), 54: (1, {'@': 161}), 123: (1, {'@': 161}), 73: (1, {'@': 161}), 74: (1, {'@': 161}), 75: (1, {'@': 161}), 76: (1, {'@': 161}), 77: (1, {'@': 161}), 79: (1, {'@': 161}), 61: (1, {'@': 161}), 82: (1, {'@': 161}), 10: (1, {'@': 161}), 66: (1, {'@': 161}), 83: (1, {'@': 161}), 85: (1, {'@': 161}), 11: (1, {'@': 161}), 88: (1, {'@': 161}), 90: (1, {'@': 161}), 91: (1, {'@': 161}), 92: (1, {'@': 161}), 94: (1, {'@': 161}), 47: (1, {'@': 161}), 95: (1, {'@': 161}), 96: (1, {'@': 161}), 4: (1, {'@': 161}), 55: (1, {'@': 161}), 98: (1, {'@': 161}), 101: (1, {'@': 161}), 13: (1, {'@': 161}), 104: (1, {'@': 161}), 106: (1, {'@': 161}), 58: (1, {'@': 161}), 109: (1, {'@': 161}), 1: (1, {'@': 161}), 110: (1, {'@': 161}), 111: (1, {'@': 161}), 113: (1, {'@': 161}), 114: (1, {'@': 161}), 32: (1, {'@': 161}), 57: (1, {'@': 161}), 117: (1, {'@': 161}), 118: (1, {'@': 161}), 119: (1, {'@': 161}), 121: (1, {'@': 161}), 122: (1, {'@': 161}), 124: (1, {'@': 161})}, 750: {72: (1, {'@': 1}), 74: (1, {'@': 1}), 98: (1, {'@': 1}), 75: (1, {'@': 1}), 99: (1, {'@': 1}), 100: (1, {'@': 1}), 101: (1, {'@': 1}), 102: (1, {'@': 1}), 76: (1, {'@': 1}), 13: (1, {'@': 1}), 103: (1, {'@': 1}), 104: (1, {'@': 1}), 105: (1, {'@': 1}), 78: (1, {'@': 1}), 77: (1, {'@': 1}), 106: (1, {'@': 1}), 107: (1, {'@': 1}), 79: (1, {'@': 1}), 108: (1, {'@': 1}), 80: (1, {'@': 1}), 81: (1, {'@': 1}), 82: (1, {'@': 1}), 109: (1, {'@': 1}), 83: (1, {'@': 1}), 1: (1, {'@': 1}), 110: (1, {'@': 1}), 111: (1, {'@': 1}), 84: (1, {'@': 1}), 112: (1, {'@': 1}), 85: (1, {'@': 1}), 11: (1, {'@': 1}), 86: (1, {'@': 1}), 113: (1, {'@': 1}), 114: (1, {'@': 1}), 87: (1, {'@': 1}), 3: (1, {'@': 1}), 88: (1, {'@': 1}), 195: (1, {'@': 1}), 89: (1, {'@': 1}), 115: (1, {'@': 1}), 116: (1, {'@': 1}), 117: (1, {'@': 1}), 118: (1, {'@': 1}), 91: (1, {'@': 1}), 119: (1, {'@': 1}), 92: (1, {'@': 1}), 93: (1, {'@': 1}), 94: (1, {'@': 1}), 120: (1, {'@': 1}), 95: (1, {'@': 1}), 96: (1, {'@': 1}), 121: (1, {'@': 1}), 123: (1, {'@': 1}), 97: (1, {'@': 1}), 124: (1, {'@': 1}), 164: (1, {'@': 1}), 165: (1, {'@': 1}), 122: (1, {'@': 1}), 166: (1, {'@': 1}), 12: (1, {'@': 1}), 44: (1, {'@': 1}), 45: (1, {'@': 1}), 168: (1, {'@': 1}), 163: (1, {'@': 1}), 167: (1, {'@': 1})}, 751: {31: (0, 3), 25: (0, 203), 50: (0, 125), 22: (0, 11), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 1: (0, 324), 19: (0, 150), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 33: (0, 356), 23: (0, 219), 29: (0, 474), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 41: (0, 273), 21: (0, 305), 27: (0, 462), 28: (0, 42), 49: (0, 373), 136: (0, 108), 24: (0, 408), 40: (0, 503), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 752: {174: (0, 359), 11: (0, 655), 175: (0, 703), 13: (0, 682), 173: (0, 778), 122: (0, 714), 121: (0, 583), 120: (0, 550)}, 753: {10: (0, 529)}, 754: {11: (1, {'@': 115}), 13: (1, {'@': 115}), 12: (1, {'@': 115})}, 755: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 131: (0, 419), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 756: {31: (0, 3), 25: (0, 203), 126: (0, 96), 22: (0, 11), 50: (0, 125), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 19: (0, 150), 1: (0, 324), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 135: (0, 483), 33: (0, 356), 129: (0, 497), 136: (0, 403), 23: (0, 219), 29: (0, 474), 130: (0, 169), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 133: (0, 160), 41: (0, 273), 27: (0, 462), 28: (0, 42), 21: (0, 305), 49: (0, 373), 46: (0, 445), 24: (0, 408), 40: (0, 503), 138: (0, 277), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 757: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 150: (0, 81), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 156: (0, 747), 11: (0, 73), 93: (0, 630), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 13: (0, 627), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 758: {11: (1, {'@': 48}), 13: (1, {'@': 48}), 12: (1, {'@': 48})}, 759: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 431), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486), 11: (1, {'@': 31}), 13: (1, {'@': 31}), 12: (1, {'@': 31})}, 760: {1: (0, 384)}, 761: {43: (1, {'@': 246}), 5: (1, {'@': 246}), 11: (1, {'@': 246}), 12: (1, {'@': 246}), 13: (1, {'@': 246})}, 762: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 131: (0, 606), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 763: {5: (1, {'@': 237}), 11: (1, {'@': 237}), 12: (1, {'@': 237}), 43: (1, {'@': 237}), 13: (1, {'@': 237})}, 764: {11: (1, {'@': 90}), 13: (1, {'@': 90}), 12: (1, {'@': 90})}, 765: {7: (1, {'@': 328}), 11: (1, {'@': 328}), 9: (1, {'@': 328}), 13: (1, {'@': 328}), 12: (1, {'@': 328})}, 766: {11: (1, {'@': 108}), 13: (1, {'@': 108}), 12: (1, {'@': 108})}, 767: {31: (0, 3), 25: (0, 203), 126: (0, 96), 22: (0, 11), 50: (0, 125), 3: (0, 68), 16: (0, 502), 17: (0, 369), 39: (0, 25), 19: (0, 150), 1: (0, 324), 35: (0, 397), 42: (0, 233), 51: (0, 456), 52: (0, 354), 135: (0, 483), 33: (0, 356), 129: (0, 497), 136: (0, 403), 23: (0, 219), 29: (0, 474), 130: (0, 169), 137: (0, 121), 14: (0, 493), 15: (0, 205), 9: (0, 154), 32: (0, 504), 30: (0, 194), 138: (0, 262), 133: (0, 160), 41: (0, 273), 27: (0, 462), 28: (0, 42), 21: (0, 305), 49: (0, 373), 46: (0, 445), 24: (0, 408), 40: (0, 503), 47: (0, 237), 53: (0, 141), 26: (0, 405), 38: (0, 50), 18: (0, 428), 37: (0, 494), 36: (0, 15), 34: (0, 6), 20: (0, 247)}, 768: {72: (1, {'@': 291}), 56: (1, {'@': 291}), 78: (1, {'@': 291}), 80: (1, {'@': 291}), 81: (1, {'@': 291}), 68: (1, {'@': 291}), 84: (1, {'@': 291}), 69: (1, {'@': 291}), 86: (1, {'@': 291}), 71: (1, {'@': 291}), 87: (1, {'@': 291}), 89: (1, {'@': 291}), 12: (1, {'@': 291}), 93: (1, {'@': 291}), 60: (1, {'@': 291}), 63: (1, {'@': 291}), 62: (1, {'@': 291}), 97: (1, {'@': 291}), 99: (1, {'@': 291}), 100: (1, {'@': 291}), 102: (1, {'@': 291}), 43: (1, {'@': 291}), 103: (1, {'@': 291}), 105: (1, {'@': 291}), 107: (1, {'@': 291}), 108: (1, {'@': 291}), 112: (1, {'@': 291}), 70: (1, {'@': 291}), 64: (1, {'@': 291}), 67: (1, {'@': 291}), 3: (1, {'@': 291}), 9: (1, {'@': 291}), 115: (1, {'@': 291}), 116: (1, {'@': 291}), 5: (1, {'@': 291}), 125: (1, {'@': 291}), 59: (1, {'@': 291}), 120: (1, {'@': 291}), 65: (1, {'@': 291}), 6: (1, {'@': 291}), 54: (1, {'@': 291}), 123: (1, {'@': 291}), 73: (1, {'@': 291}), 74: (1, {'@': 291}), 75: (1, {'@': 291}), 76: (1, {'@': 291}), 77: (1, {'@': 291}), 79: (1, {'@': 291}), 61: (1, {'@': 291}), 82: (1, {'@': 291}), 10: (1, {'@': 291}), 66: (1, {'@': 291}), 83: (1, {'@': 291}), 85: (1, {'@': 291}), 11: (1, {'@': 291}), 88: (1, {'@': 291}), 90: (1, {'@': 291}), 91: (1, {'@': 291}), 92: (1, {'@': 291}), 94: (1, {'@': 291}), 47: (1, {'@': 291}), 95: (1, {'@': 291}), 96: (1, {'@': 291}), 4: (1, {'@': 291}), 55: (1, {'@': 291}), 98: (1, {'@': 291}), 101: (1, {'@': 291}), 13: (1, {'@': 291}), 104: (1, {'@': 291}), 106: (1, {'@': 291}), 58: (1, {'@': 291}), 109: (1, {'@': 291}), 1: (1, {'@': 291}), 110: (1, {'@': 291}), 111: (1, {'@': 291}), 113: (1, {'@': 291}), 114: (1, {'@': 291}), 32: (1, {'@': 291}), 57: (1, {'@': 291}), 117: (1, {'@': 291}), 118: (1, {'@': 291}), 119: (1, {'@': 291}), 121: (1, {'@': 291}), 122: (1, {'@': 291}), 124: (1, {'@': 291})}, 769: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 13: (0, 669), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 191: (0, 124), 81: (0, 786), 11: (0, 632), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 116: (0, 774), 155: (0, 651), 156: (0, 747), 93: (0, 630), 193: (0, 177), 98: (0, 544), 118: (0, 548), 157: (0, 552), 112: (0, 522), 78: (0, 511), 77: (0, 670), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 102: (0, 601), 194: (0, 414), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 192: (0, 472), 119: (0, 674), 150: (0, 741), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 770: {31: (0, 3), 126: (0, 96), 50: (0, 125), 17: (0, 369), 3: (0, 68), 127: (0, 47), 16: (0, 502), 39: (0, 25), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 771: {11: (1, {'@': 193}), 12: (1, {'@': 193}), 13: (1, {'@': 193})}, 772: {33: (1, {'@': 181}), 3: (1, {'@': 181})}, 773: {72: (1, {'@': 224}), 74: (1, {'@': 224}), 98: (1, {'@': 224}), 75: (1, {'@': 224}), 99: (1, {'@': 224}), 100: (1, {'@': 224}), 101: (1, {'@': 224}), 102: (1, {'@': 224}), 76: (1, {'@': 224}), 13: (1, {'@': 224}), 103: (1, {'@': 224}), 104: (1, {'@': 224}), 105: (1, {'@': 224}), 78: (1, {'@': 224}), 77: (1, {'@': 224}), 106: (1, {'@': 224}), 107: (1, {'@': 224}), 79: (1, {'@': 224}), 108: (1, {'@': 224}), 80: (1, {'@': 224}), 81: (1, {'@': 224}), 82: (1, {'@': 224}), 109: (1, {'@': 224}), 83: (1, {'@': 224}), 1: (1, {'@': 224}), 110: (1, {'@': 224}), 111: (1, {'@': 224}), 84: (1, {'@': 224}), 112: (1, {'@': 224}), 85: (1, {'@': 224}), 11: (1, {'@': 224}), 86: (1, {'@': 224}), 113: (1, {'@': 224}), 114: (1, {'@': 224}), 87: (1, {'@': 224}), 3: (1, {'@': 224}), 88: (1, {'@': 224}), 89: (1, {'@': 224}), 115: (1, {'@': 224}), 116: (1, {'@': 224}), 117: (1, {'@': 224}), 118: (1, {'@': 224}), 91: (1, {'@': 224}), 119: (1, {'@': 224}), 92: (1, {'@': 224}), 93: (1, {'@': 224}), 94: (1, {'@': 224}), 120: (1, {'@': 224}), 95: (1, {'@': 224}), 96: (1, {'@': 224}), 121: (1, {'@': 224}), 122: (1, {'@': 224}), 123: (1, {'@': 224}), 97: (1, {'@': 224}), 124: (1, {'@': 224})}, 774: {3: (0, 195)}, 775: {11: (0, 300), 12: (1, {'@': 206}), 13: (1, {'@': 206})}, 776: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 131: (0, 596), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 777: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 10: (0, 27), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 54: (0, 222), 131: (0, 69), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 778: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 131: (0, 517), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 779: {12: (0, 645), 187: (0, 537), 11: (1, {'@': 53}), 13: (1, {'@': 53})}, 780: {11: (1, {'@': 158}), 13: (1, {'@': 158}), 12: (1, {'@': 158})}, 781: {145: (0, 755), 96: (0, 772), 103: (0, 752), 146: (0, 692), 84: (0, 742), 104: (0, 735), 147: (0, 580), 105: (0, 588), 148: (0, 769), 149: (0, 779), 123: (0, 776), 110: (0, 724), 91: (0, 716), 81: (0, 786), 83: (0, 720), 95: (0, 638), 109: (0, 652), 3: (0, 688), 124: (0, 523), 1: (0, 573), 86: (0, 656), 151: (0, 717), 72: (0, 555), 101: (0, 699), 175: (0, 8), 100: (0, 685), 152: (0, 536), 92: (0, 604), 153: (0, 715), 154: (0, 780), 107: (0, 591), 114: (0, 759), 155: (0, 651), 116: (0, 774), 156: (0, 747), 93: (0, 630), 220: (0, 10), 13: (0, 599), 98: (0, 544), 11: (0, 602), 118: (0, 548), 157: (0, 552), 112: (0, 522), 44: (0, 12), 78: (0, 511), 77: (0, 670), 45: (0, 32), 158: (0, 564), 82: (0, 568), 94: (0, 584), 159: (0, 595), 150: (0, 705), 102: (0, 601), 79: (0, 589), 117: (0, 626), 80: (0, 639), 88: (0, 650), 99: (0, 644), 97: (0, 700), 106: (0, 762), 108: (0, 518), 120: (0, 550), 119: (0, 674), 160: (0, 746), 111: (0, 783), 161: (0, 729), 87: (0, 719), 115: (0, 710), 85: (0, 680), 89: (0, 401), 162: (0, 470), 121: (0, 434), 76: (0, 459)}, 782: {5: (0, 605), 11: (1, {'@': 78}), 13: (1, {'@': 78}), 12: (1, {'@': 78})}, 783: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 131: (0, 732), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486), 11: (1, {'@': 67}), 13: (1, {'@': 67}), 12: (1, {'@': 67})}, 784: {175: (0, 94), 229: (0, 543), 168: (0, 231), 120: (0, 550)}, 785: {179: (0, 9), 180: (0, 466), 177: (0, 72), 176: (0, 122), 182: (0, 248), 181: (0, 334), 178: (0, 31), 183: (0, 443), 184: (0, 313)}, 786: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 521), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 197: (0, 708), 1: (0, 614), 25: (0, 203), 134: (0, 433), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 196: (0, 216), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 131: (0, 66), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486)}, 787: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 131: (0, 83), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 95: (0, 542), 20: (0, 247), 101: (0, 265), 219: (0, 86), 138: (0, 287), 82: (0, 278), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 108: (0, 487), 139: (0, 505), 22: (0, 11), 42: (0, 233), 110: (0, 450), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 100: (0, 314), 48: (0, 486)}, 788: {11: (1, {'@': 345}), 12: (1, {'@': 345}), 13: (1, {'@': 345})}, 789: {31: (0, 3), 126: (0, 96), 50: (0, 125), 3: (0, 68), 127: (0, 341), 16: (0, 502), 39: (0, 25), 17: (0, 369), 128: (0, 318), 51: (0, 456), 129: (0, 497), 23: (0, 219), 14: (0, 493), 130: (0, 169), 19: (0, 150), 132: (0, 254), 9: (0, 154), 133: (0, 160), 18: (0, 428), 36: (0, 15), 34: (0, 6), 25: (0, 203), 134: (0, 433), 1: (0, 324), 35: (0, 397), 135: (0, 483), 136: (0, 403), 33: (0, 356), 29: (0, 474), 137: (0, 121), 27: (0, 462), 28: (0, 42), 49: (0, 373), 24: (0, 408), 47: (0, 237), 20: (0, 247), 138: (0, 287), 15: (0, 205), 32: (0, 504), 30: (0, 194), 52: (0, 354), 46: (0, 445), 40: (0, 503), 139: (0, 505), 22: (0, 11), 42: (0, 233), 41: (0, 273), 21: (0, 305), 53: (0, 141), 26: (0, 405), 38: (0, 50), 37: (0, 494), 48: (0, 486), 131: (0, 268)}}, 'start_states': {'start': 345}, 'end_states': {'start': 556}}, '__type__': 'ParsingFrontend'}, 'rules': [{'@': 0}, {'@': 1}, {'@': 2}, {'@': 3}, {'@': 4}, {'@': 5}, {'@': 6}, {'@': 7}, {'@': 8}, {'@': 9}, {'@': 10}, {'@': 11}, {'@': 12}, {'@': 13}, {'@': 14}, {'@': 15}, {'@': 16}, {'@': 17}, {'@': 18}, {'@': 19}, {'@': 20}, {'@': 21}, {'@': 22}, {'@': 23}, {'@': 24}, {'@': 25}, {'@': 26}, {'@': 27}, {'@': 28}, {'@': 29}, {'@': 30}, {'@': 31}, {'@': 32}, {'@': 33}, {'@': 34}, {'@': 35}, {'@': 36}, {'@': 37}, {'@': 38}, {'@': 39}, {'@': 40}, {'@': 41}, {'@': 42}, {'@': 43}, {'@': 44}, {'@': 45}, {'@': 46}, {'@': 47}, {'@': 48}, {'@': 49}, {'@': 50}, {'@': 51}, {'@': 52}, {'@': 53}, {'@': 54}, {'@': 55}, {'@': 56}, {'@': 57}, {'@': 58}, {'@': 59}, {'@': 60}, {'@': 61}, {'@': 62}, {'@': 63}, {'@': 64}, {'@': 65}, {'@': 66}, {'@': 67}, {'@': 68}, {'@': 69}, {'@': 70}, {'@': 71}, {'@': 72}, {'@': 73}, {'@': 74}, {'@': 75}, {'@': 76}, {'@': 77}, {'@': 78}, {'@': 79}, {'@': 80}, {'@': 81}, {'@': 82}, {'@': 83}, {'@': 84}, {'@': 85}, {'@': 86}, {'@': 87}, {'@': 88}, {'@': 89}, {'@': 90}, {'@': 91}, {'@': 92}, {'@': 93}, {'@': 94}, {'@': 95}, {'@': 96}, {'@': 97}, {'@': 98}, {'@': 99}, {'@': 100}, {'@': 101}, {'@': 102}, {'@': 103}, {'@': 104}, {'@': 105}, {'@': 106}, {'@': 107}, {'@': 108}, {'@': 109}, {'@': 110}, {'@': 111}, {'@': 112}, {'@': 113}, {'@': 114}, {'@': 115}, {'@': 116}, {'@': 117}, {'@': 118}, {'@': 119}, {'@': 120}, {'@': 121}, {'@': 122}, {'@': 123}, {'@': 124}, {'@': 125}, {'@': 126}, {'@': 127}, {'@': 128}, {'@': 129}, {'@': 130}, {'@': 131}, {'@': 132}, {'@': 133}, {'@': 134}, {'@': 135}, {'@': 136}, {'@': 137}, {'@': 138}, {'@': 139}, {'@': 140}, {'@': 141}, {'@': 142}, {'@': 143}, {'@': 144}, {'@': 145}, {'@': 146}, {'@': 147}, {'@': 148}, {'@': 149}, {'@': 150}, {'@': 151}, {'@': 152}, {'@': 153}, {'@': 154}, {'@': 155}, {'@': 156}, {'@': 157}, {'@': 158}, {'@': 159}, {'@': 160}, {'@': 161}, {'@': 162}, {'@': 163}, {'@': 164}, {'@': 165}, {'@': 166}, {'@': 167}, {'@': 168}, {'@': 169}, {'@': 170}, {'@': 171}, {'@': 172}, {'@': 173}, {'@': 174}, {'@': 175}, {'@': 176}, {'@': 177}, {'@': 178}, {'@': 179}, {'@': 180}, {'@': 181}, {'@': 182}, {'@': 183}, {'@': 184}, {'@': 185}, {'@': 186}, {'@': 187}, {'@': 188}, {'@': 189}, {'@': 190}, {'@': 191}, {'@': 192}, {'@': 193}, {'@': 194}, {'@': 195}, {'@': 196}, {'@': 197}, {'@': 198}, {'@': 199}, {'@': 200}, {'@': 201}, {'@': 202}, {'@': 203}, {'@': 204}, {'@': 205}, {'@': 206}, {'@': 207}, {'@': 208}, {'@': 209}, {'@': 210}, {'@': 211}, {'@': 212}, {'@': 213}, {'@': 214}, {'@': 215}, {'@': 216}, {'@': 217}, {'@': 218}, {'@': 219}, {'@': 220}, {'@': 221}, {'@': 222}, {'@': 223}, {'@': 224}, {'@': 225}, {'@': 226}, {'@': 227}, {'@': 228}, {'@': 229}, {'@': 230}, {'@': 231}, {'@': 232}, {'@': 233}, {'@': 234}, {'@': 235}, {'@': 236}, {'@': 237}, {'@': 238}, {'@': 239}, {'@': 240}, {'@': 241}, {'@': 242}, {'@': 243}, {'@': 244}, {'@': 245}, {'@': 246}, {'@': 247}, {'@': 248}, {'@': 249}, {'@': 250}, {'@': 251}, {'@': 252}, {'@': 253}, {'@': 254}, {'@': 255}, {'@': 256}, {'@': 257}, {'@': 258}, {'@': 259}, {'@': 260}, {'@': 261}, {'@': 262}, {'@': 263}, {'@': 264}, {'@': 265}, {'@': 266}, {'@': 267}, {'@': 268}, {'@': 269}, {'@': 270}, {'@': 271}, {'@': 272}, {'@': 273}, {'@': 274}, {'@': 275}, {'@': 276}, {'@': 277}, {'@': 278}, {'@': 279}, {'@': 280}, {'@': 281}, {'@': 282}, {'@': 283}, {'@': 284}, {'@': 285}, {'@': 286}, {'@': 287}, {'@': 288}, {'@': 289}, {'@': 290}, {'@': 291}, {'@': 292}, {'@': 293}, {'@': 294}, {'@': 295}, {'@': 296}, {'@': 297}, {'@': 298}, {'@': 299}, {'@': 300}, {'@': 301}, {'@': 302}, {'@': 303}, {'@': 304}, {'@': 305}, {'@': 306}, {'@': 307}, {'@': 308}, {'@': 309}, {'@': 310}, {'@': 311}, {'@': 312}, {'@': 313}, {'@': 314}, {'@': 315}, {'@': 316}, {'@': 317}, {'@': 318}, {'@': 319}, {'@': 320}, {'@': 321}, {'@': 322}, {'@': 323}, {'@': 324}, {'@': 325}, {'@': 326}, {'@': 327}, {'@': 328}, {'@': 329}, {'@': 330}, {'@': 331}, {'@': 332}, {'@': 333}, {'@': 334}, {'@': 335}, {'@': 336}, {'@': 337}, {'@': 338}, {'@': 339}, {'@': 340}, {'@': 341}, {'@': 342}, {'@': 343}, {'@': 344}, {'@': 345}, {'@': 346}, {'@': 347}, {'@': 348}, {'@': 349}, {'@': 350}, {'@': 351}, {'@': 352}, {'@': 353}, {'@': 354}, {'@': 355}, {'@': 356}, {'@': 357}, {'@': 358}, {'@': 359}, {'@': 360}, {'@': 361}, {'@': 362}, {'@': 363}, {'@': 364}, {'@': 365}, {'@': 366}, {'@': 367}, {'@': 368}, {'@': 369}, {'@': 370}, {'@': 371}, {'@': 372}, {'@': 373}, {'@': 374}, {'@': 375}, {'@': 376}, {'@': 377}, {'@': 378}, {'@': 379}, {'@': 380}, {'@': 381}, {'@': 382}, {'@': 383}, {'@': 384}, {'@': 385}, {'@': 386}, {'@': 387}, {'@': 388}, {'@': 389}, {'@': 390}, {'@': 391}, {'@': 392}, {'@': 393}, {'@': 394}, {'@': 395}, {'@': 396}, {'@': 397}, {'@': 398}, {'@': 399}, {'@': 400}, {'@': 401}, {'@': 402}, {'@': 403}, {'@': 404}, {'@': 405}, {'@': 406}, {'@': 407}, {'@': 408}, {'@': 409}, {'@': 410}, {'@': 411}, {'@': 412}, {'@': 413}, {'@': 414}, {'@': 415}, {'@': 416}, {'@': 417}], 'options': {'debug': False, 'strict': False, 'keep_all_tokens': False, 'tree_class': None, 'cache': False, 'cache_grammar': False, 'postlex': None, 'parser': 'lalr', 'lexer': 'contextual', 'transformer': None, 'start': ['start'], 'priority': 'normal', 'ambiguity': 'auto', 'regex': False, 'propagate_positions': False, 'lexer_callbacks': {}, 'maybe_placeholders': False, 'edit_terminals': None, 'g_regex_flags': 0, 'use_bytes': False, 'ordered_sets': True, 'import_paths': [], 'source_path': None, '_plugins': {}}, '__type__': 'Lark'} +) +MEMO = ( +{0: {'origin': {'name': 'start', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'start', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 1: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program_line', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'program_program_line', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 2: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'program_line', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'program', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 3: {'origin': {'name': 'program_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'preproc_line', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'program_line', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 4: {'origin': {'name': 'program_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label_line', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'program_line', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 5: {'origin': {'name': 'program_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'statements', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'program_line', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 6: {'origin': {'name': 'program_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'statements_co', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'program_line', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 7: {'origin': {'name': 'program_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'co_statements', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'program_line', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 8: {'origin': {'name': 'program_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'co_statements_co', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'program_line', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 9: {'origin': {'name': 'program_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 6, 'alias': 'program_line', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 10: {'origin': {'name': 'co_statements_co', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'co_statements', '__type__': 'NonTerminal'}, {'name': 'CO', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'co_statements_co', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': 2, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 11: {'origin': {'name': 'co_statements_co', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'co_statements_co', '__type__': 'NonTerminal'}, {'name': 'CO', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'co_statements_co', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': 2, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 12: {'origin': {'name': 'co_statements_co', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CO', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'co_statements_co', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': 2, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 13: {'origin': {'name': 'co_statements', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'co_statements_co', '__type__': 'NonTerminal'}, {'name': 'statement', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'co_statements', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 14: {'origin': {'name': 'statements_co', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'statements', '__type__': 'NonTerminal'}, {'name': 'CO', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'statements_co', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': 2, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 15: {'origin': {'name': 'statements_co', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'statements_co', '__type__': 'NonTerminal'}, {'name': 'CO', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'statements_co', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': 2, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 16: {'origin': {'name': 'statements', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'statement', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'statements_statement', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 17: {'origin': {'name': 'statements', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'statements_co', '__type__': 'NonTerminal'}, {'name': 'statement', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'statements_statement', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 18: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'var_decl', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'var_decls', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 19: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'function_declaration', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'staement_func_decl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 20: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BORDER', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'statement_border', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 21: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PLOT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'statement_plot', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 22: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PLOT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'attr_list', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'statement_plot_attr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 23: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DRAW', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 5, 'alias': 'statement_draw3', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 24: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DRAW', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'attr_list', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 6, 'alias': 'statement_draw3_attr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 25: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DRAW', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 7, 'alias': 'statement_draw', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 26: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DRAW', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'attr_list', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 8, 'alias': 'statement_draw_attr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 27: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CIRCLE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 9, 'alias': 'statement_circle', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 28: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CIRCLE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'attr_list', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 10, 'alias': 'statement_circle_attr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 29: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CLS', 'filter_out': False, '__type__': 'Terminal'}], 'order': 11, 'alias': 'statement_cls', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 30: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ASM', 'filter_out': False, '__type__': 'Terminal'}], 'order': 12, 'alias': 'statement_asm', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 31: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RANDOMIZE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 13, 'alias': 'statement_randomize', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 32: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RANDOMIZE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 14, 'alias': 'statement_randomize_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 33: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BEEP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 15, 'alias': 'statement_beep', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 34: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arg_list', '__type__': 'NonTerminal'}], 'order': 16, 'alias': 'statement_call', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 35: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arguments', '__type__': 'NonTerminal'}], 'order': 17, 'alias': 'statement_call', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 36: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 18, 'alias': 'statement_call', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 37: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'lexpr', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 19, 'alias': 'assignment', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 38: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 20, 'alias': 'array_copy', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 39: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 21, 'alias': 'array_copy', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 40: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arg_list', '__type__': 'NonTerminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 22, 'alias': 'arr_assignment', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 41: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arg_list', '__type__': 'NonTerminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 23, 'alias': 'arr_assignment', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 42: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 24, 'alias': 'substr_assignment_no_let', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 43: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arg_list', '__type__': 'NonTerminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 25, 'alias': 'substr_assignment', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 44: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'substr', '__type__': 'NonTerminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 26, 'alias': 'str_assign', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 45: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'substr', '__type__': 'NonTerminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 27, 'alias': 'str_assign', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 46: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'goto', '__type__': 'NonTerminal'}, {'name': 'NUMBER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 28, 'alias': 'goto', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 47: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'goto', '__type__': 'NonTerminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 29, 'alias': 'goto', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 48: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_then_part', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 30, 'alias': 'if_sentence', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 49: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_then_part', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 31, 'alias': 'if_sentence', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 50: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_then_part', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'statements_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 32, 'alias': 'if_sentence', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 51: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_then_part', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'co_statements_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 33, 'alias': 'if_sentence', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 52: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_then_part', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'label', '__type__': 'NonTerminal'}, {'name': 'statements_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 34, 'alias': 'if_sentence', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 53: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_inline', '__type__': 'NonTerminal'}], 'order': 35, 'alias': 'statement_if', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 54: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_then_part', '__type__': 'NonTerminal'}, {'name': 'statements_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 36, 'alias': 'statement_if_then_endif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 55: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_then_part', '__type__': 'NonTerminal'}, {'name': 'co_statements_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 37, 'alias': 'statement_if_then_endif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 56: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_then_part', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'elseiflist', '__type__': 'NonTerminal'}], 'order': 38, 'alias': 'if_elseif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 57: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_then_part', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'elseiflist', '__type__': 'NonTerminal'}], 'order': 39, 'alias': 'if_elseif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 58: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_inline', '__type__': 'NonTerminal'}, {'name': 'else_part_inline', '__type__': 'NonTerminal'}], 'order': 40, 'alias': 'if_inline', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 59: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_then_part', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'else_part', '__type__': 'NonTerminal'}], 'order': 41, 'alias': 'if_else', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 60: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'for_start', '__type__': 'NonTerminal'}, {'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'label_next', '__type__': 'NonTerminal'}], 'order': 42, 'alias': 'for_sentence', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 61: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'for_start', '__type__': 'NonTerminal'}, {'name': 'co_statements_co', '__type__': 'NonTerminal'}, {'name': 'label_next', '__type__': 'NonTerminal'}], 'order': 43, 'alias': 'for_sentence', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 62: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'for_start', '__type__': 'NonTerminal'}, {'name': 'program', '__type__': 'NonTerminal'}, {'name': 'label_next', '__type__': 'NonTerminal'}], 'order': 44, 'alias': 'for_sentence', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 63: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'END', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 45, 'alias': 'end', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 64: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'END', 'filter_out': False, '__type__': 'Terminal'}], 'order': 46, 'alias': 'end', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 65: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ERROR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 47, 'alias': 'error_raise', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 66: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'STOP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 48, 'alias': 'stop_raise', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 67: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'STOP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 49, 'alias': 'stop_raise', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 68: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'do_start', '__type__': 'NonTerminal'}, {'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'label_loop', '__type__': 'NonTerminal'}], 'order': 50, 'alias': 'do_loop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 69: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'do_start', '__type__': 'NonTerminal'}, {'name': 'label_loop', '__type__': 'NonTerminal'}], 'order': 51, 'alias': 'do_loop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 70: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'label_loop', '__type__': 'NonTerminal'}], 'order': 52, 'alias': 'do_loop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 71: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'do_start', '__type__': 'NonTerminal'}, {'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'label_loop', '__type__': 'NonTerminal'}, {'name': 'UNTIL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 53, 'alias': 'do_loop_until', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 72: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'do_start', '__type__': 'NonTerminal'}, {'name': 'label_loop', '__type__': 'NonTerminal'}, {'name': 'UNTIL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 54, 'alias': 'do_loop_until', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 73: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'label_loop', '__type__': 'NonTerminal'}, {'name': 'UNTIL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 55, 'alias': 'do_loop_until', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 74: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DATA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arguments', '__type__': 'NonTerminal'}], 'order': 56, 'alias': 'data', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 75: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RESTORE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 57, 'alias': 'restore', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 76: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RESTORE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 58, 'alias': 'restore', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 77: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RESTORE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NUMBER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 59, 'alias': 'restore', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 78: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'READ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arguments', '__type__': 'NonTerminal'}], 'order': 60, 'alias': 'read', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 79: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'do_start', '__type__': 'NonTerminal'}, {'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'label_loop', '__type__': 'NonTerminal'}, {'name': 'WHILE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 61, 'alias': 'do_loop_while', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 80: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'do_start', '__type__': 'NonTerminal'}, {'name': 'label_loop', '__type__': 'NonTerminal'}, {'name': 'WHILE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 62, 'alias': 'do_loop_while', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 81: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'label_loop', '__type__': 'NonTerminal'}, {'name': 'WHILE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 63, 'alias': 'do_loop_while', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 82: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'do_while_start', '__type__': 'NonTerminal'}, {'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'LOOP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 64, 'alias': 'do_while_loop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 83: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'do_while_start', '__type__': 'NonTerminal'}, {'name': 'co_statements_co', '__type__': 'NonTerminal'}, {'name': 'LOOP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 65, 'alias': 'do_while_loop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 84: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'do_while_start', '__type__': 'NonTerminal'}, {'name': 'LOOP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 66, 'alias': 'do_while_loop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 85: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'do_until_start', '__type__': 'NonTerminal'}, {'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'LOOP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 67, 'alias': 'do_until_loop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 86: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'do_until_start', '__type__': 'NonTerminal'}, {'name': 'co_statements_co', '__type__': 'NonTerminal'}, {'name': 'LOOP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 68, 'alias': 'do_until_loop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 87: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'do_until_start', '__type__': 'NonTerminal'}, {'name': 'LOOP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 69, 'alias': 'do_until_loop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 88: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'while_start', '__type__': 'NonTerminal'}, {'name': 'co_statements_co', '__type__': 'NonTerminal'}, {'name': 'label_end_while', '__type__': 'NonTerminal'}], 'order': 70, 'alias': 'while_sentence', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 89: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'while_start', '__type__': 'NonTerminal'}, {'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'label_end_while', '__type__': 'NonTerminal'}], 'order': 71, 'alias': 'while_sentence', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 90: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EXIT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'WHILE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 72, 'alias': 'exit', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 91: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EXIT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DO', 'filter_out': False, '__type__': 'Terminal'}], 'order': 73, 'alias': 'exit', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 92: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EXIT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'FOR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 74, 'alias': 'exit', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 93: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONTINUE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'WHILE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 75, 'alias': 'continue_', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 94: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONTINUE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'DO', 'filter_out': False, '__type__': 'Terminal'}], 'order': 76, 'alias': 'continue_', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 95: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONTINUE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'FOR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 77, 'alias': 'continue_', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 96: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PRINT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'print_list', '__type__': 'NonTerminal'}], 'order': 78, 'alias': 'print_sentence', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 97: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ON', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'goto', '__type__': 'NonTerminal'}, {'name': 'label_list', '__type__': 'NonTerminal'}], 'order': 79, 'alias': 'on_goto', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 98: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RETURN', 'filter_out': False, '__type__': 'Terminal'}], 'order': 80, 'alias': 'return_', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 99: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RETURN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 81, 'alias': 'return_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 100: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PAUSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 82, 'alias': 'pause', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 101: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'POKE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 83, 'alias': 'poke', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 102: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'POKE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 84, 'alias': 'poke', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 103: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'POKE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'numbertype', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 85, 'alias': 'poke2', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 104: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'POKE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'numbertype', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 86, 'alias': 'poke2', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 105: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'POKE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'numbertype', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 87, 'alias': 'poke3', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 106: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'POKE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'numbertype', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 88, 'alias': 'poke3', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 107: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OUT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 89, 'alias': 'out', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 108: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ITALIC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 90, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 109: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BOLD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 91, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 110: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INK', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 92, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 111: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PAPER', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 93, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 112: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BRIGHT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 94, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 113: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'FLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 95, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 114: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OVER', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 96, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 115: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INVERSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 97, 'alias': 'simple_instruction', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 116: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SAVE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'CODE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 98, 'alias': 'save_code', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 117: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SAVE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 99, 'alias': 'save_code', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 118: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SAVE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 100, 'alias': 'save_code', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 119: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SAVE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'DATA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 101, 'alias': 'save_data', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 120: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SAVE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'DATA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 102, 'alias': 'save_data', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 121: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SAVE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'DATA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 103, 'alias': 'save_data', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 122: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'load_or_verify', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 104, 'alias': 'load_code', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 123: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'load_or_verify', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'CODE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 105, 'alias': 'load_code', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 124: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'load_or_verify', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'CODE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 106, 'alias': 'load_code', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 125: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'load_or_verify', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'CODE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 107, 'alias': 'load_code', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 126: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'load_or_verify', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'DATA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 108, 'alias': 'load_data', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 127: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'load_or_verify', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'DATA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 109, 'alias': 'load_data', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 128: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'load_or_verify', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'DATA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 110, 'alias': 'load_data', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 129: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 111, 'alias': 'array_eq_error', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 130: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arg_list', '__type__': 'NonTerminal'}, {'name': 'substr', '__type__': 'NonTerminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 112, 'alias': 'let_arr_substr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 131: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arg_list', '__type__': 'NonTerminal'}, {'name': 'substr', '__type__': 'NonTerminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 113, 'alias': 'let_arr_substr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 132: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arg_list', '__type__': 'NonTerminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 114, 'alias': 'let_arr_substr_single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 133: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arg_list', '__type__': 'NonTerminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 115, 'alias': 'let_arr_substr_single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 134: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arguments', '__type__': 'NonTerminal'}, {'name': 'TO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 116, 'alias': 'let_arr_substr_in_args', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 135: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arguments', '__type__': 'NonTerminal'}, {'name': 'TO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 117, 'alias': 'let_arr_substr_in_args', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 136: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arguments', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'TO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 118, 'alias': 'let_arr_substr_in_args2', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 137: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arguments', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'TO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 119, 'alias': 'let_arr_substr_in_args2', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 138: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arguments', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'TO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 120, 'alias': 'let_arr_substr_in_args3', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 139: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arguments', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'TO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 121, 'alias': 'let_arr_substr_in_args3', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 140: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arguments', '__type__': 'NonTerminal'}, {'name': 'TO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 122, 'alias': 'let_arr_substr_in_args4', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 141: {'origin': {'name': 'statement', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arguments', '__type__': 'NonTerminal'}, {'name': 'TO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 123, 'alias': 'let_arr_substr_in_args4', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 142: {'origin': {'name': 'label', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LABEL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'label', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 143: {'origin': {'name': 'label_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}, {'name': 'statements', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'program_line_label', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 144: {'origin': {'name': 'label_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}, {'name': 'co_statements', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'program_line_label', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 145: {'origin': {'name': 'label_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label_line_co', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'label_line_label_line_co', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 146: {'origin': {'name': 'label_line_co', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}, {'name': 'statements_co', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'label_line_co', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 147: {'origin': {'name': 'label_line_co', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}, {'name': 'co_statements_co', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'label_line_co', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 148: {'origin': {'name': 'label_line_co', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'label_line_co', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 149: {'origin': {'name': 'program_co', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'program_line_co', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': 2, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 150: {'origin': {'name': 'program_co', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'label_line_co', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'program_line_co', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': 2, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 151: {'origin': {'name': 'program_co', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'co_statements_co', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'program_line_co', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': 2, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 152: {'origin': {'name': 'program_co', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'statements_co', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'program_line_co', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': 2, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 153: {'origin': {'name': 'var_decl', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DIM', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'idlist', '__type__': 'NonTerminal'}, {'name': 'typedef', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'var_decl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 154: {'origin': {'name': 'var_decl', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DIM', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'idlist', '__type__': 'NonTerminal'}, {'name': 'typedef', '__type__': 'NonTerminal'}, {'name': 'AT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'var_decl_at', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 155: {'origin': {'name': 'var_decl', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DIM', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'idlist', '__type__': 'NonTerminal'}, {'name': 'typedef', '__type__': 'NonTerminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'var_decl_ini', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 156: {'origin': {'name': 'var_decl', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONST', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'idlist', '__type__': 'NonTerminal'}, {'name': 'typedef', '__type__': 'NonTerminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'var_decl_ini', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 157: {'origin': {'name': 'var_decl', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'var_arr_decl', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'arr_decl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 158: {'origin': {'name': 'var_decl', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'var_arr_decl_addr', '__type__': 'NonTerminal'}], 'order': 5, 'alias': 'arr_decl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 159: {'origin': {'name': 'var_decl', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DIM', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'idlist', '__type__': 'NonTerminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bound_list', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'typedef', '__type__': 'NonTerminal'}, {'name': 'RIGHTARROW', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'const_vector', '__type__': 'NonTerminal'}], 'order': 6, 'alias': 'arr_decl_initialized', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 160: {'origin': {'name': 'var_decl', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DIM', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'idlist', '__type__': 'NonTerminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bound_list', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'typedef', '__type__': 'NonTerminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'const_vector', '__type__': 'NonTerminal'}], 'order': 7, 'alias': 'arr_decl_initialized', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 161: {'origin': {'name': 'singleid', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'singleid', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 162: {'origin': {'name': 'singleid', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'singleid', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 163: {'origin': {'name': 'idlist', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'singleid', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'idlist_id', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 164: {'origin': {'name': 'idlist', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'idlist', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'singleid', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'idlist_idlist_id', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 165: {'origin': {'name': 'var_arr_decl_addr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'var_arr_decl', '__type__': 'NonTerminal'}, {'name': 'AT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'arr_decl_attr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 166: {'origin': {'name': 'var_arr_decl', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DIM', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'idlist', '__type__': 'NonTerminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bound_list', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'typedef', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'decl_arr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 167: {'origin': {'name': 'bound_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bound', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'bound_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 168: {'origin': {'name': 'bound_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bound_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bound', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'bound_list_bound', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 169: {'origin': {'name': 'bound', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'bound', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 170: {'origin': {'name': 'bound', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'TO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'bound_to_bound', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 171: {'origin': {'name': 'const_vector', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LBRACE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'const_vector_list', '__type__': 'NonTerminal'}, {'name': 'RBRACE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'const_vector', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 172: {'origin': {'name': 'const_vector', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LBRACE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'const_number_list', '__type__': 'NonTerminal'}, {'name': 'RBRACE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'const_vector', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 173: {'origin': {'name': 'const_number_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'const_vector_elem_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 174: {'origin': {'name': 'const_number_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'const_number_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'const_vector_elem_list_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 175: {'origin': {'name': 'const_vector_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'const_vector', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'const_vector_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 176: {'origin': {'name': 'const_vector_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'const_vector_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'const_vector', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'const_vector_vector_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 177: {'origin': {'name': 'lexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'lexpr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 178: {'origin': {'name': 'lexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LET', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'lexpr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 179: {'origin': {'name': 'goto', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'GO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'TO', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'go', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 180: {'origin': {'name': 'goto', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'GO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'SUB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'go', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 181: {'origin': {'name': 'goto', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'GOTO', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'go', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 182: {'origin': {'name': 'goto', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'GOSUB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'go', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 183: {'origin': {'name': 'endif', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'END_IF', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'endif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 184: {'origin': {'name': 'endif', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}, {'name': 'END_IF', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'endif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 185: {'origin': {'name': 'endif', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ENDIF', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'endif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 186: {'origin': {'name': 'endif', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}, {'name': 'ENDIF', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'endif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 187: {'origin': {'name': 'if_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_then_part', '__type__': 'NonTerminal'}, {'name': 'statements', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'single_line_if', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 188: {'origin': {'name': 'if_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_then_part', '__type__': 'NonTerminal'}, {'name': 'co_statements_co', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'single_line_if', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 189: {'origin': {'name': 'if_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_then_part', '__type__': 'NonTerminal'}, {'name': 'statements_co', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'single_line_if', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 190: {'origin': {'name': 'if_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_then_part', '__type__': 'NonTerminal'}, {'name': 'co_statements', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'single_line_if', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 191: {'origin': {'name': 'elseif_expr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ELSEIF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'then', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'elseif_part', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 192: {'origin': {'name': 'elseif_expr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}, {'name': 'ELSEIF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'then', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'elseif_part', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 193: {'origin': {'name': 'elseiflist', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'elseif_expr', '__type__': 'NonTerminal'}, {'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'elseif_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 194: {'origin': {'name': 'elseiflist', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'elseif_expr', '__type__': 'NonTerminal'}, {'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'else_part', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'elseif_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 195: {'origin': {'name': 'elseiflist', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'elseif_expr', '__type__': 'NonTerminal'}, {'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'elseiflist', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'elseif_elseiflist', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 196: {'origin': {'name': 'else_part_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'else_part_endif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 197: {'origin': {'name': 'else_part_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'statements_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'else_part_endif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 198: {'origin': {'name': 'else_part_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'co_statements_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'else_part_endif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 199: {'origin': {'name': 'else_part_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'else_part_endif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 200: {'origin': {'name': 'else_part_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'label', '__type__': 'NonTerminal'}, {'name': 'statements_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'else_part_endif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 201: {'origin': {'name': 'else_part_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'label', '__type__': 'NonTerminal'}, {'name': 'co_statements_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 5, 'alias': 'else_part_endif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 202: {'origin': {'name': 'else_part_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'statements_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 6, 'alias': 'else_part_endif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 203: {'origin': {'name': 'else_part_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'co_statements_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 7, 'alias': 'else_part_endif', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 204: {'origin': {'name': 'else_part_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'statements_co', '__type__': 'NonTerminal'}], 'order': 8, 'alias': 'else_part', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 205: {'origin': {'name': 'else_part_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'co_statements_co', '__type__': 'NonTerminal'}], 'order': 9, 'alias': 'else_part', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 206: {'origin': {'name': 'else_part_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'co_statements', '__type__': 'NonTerminal'}], 'order': 10, 'alias': 'else_part', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 207: {'origin': {'name': 'else_part_inline', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'statements', '__type__': 'NonTerminal'}], 'order': 11, 'alias': 'else_part', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 208: {'origin': {'name': 'else_part', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'else_part_inline', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'else_part_is_inline', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 209: {'origin': {'name': 'else_part', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}, {'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'else_part_label', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 210: {'origin': {'name': 'else_part', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}, {'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'statements_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'else_part_label', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 211: {'origin': {'name': 'else_part', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}, {'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'co_statements_co', '__type__': 'NonTerminal'}, {'name': 'endif', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'else_part_label', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 212: {'origin': {'name': 'if_then_part', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'then', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'if_then_part', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 213: {'origin': {'name': 'then', '__type__': 'NonTerminal'}, 'expansion': [], 'order': 0, 'alias': 'then', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 214: {'origin': {'name': 'then', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'THEN', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'then', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 215: {'origin': {'name': 'label_next', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}, {'name': 'NEXT', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'next', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 216: {'origin': {'name': 'label_next', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NEXT', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'next', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 217: {'origin': {'name': 'label_next', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}, {'name': 'NEXT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'next1', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 218: {'origin': {'name': 'label_next', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NEXT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'next1', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 219: {'origin': {'name': 'for_start', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'FOR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'TO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'step', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'for_sentence_start', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 220: {'origin': {'name': 'step', '__type__': 'NonTerminal'}, 'expansion': [], 'order': 0, 'alias': 'step', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 221: {'origin': {'name': 'step', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'STEP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'step_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 222: {'origin': {'name': 'label_loop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}, {'name': 'LOOP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'loop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 223: {'origin': {'name': 'label_loop', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LOOP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'loop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 224: {'origin': {'name': 'do_while_start', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'WHILE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'do_while_start', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 225: {'origin': {'name': 'do_until_start', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'UNTIL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'do_until_start', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 226: {'origin': {'name': 'do_start', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'CO', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'do_start', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 227: {'origin': {'name': 'do_start', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'do_start', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 228: {'origin': {'name': 'label_end_while', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}, {'name': 'WEND', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'label_end_while', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 229: {'origin': {'name': 'label_end_while', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label', '__type__': 'NonTerminal'}, {'name': 'END_WHILE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'label_end_while', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 230: {'origin': {'name': 'label_end_while', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'WEND', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'label_end_while', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 231: {'origin': {'name': 'label_end_while', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'END_WHILE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'label_end_while', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 232: {'origin': {'name': 'while_start', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'WHILE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'while_start', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 233: {'origin': {'name': 'print_elem', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'print_elem_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 234: {'origin': {'name': 'print_elem', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'print_at', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'print_list_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 235: {'origin': {'name': 'print_elem', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'print_tab', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'print_list_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 236: {'origin': {'name': 'print_elem', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'attr', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'print_list_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 237: {'origin': {'name': 'print_elem', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BOLD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'print_list_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 238: {'origin': {'name': 'print_elem', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ITALIC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 5, 'alias': 'print_list_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 239: {'origin': {'name': 'print_elem', '__type__': 'NonTerminal'}, 'expansion': [], 'order': 6, 'alias': 'print_list_epsilon', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 240: {'origin': {'name': 'attr_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'attr', '__type__': 'NonTerminal'}, {'name': 'SC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'attr_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 241: {'origin': {'name': 'attr_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'attr_list', '__type__': 'NonTerminal'}, {'name': 'attr', '__type__': 'NonTerminal'}, {'name': 'SC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'attr_list_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 242: {'origin': {'name': 'attr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'OVER', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'attr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 243: {'origin': {'name': 'attr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INVERSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'attr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 244: {'origin': {'name': 'attr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INK', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'attr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 245: {'origin': {'name': 'attr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PAPER', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'attr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 246: {'origin': {'name': 'attr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BRIGHT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'attr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 247: {'origin': {'name': 'attr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'FLASH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 5, 'alias': 'attr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 248: {'origin': {'name': 'print_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'print_elem', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'print_list_elem', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 249: {'origin': {'name': 'print_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'print_list', '__type__': 'NonTerminal'}, {'name': 'SC', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'print_elem', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'print_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 250: {'origin': {'name': 'print_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'print_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'print_elem', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'print_list_comma', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 251: {'origin': {'name': 'print_at', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'AT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'print_list_at', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 252: {'origin': {'name': 'print_tab', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'TAB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'print_list_tab', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 253: {'origin': {'name': 'label_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'label_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 254: {'origin': {'name': 'label_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NUMBER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'label_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 255: {'origin': {'name': 'label_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'label_list_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 256: {'origin': {'name': 'label_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'label_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NUMBER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'label_list_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 257: {'origin': {'name': 'load_or_verify', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LOAD', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'load_or_verify', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 258: {'origin': {'name': 'load_or_verify', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'VERIFY', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'load_or_verify', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 259: {'origin': {'name': 'numbertype', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BYTE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'numbertype', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 260: {'origin': {'name': 'numbertype', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'UBYTE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'numbertype', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 261: {'origin': {'name': 'numbertype', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INTEGER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'numbertype', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 262: {'origin': {'name': 'numbertype', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'UINTEGER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'numbertype', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 263: {'origin': {'name': 'numbertype', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LONG', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'numbertype', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 264: {'origin': {'name': 'numbertype', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ULONG', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'numbertype', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 265: {'origin': {'name': 'numbertype', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'FIXED', 'filter_out': False, '__type__': 'Terminal'}], 'order': 6, 'alias': 'numbertype', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 266: {'origin': {'name': 'numbertype', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'FLOAT', 'filter_out': False, '__type__': 'Terminal'}], 'order': 7, 'alias': 'numbertype', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 267: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'lp_expr_rp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 268: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NUMBER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'number_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 269: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PI', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'expr_pi', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 270: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'string', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'expr_string', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 271: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'id_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 272: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADDRESSOF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'singleid', '__type__': 'NonTerminal'}], 'order': 5, 'alias': 'addr_of_id', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 273: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'func_call', '__type__': 'NonTerminal'}], 'order': 6, 'alias': 'expr_funccall', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 274: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADDRESSOF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arg_list', '__type__': 'NonTerminal'}], 'order': 7, 'alias': 'addr_of_array_element', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 275: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ADDRESSOF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arg_list', '__type__': 'NonTerminal'}], 'order': 8, 'alias': 'err_undefined_arr_access', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 276: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bexpr', '__type__': 'NonTerminal'}], 'order': 9, 'alias': 'bexpr_func', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 277: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'USR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bexpr', '__type__': 'NonTerminal'}], 'order': 10, 'alias': 'expr_usr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 278: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RND', 'filter_out': False, '__type__': 'Terminal'}], 'order': 11, 'alias': 'expr_rnd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 279: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RND', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 12, 'alias': 'expr_rnd', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 280: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PEEK', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bexpr', '__type__': 'NonTerminal'}], 'order': 13, 'alias': 'expr_peek', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 281: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PEEK', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'numbertype', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 14, 'alias': 'expr_peektype_', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 282: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bexpr', '__type__': 'NonTerminal'}], 'order': 15, 'alias': 'expr_in', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 283: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LBOUND', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 16, 'alias': 'expr_lbound', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 284: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'UBOUND', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 17, 'alias': 'expr_lbound', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 285: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LBOUND', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 18, 'alias': 'expr_lbound_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 286: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'UBOUND', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 19, 'alias': 'expr_lbound_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 287: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LEN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bexpr', '__type__': 'NonTerminal'}], 'order': 20, 'alias': 'len', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 288: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SIZEOF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'type', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 21, 'alias': 'sizeof', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 289: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SIZEOF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 22, 'alias': 'sizeof', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 290: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SIZEOF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 23, 'alias': 'sizeof', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 291: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'VAL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bexpr', '__type__': 'NonTerminal'}], 'order': 24, 'alias': 'val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 292: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CODE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bexpr', '__type__': 'NonTerminal'}], 'order': 25, 'alias': 'code', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 293: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SGN', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bexpr', '__type__': 'NonTerminal'}], 'order': 26, 'alias': 'sgn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 294: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'math_fn', '__type__': 'NonTerminal'}, {'name': 'bexpr', '__type__': 'NonTerminal'}], 'order': 27, 'alias': 'expr_trig', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 295: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bexpr', '__type__': 'NonTerminal'}], 'order': 28, 'alias': 'expr_int', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 296: {'origin': {'name': 'bexpr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ABS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bexpr', '__type__': 'NonTerminal'}], 'order': 29, 'alias': 'abs', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 297: {'origin': {'name': 'string', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'func_call', '__type__': 'NonTerminal'}, {'name': 'substr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'string_func_call', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 298: {'origin': {'name': 'string', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'func_call', '__type__': 'NonTerminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'string_func_call_single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 299: {'origin': {'name': 'string', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'STRC', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'string_str', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 300: {'origin': {'name': 'string', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'string', '__type__': 'NonTerminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'string_lprp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 301: {'origin': {'name': 'string', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'string', '__type__': 'NonTerminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'string_lp_expr_rp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 302: {'origin': {'name': 'string', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'substr', '__type__': 'NonTerminal'}], 'order': 5, 'alias': 'expr_id_substr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 303: {'origin': {'name': 'string', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'string', '__type__': 'NonTerminal'}, {'name': 'substr', '__type__': 'NonTerminal'}], 'order': 6, 'alias': 'string_substr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 304: {'origin': {'name': 'string', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'substr', '__type__': 'NonTerminal'}], 'order': 7, 'alias': 'string_expr_lp', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 305: {'origin': {'name': 'string', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'STR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bexpr', '__type__': 'NonTerminal'}], 'order': 8, 'alias': 'str', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 306: {'origin': {'name': 'string', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INKEY', 'filter_out': False, '__type__': 'Terminal'}], 'order': 9, 'alias': 'inkey', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 307: {'origin': {'name': 'string', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CHR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'bexpr', '__type__': 'NonTerminal'}], 'order': 10, 'alias': 'chr_one', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 308: {'origin': {'name': 'string', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CHR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arg_list', '__type__': 'NonTerminal'}], 'order': 11, 'alias': 'chr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 309: {'origin': {'name': 'substr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'TO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'subind_str', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 310: {'origin': {'name': 'substr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'TO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'subind_strto', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 311: {'origin': {'name': 'substr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'TO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'subind_tostr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 312: {'origin': {'name': 'substr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'TO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'subind_to', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 313: {'origin': {'name': 'func_call', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arg_list', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'idcall_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 314: {'origin': {'name': 'func_call', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arg_list', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'arr_access_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 315: {'origin': {'name': 'arg_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'arg_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 316: {'origin': {'name': 'arg_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arguments', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'arg_list_arg', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 317: {'origin': {'name': 'arguments', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'argument', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'arguments', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 318: {'origin': {'name': 'arguments', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'arguments', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'argument', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'arguments_argument', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 319: {'origin': {'name': 'argument', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'argument', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 320: {'origin': {'name': 'argument', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'WEQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'named_argument', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 321: {'origin': {'name': 'argument', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ARRAY_ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'argument_array', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 322: {'origin': {'name': 'function_declaration', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'function_header', '__type__': 'NonTerminal'}, {'name': 'function_body', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'funcdecl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 323: {'origin': {'name': 'function_declaration', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DECLARE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'function_header_pre', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'funcdeclforward', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 324: {'origin': {'name': 'function_header', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'function_header_pre', '__type__': 'NonTerminal'}, {'name': 'CO', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'function_header', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 325: {'origin': {'name': 'function_header', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'function_header_pre', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'function_header', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 326: {'origin': {'name': 'function_header_pre', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'function_def', '__type__': 'NonTerminal'}, {'name': 'param_decl', '__type__': 'NonTerminal'}, {'name': 'typedef', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'function_header_pre', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 327: {'origin': {'name': 'function_def', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'FUNCTION', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'convention', '__type__': 'NonTerminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'function_def', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 328: {'origin': {'name': 'function_def', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SUB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'convention', '__type__': 'NonTerminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'function_def', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 329: {'origin': {'name': 'convention', '__type__': 'NonTerminal'}, 'expansion': [], 'order': 0, 'alias': 'convention', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 330: {'origin': {'name': 'convention', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'STDCALL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'convention', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 331: {'origin': {'name': 'convention', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'FASTCALL', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'convention2', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 332: {'origin': {'name': 'param_decl', '__type__': 'NonTerminal'}, 'expansion': [], 'order': 0, 'alias': 'param_decl_none', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 333: {'origin': {'name': 'param_decl', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'param_decl_none', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 334: {'origin': {'name': 'param_decl', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'param_decl_list', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'param_decl', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 335: {'origin': {'name': 'param_decl_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'param_definition', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'param_decl_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 336: {'origin': {'name': 'param_decl_list', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'param_decl_list', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'param_definition', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'param_decl_list2', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 337: {'origin': {'name': 'param_definition', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BYREF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'param_def', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'param_byref_definition', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 338: {'origin': {'name': 'param_definition', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BYVAL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'param_def', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'param_byval_definition', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 339: {'origin': {'name': 'param_definition', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'param_def', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'param_definition', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 340: {'origin': {'name': 'param_def', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'singleid', '__type__': 'NonTerminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'typedef', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'param_def_array', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 341: {'origin': {'name': 'param_def', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'singleid', '__type__': 'NonTerminal'}, {'name': 'typedef', '__type__': 'NonTerminal'}, {'name': 'default_arg_value', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'param_def_type', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 342: {'origin': {'name': 'default_arg_value', '__type__': 'NonTerminal'}, 'expansion': [], 'order': 0, 'alias': 'param_def_default_arg_value', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 343: {'origin': {'name': 'default_arg_value', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'param_def_default_arg_value', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 344: {'origin': {'name': 'function_body', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'END_FUNCTION', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'function_body', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 345: {'origin': {'name': 'function_body', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program_co', '__type__': 'NonTerminal'}, {'name': 'END_SUB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'function_body', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 346: {'origin': {'name': 'function_body', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'statements_co', '__type__': 'NonTerminal'}, {'name': 'END_FUNCTION', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'function_body', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 347: {'origin': {'name': 'function_body', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'statements_co', '__type__': 'NonTerminal'}, {'name': 'END_SUB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'function_body', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 348: {'origin': {'name': 'function_body', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'co_statements_co', '__type__': 'NonTerminal'}, {'name': 'END_FUNCTION', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'function_body', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 349: {'origin': {'name': 'function_body', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'co_statements_co', '__type__': 'NonTerminal'}, {'name': 'END_SUB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'function_body', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 350: {'origin': {'name': 'function_body', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'END_FUNCTION', 'filter_out': False, '__type__': 'Terminal'}], 'order': 6, 'alias': 'function_body', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 351: {'origin': {'name': 'function_body', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'END_SUB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 7, 'alias': 'function_body', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 352: {'origin': {'name': 'typedef', '__type__': 'NonTerminal'}, 'expansion': [], 'order': 0, 'alias': 'type_def_empty', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 353: {'origin': {'name': 'typedef', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'AS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'type', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'type_def', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 354: {'origin': {'name': 'typedef', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'AS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'type_def_id', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 355: {'origin': {'name': 'type', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BYTE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'type', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 356: {'origin': {'name': 'type', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'UBYTE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'type', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 357: {'origin': {'name': 'type', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INTEGER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'type', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 358: {'origin': {'name': 'type', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'UINTEGER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'type', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 359: {'origin': {'name': 'type', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LONG', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'type', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 360: {'origin': {'name': 'type', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ULONG', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'type', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 361: {'origin': {'name': 'type', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'FIXED', 'filter_out': False, '__type__': 'Terminal'}], 'order': 6, 'alias': 'type', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 362: {'origin': {'name': 'type', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'FLOAT', 'filter_out': False, '__type__': 'Terminal'}], 'order': 7, 'alias': 'type', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 363: {'origin': {'name': 'type', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}], 'order': 8, 'alias': 'type', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 364: {'origin': {'name': 'preproc_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': '_INIT', 'filter_out': True, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'preproc_line_init', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 365: {'origin': {'name': 'preproc_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': '_REQUIRE', 'filter_out': True, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'preproc_line_require', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 366: {'origin': {'name': 'preproc_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': '_PRAGMA', 'filter_out': True, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'preproc_line_pragma_option', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 367: {'origin': {'name': 'preproc_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': '_PRAGMA', 'filter_out': True, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'preproc_line_pragma_option', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 368: {'origin': {'name': 'preproc_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': '_PRAGMA', 'filter_out': True, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'INTEGER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'preproc_line_pragma_option', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 369: {'origin': {'name': 'preproc_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': '_PRAGMA', 'filter_out': True, '__type__': 'Terminal'}, {'name': '_PUSH', 'filter_out': True, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'preproc_pragma_push', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 370: {'origin': {'name': 'preproc_line', '__type__': 'NonTerminal'}, 'expansion': [{'name': '_PRAGMA', 'filter_out': True, '__type__': 'Terminal'}, {'name': '_POP', 'filter_out': True, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 6, 'alias': 'preproc_pragma_pop', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 371: {'origin': {'name': 'math_fn', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SIN', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'math_fn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 372: {'origin': {'name': 'math_fn', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'COS', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'math_fn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 373: {'origin': {'name': 'math_fn', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'TAN', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'math_fn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 374: {'origin': {'name': 'math_fn', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ASN', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'math_fn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 375: {'origin': {'name': 'math_fn', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ACS', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'math_fn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 376: {'origin': {'name': 'math_fn', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ATN', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'math_fn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 377: {'origin': {'name': 'math_fn', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LN', 'filter_out': False, '__type__': 'Terminal'}], 'order': 6, 'alias': 'math_fn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 378: {'origin': {'name': 'math_fn', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'EXP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 7, 'alias': 'math_fn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 379: {'origin': {'name': 'math_fn', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SQR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 8, 'alias': 'math_fn', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 380: {'origin': {'name': 'expr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_or', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 381: {'origin': {'name': 'expr_or', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_or', '__type__': 'NonTerminal'}, {'name': 'OR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_and', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_or_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 382: {'origin': {'name': 'expr_or', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_and', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 383: {'origin': {'name': 'expr_and', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_and', '__type__': 'NonTerminal'}, {'name': 'AND', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_xor', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_and_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 384: {'origin': {'name': 'expr_and', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_xor', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 385: {'origin': {'name': 'expr_xor', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_xor', '__type__': 'NonTerminal'}, {'name': 'XOR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_not', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_xor_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 386: {'origin': {'name': 'expr_xor', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_not', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 387: {'origin': {'name': 'expr_not', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NOT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_not', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'not_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 388: {'origin': {'name': 'expr_not', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_cmp', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 389: {'origin': {'name': 'expr_cmp', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_cmp', '__type__': 'NonTerminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_bor', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_eq_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 390: {'origin': {'name': 'expr_cmp', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_cmp', '__type__': 'NonTerminal'}, {'name': 'NE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_bor', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'expr_ne_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 391: {'origin': {'name': 'expr_cmp', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_cmp', '__type__': 'NonTerminal'}, {'name': 'LT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_bor', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'expr_lt_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 392: {'origin': {'name': 'expr_cmp', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_cmp', '__type__': 'NonTerminal'}, {'name': 'LE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_bor', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'expr_le_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 393: {'origin': {'name': 'expr_cmp', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_cmp', '__type__': 'NonTerminal'}, {'name': 'GT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_bor', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'expr_gt_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 394: {'origin': {'name': 'expr_cmp', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_cmp', '__type__': 'NonTerminal'}, {'name': 'GE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_bor', '__type__': 'NonTerminal'}], 'order': 5, 'alias': 'expr_ge_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 395: {'origin': {'name': 'expr_cmp', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bor', '__type__': 'NonTerminal'}], 'order': 6, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 396: {'origin': {'name': 'expr_bor', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bor', '__type__': 'NonTerminal'}, {'name': 'BOR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_bxor', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_bor_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 397: {'origin': {'name': 'expr_bor', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bxor', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 398: {'origin': {'name': 'expr_bxor', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bxor', '__type__': 'NonTerminal'}, {'name': 'BAND', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_add', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_band_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 399: {'origin': {'name': 'expr_bxor', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bxor', '__type__': 'NonTerminal'}, {'name': 'BXOR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_add', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'expr_bxor_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 400: {'origin': {'name': 'expr_bxor', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bxor', '__type__': 'NonTerminal'}, {'name': 'SHL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_add', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'expr_shl_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 401: {'origin': {'name': 'expr_bxor', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_bxor', '__type__': 'NonTerminal'}, {'name': 'SHR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_add', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'expr_shr_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 402: {'origin': {'name': 'expr_bxor', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_add', '__type__': 'NonTerminal'}], 'order': 4, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 403: {'origin': {'name': 'expr_add', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_add', '__type__': 'NonTerminal'}, {'name': 'PLUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_mod', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_plus_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 404: {'origin': {'name': 'expr_add', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_add', '__type__': 'NonTerminal'}, {'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_mod', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'expr_minus_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 405: {'origin': {'name': 'expr_add', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'BNOT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_add', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'bnot_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 406: {'origin': {'name': 'expr_add', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_mod', '__type__': 'NonTerminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 407: {'origin': {'name': 'expr_mod', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_mod', '__type__': 'NonTerminal'}, {'name': 'MOD', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_mul', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_mod_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 408: {'origin': {'name': 'expr_mod', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_mul', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 409: {'origin': {'name': 'expr_mul', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_mul', '__type__': 'NonTerminal'}, {'name': 'MUL', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_unary', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_mul_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 410: {'origin': {'name': 'expr_mul', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_mul', '__type__': 'NonTerminal'}, {'name': 'DIV', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_unary', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'expr_div_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 411: {'origin': {'name': 'expr_mul', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_unary', '__type__': 'NonTerminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 412: {'origin': {'name': 'expr_unary', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'MINUS', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_unary', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'minus_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 413: {'origin': {'name': 'expr_unary', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_pow', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 414: {'origin': {'name': 'expr_pow', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_pow', '__type__': 'NonTerminal'}, {'name': 'POW', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_pow', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_pow_expr', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 415: {'origin': {'name': 'expr_pow', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_cast', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 416: {'origin': {'name': 'expr_cast', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CAST', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'numbertype', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'cast', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 417: {'origin': {'name': 'expr_cast', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'bexpr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}} +) +Shift = 0 +Reduce = 1 +def Lark_StandAlone(**kwargs): + return Lark._load_from_dict(DATA, MEMO, **kwargs) diff --git a/src/zxbpp/__init__.py b/src/zxbpp/__init__.py index 0ce13993f..b467db136 100644 --- a/src/zxbpp/__init__.py +++ b/src/zxbpp/__init__.py @@ -1 +1,8 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from .zxbpp import entry_point # noqa diff --git a/src/zxbpp/base_pplex.py b/src/zxbpp/base_pplex.py index 678ed3376..cf5c79959 100644 --- a/src/zxbpp/base_pplex.py +++ b/src/zxbpp/base_pplex.py @@ -1,20 +1,17 @@ -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# -# This is the Lexer for the ZXBppASM (ZXBASM Preprocessor) -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import os import sys from collections.abc import Callable, Iterable from dataclasses import dataclass -from enum import Enum, unique +from enum import StrEnum, unique -from src.api import utils -from src.ply import lex +from src.api import lex, utils from src.zxbpp.prepro import output from src.zxbpp.prepro.builtinmacro import BuiltinMacro from src.zxbpp.prepro.definestable import DefinesTable @@ -28,7 +25,7 @@ @unique -class ReservedDirectives(str, Enum): +class ReservedDirectives(StrEnum): INCLUDE = "INCLUDE" ONCE = "ONCE" DEFINE = "DEFINE" diff --git a/src/zxbpp/prepro/__init__.py b/src/zxbpp/prepro/__init__.py index 6d5436d63..df68e91dd 100644 --- a/src/zxbpp/prepro/__init__.py +++ b/src/zxbpp/prepro/__init__.py @@ -1,4 +1,9 @@ -# vim: ts=4:sw=4:et: +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .args import Arg, ArgList from .definestable import DefinesTable diff --git a/src/zxbpp/prepro/args.py b/src/zxbpp/prepro/args.py index 6ac2cc33c..4db709bd2 100644 --- a/src/zxbpp/prepro/args.py +++ b/src/zxbpp/prepro/args.py @@ -1,4 +1,9 @@ -# vim:ts=4:et:sw=4: +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from .macrocall import MacroCall diff --git a/src/zxbpp/prepro/builtinmacro.py b/src/zxbpp/prepro/builtinmacro.py index 5afb641cc..7f56bac78 100644 --- a/src/zxbpp/prepro/builtinmacro.py +++ b/src/zxbpp/prepro/builtinmacro.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from collections.abc import Callable from src.zxbpp.prepro import DefinesTable diff --git a/src/zxbpp/prepro/definestable.py b/src/zxbpp/prepro/definestable.py index 682327fb2..38f1d47cc 100644 --- a/src/zxbpp/prepro/definestable.py +++ b/src/zxbpp/prepro/definestable.py @@ -1,4 +1,9 @@ -# vim: ts=4:sw=4:et: +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- """Class for a Table of Defines. Each identifier has a dictionary entry. diff --git a/src/zxbpp/prepro/exceptions.py b/src/zxbpp/prepro/exceptions.py index cbd0f9271..05c1bf8e5 100644 --- a/src/zxbpp/prepro/exceptions.py +++ b/src/zxbpp/prepro/exceptions.py @@ -1,3 +1,11 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + + class PreprocError(Exception): """Denotes an exception in the preprocessor""" diff --git a/src/zxbpp/prepro/id_.py b/src/zxbpp/prepro/id_.py index 79617cd1b..322b40e2f 100644 --- a/src/zxbpp/prepro/id_.py +++ b/src/zxbpp/prepro/id_.py @@ -1,4 +1,9 @@ -# vim: ts=4:sw=4:et: +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- __doc__ = """ A class for an identifier parsed by the preprocessor. It contains it's name, arguments and macro value. @@ -48,7 +53,7 @@ def __str__(self): return self.name @staticmethod - def __dumptable(table: "prepro.DefinesTable") -> None: + def __dumptable(table: prepro.DefinesTable) -> None: """Dumps table on screen for debugging purposes""" for k, v in table.table.items(): sys.stdout.write(f"{k}\t<--- {v} {type(v)}") diff --git a/src/zxbpp/prepro/macrocall.py b/src/zxbpp/prepro/macrocall.py index ef9c239ac..880dda9d6 100644 --- a/src/zxbpp/prepro/macrocall.py +++ b/src/zxbpp/prepro/macrocall.py @@ -1,8 +1,12 @@ -# vim:ts=4:et:sw=4: +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import copy import re -from typing import Union from src.api.debug import __DEBUG__ from src.zxbpp import prepro @@ -21,7 +25,7 @@ class MacroCall: __slots__ = "callargs", "fname", "id_", "lineno", "table" - def __init__(self, fname: str, lineno: int, table: "prepro.DefinesTable", id_: Union["MacroCall", str], args=None): + def __init__(self, fname: str, lineno: int, table: prepro.DefinesTable, id_: MacroCall | str, args=None): """Initializes the object with the ID table, the ID name and optionally, the passed args. """ @@ -39,7 +43,7 @@ def eval(arg) -> str: """ return str(arg()) # Evaluate the arg (could be a macrocall) - def __call__(self, symbolTable: "prepro.DefinesTable" = None) -> str: + def __call__(self, symbolTable: prepro.DefinesTable | None = None) -> str: """Execute the macro call using LAZY evaluation""" if isinstance(self.id_, MacroCall): self.id_ = self.id_() @@ -97,7 +101,7 @@ def __call__(self, symbolTable: "prepro.DefinesTable" = None) -> str: tmp = id_(table, self) return tmp - def is_defined(self, symbolTable: "prepro.DefinesTable" = None) -> bool: + def is_defined(self, symbolTable: prepro.DefinesTable | None = None) -> bool: """True if this macro has been defined""" if symbolTable is None: symbolTable = self.table diff --git a/src/zxbpp/prepro/operators.py b/src/zxbpp/prepro/operators.py index 555938b7f..2589b5fd9 100644 --- a/src/zxbpp/prepro/operators.py +++ b/src/zxbpp/prepro/operators.py @@ -1,4 +1,9 @@ -# Operators implemented in the preprocessor +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- from src.zxbpp.prepro import DefinesTable from src.zxbpp.prepro.macrocall import MacroCall @@ -17,7 +22,7 @@ def __init__(self, fname: str, lineno: int, table: DefinesTable, left: MacroCall self.left = left self.right = right - def __call__(self, symbolTable: DefinesTable = None) -> str: + def __call__(self, symbolTable: DefinesTable | None = None) -> str: return self.left(symbolTable).rstrip() + self.right(symbolTable).lstrip() @@ -36,5 +41,5 @@ def stringize(s: str) -> str: s = s.replace('"', '""') return f'"{s}"' - def __call__(self, symbolTable: DefinesTable = None) -> str: + def __call__(self, symbolTable: DefinesTable | None = None) -> str: return self.stringize(self.macro_call(symbolTable)) diff --git a/src/zxbpp/prepro/output.py b/src/zxbpp/prepro/output.py index 05fc10887..949bd8535 100644 --- a/src/zxbpp/prepro/output.py +++ b/src/zxbpp/prepro/output.py @@ -1,4 +1,9 @@ -# vim:ts=4:et:sw=4: +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- """Common output functions for the preprocessor. Need the global OPTION object diff --git a/src/zxbpp/zxbasmpplex.py b/src/zxbpp/zxbasmpplex.py index 4c9df04c9..2aceac0bd 100755 --- a/src/zxbpp/zxbasmpplex.py +++ b/src/zxbpp/zxbasmpplex.py @@ -1,17 +1,15 @@ #!/usr/bin/python3 -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# -# This is the Lexer for the ZXBppASM (ZXBASM Preprocessor) -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import sys -from src.ply import lex +from src.api import lex from src.zxbpp.base_pplex import BaseLexer, ReservedDirectives from src.zxbpp.prepro.definestable import DefinesTable @@ -41,6 +39,7 @@ "OR", "STRING", "TEXT", + "CO", "TOKEN", "NEWLINE", "_ENDFILE_", @@ -50,10 +49,12 @@ "EQ", "PUSH", "POP", + "LB", "LP", "LLP", "RRP", "RP", + "RB", "COMMA", "CONTINUE", "NUMBER", @@ -135,6 +136,18 @@ def t_prepro_define_pragma_defargs_defargsopt_CONTINUE(self, t): t.type = "NEWLINE" return t + def t_prepro_LB(self, t): + r"\[" + return t + + def t_prepro_RB(self, t): + r"\]" + return t + + def t_prepro_CO(self, t): + r"\:" + return t + # Any other character is ignored until EOL def t_singlecomment_comment_Skip(self, t): r".+" diff --git a/src/zxbpp/zxbpp.py b/src/zxbpp/zxbpp.py index 36dabed3f..7d9010058 100755 --- a/src/zxbpp/zxbpp.py +++ b/src/zxbpp/zxbpp.py @@ -1,26 +1,21 @@ #!/usr/bin/env python3 -# vim: ts=4:sw=4:et: -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# -# This is the Parser for the ZXBpp (ZXBasic Preprocessor) -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import argparse import os import re import sys from dataclasses import dataclass -from enum import Enum, unique -from typing import Any, NamedTuple +from enum import StrEnum, unique +from typing import Any, Final, NamedTuple -from src import arch from src.api import config, global_, utils -from src.ply import yacc from src.zxbpp import zxbasmpplex, zxbpplex from src.zxbpp.base_pplex import STDIN from src.zxbpp.prepro import ID, Arg, ArgList, DefinesTable, MacroCall, output @@ -30,13 +25,18 @@ from src.zxbpp.prepro.output import error, warning from src.zxbpp.zxbpplex import tokens # noqa +from .zxbpp_standalone import Lark_StandAlone, Lexer, Token, Transformer, UnexpectedInput + @unique -class PreprocMode(str, Enum): +class PreprocMode(StrEnum): BASIC = "BASIC" ASM = "ASM" +# List of available architectures +AVAILABLE_ARCHITECTURES: Final[list[str]] = [] + # Generated output OUTPUT = "" @@ -54,6 +54,9 @@ class PreprocMode(str, Enum): # Default include path INCLUDEPATH: list[str] = ["stdlib", "runtime"] +# Include paths for every arch +INCLUDE_MAP: Final[dict[str, list[str]]] = {} + # Enabled to FALSE if IFDEF failed ENABLED: bool = True @@ -74,26 +77,17 @@ class ParentIncludingFile(NamedTuple): @dataclass class IncludedFileInfo: - once: bool # whether this file is + once: bool # whether this file is to be included only once (e.g. #pragma once) parents: list[ParentIncludingFile] -# Files already includes, with a list of file, line where they were -# included sinc a file can be included more than once. +# Files already included, with a list of (file, line) tuples where they were +# included, since a file can be included more than once. INCLUDED: dict[str, IncludedFileInfo] = {} # IFDEFS array IFDEFS: list[IfDef] = [] # Push (Line, state here) -precedence = ( - ("nonassoc", "DUMMY"), - ("left", "OR"), - ("left", "AND"), - ("left", "EQ", "NE", "LT", "LE", "GT", "GE"), - ("right", "LLP"), - ("left", "PASTE", "STRINGIZING"), -) - def remove_spaces(x: str) -> str: if not x: @@ -123,6 +117,11 @@ def init(): global ENABLED global IFDEFS + from src import arch + + AVAILABLE_ARCHITECTURES.clear() + AVAILABLE_ARCHITECTURES.extend(arch.AVAILABLE_ARCHITECTURES) + config.OPTIONS(config.Action.ADD_IF_NOT_DEFINED, name="debug_zxbpp", type=bool, default=False) global_.FILENAME = STDIN OUTPUT = "" @@ -138,17 +137,35 @@ def init(): reset_id_table() -def get_include_path() -> str: - """Default include path using a tricky sys calls.""" +def get_include_path(arch: str = "") -> str: + """Default include path using a tricky sys call.""" return os.path.realpath( - os.path.join(os.path.dirname(__file__), os.path.pardir, "lib", "arch", config.OPTIONS.architecture or "") + os.path.join( + os.path.dirname(__file__), + os.path.pardir, + "lib", + "arch", + arch or config.OPTIONS.architecture or "", + ) ) def set_include_path(): global INCLUDEPATH - pwd = get_include_path() - INCLUDEPATH = [os.path.join(pwd, "stdlib"), os.path.join(pwd, "runtime")] + + INCLUDE_MAP.clear() + + for arch_ in AVAILABLE_ARCHITECTURES: + pwd = get_include_path(arch_) + INCLUDE_MAP[arch_] = [os.path.join(pwd, "stdlib"), os.path.join(pwd, "runtime")] + + # zx81sd inherits runtime files from zx48k (only overrides specific ones). + # zx81sd paths take priority: its own files shadow zx48k equivalents. + if "zx81sd" in INCLUDE_MAP: + zx48k_pwd = get_include_path("zx48k") + INCLUDE_MAP["zx81sd"].extend([os.path.join(zx48k_pwd, "stdlib"), os.path.join(zx48k_pwd, "runtime")]) + + INCLUDEPATH = INCLUDE_MAP.get(config.OPTIONS.architecture, []) def setMode(mode: PreprocMode) -> None: @@ -166,7 +183,7 @@ def setMode(mode: PreprocMode) -> None: LEXER = lexers[PreprocMode(mode)] -def search_filename(fname: str, lineno: int, local_first: bool) -> str: +def search_filename(fname: str, lineno: int, local_first: bool, arch: str = "") -> str: """Search a filename into the list of the include path. If local_first is true, it will try first in the current directory of the file being analyzed. @@ -174,7 +191,9 @@ def search_filename(fname: str, lineno: int, local_first: bool) -> str: fname = utils.sanitize_filename(fname) assert CURRENT_DIR is not None - i_path: list[str] = [CURRENT_DIR] + INCLUDEPATH if local_first else list(INCLUDEPATH) + include_path = INCLUDE_MAP.get(arch, INCLUDEPATH) + + i_path: list[str] = [CURRENT_DIR] + include_path if local_first else list(include_path) i_path.extend(config.OPTIONS.include_path.split(":") if config.OPTIONS.include_path else []) if os.path.isabs(fname): @@ -182,7 +201,7 @@ def search_filename(fname: str, lineno: int, local_first: bool) -> str: return fname else: for dir_ in i_path: - path = utils.sanitize_filename(os.path.join(dir_, fname)) + path = utils.get_absolute_filename_path(utils.sanitize_filename(os.path.join(dir_, fname))) if os.path.exists(path): return path @@ -190,9 +209,9 @@ def search_filename(fname: str, lineno: int, local_first: bool) -> str: return "" -def include_file(filename: str, lineno: int, local_first: bool) -> str: +def include_file(filename: str, lineno: int, local_first: bool, arch: str = "") -> str: """Performs a file inclusion (#include) in the preprocessor. - Writes down that "filename" was included at the current file, + Writes down that "filename" was included in the current file, at line . If local_first is True, then it will first search the file in the @@ -201,8 +220,9 @@ def include_file(filename: str, lineno: int, local_first: bool) -> str: """ global CURRENT_DIR - filename = search_filename(filename, lineno, local_first) - abs_filename = utils.get_absolute_filename_path(filename) + abs_filename = search_filename(filename, lineno, local_first, arch=arch) + filename = utils.get_relative_filename_path(abs_filename) + if abs_filename not in INCLUDED: INCLUDED[abs_filename] = IncludedFileInfo(once=False, parents=[]) elif INCLUDED[abs_filename].once: @@ -218,30 +238,27 @@ def include_file(filename: str, lineno: int, local_first: bool) -> str: return LEXER.include(filename) -def include_once(filename: str, lineno: int, local_first: bool) -> str: +def include_once(filename: str, lineno: int, local_first: bool, arch: str = "") -> str: """Performs a file inclusion (#include) in the preprocessor. - Writes down that "filename" was included at the current file, + Writes down that "filename" was included in the current file, at line . The file is ignored if it was previously included (a warning will - be emitted though). + be emitted, though). If local_first is True, then it will first search the file in the local path before looking for it in the include path chain. This is used when doing a #include "filename". """ - filename = search_filename(filename, lineno, local_first) - abs_filename = utils.get_absolute_filename_path(filename) + abs_filename = search_filename(filename, lineno, local_first, arch=arch) + if abs_filename not in INCLUDED: # If not already included - return include_file(filename, lineno, local_first) # include it and return + return include_file(filename, lineno, local_first, arch) # include it and return # Now checks if the file has been included more than once if len(INCLUDED[abs_filename].parents) > 1: parent_file, lineno = INCLUDED[abs_filename].parents[0] - warning( - lineno, - "file '%s' already included more than once, in file '%s' at line %i" % (filename, parent_file, lineno), - ) + warning(lineno, f"file '{filename}' already included more than once, in file '{parent_file}' at line {lineno}") # Empty file (already included) LEXER.next_token = "_ENDFILE_" @@ -278,576 +295,471 @@ def to_int(expr: str | int) -> int: return expr -# -------- GRAMMAR RULES for the preprocessor --------- -def p_start(p): - """start : program""" - global OUTPUT - - OUTPUT += "".join(p[1]) - - -def p_program(p): - """program : include_file - | line - | init - | undef - | ifdef - | require - | pragma - | errormsg - | warningmsg - """ - p[0] = p[1] - - -def p_program_tokenstring(p): - """program : defs NEWLINE""" - tmp = expand_macros(p[1], p.lineno(2)) - if tmp is None: - p[0] = [] - return - - p[0] = [tmp] - - -def p_program_tokenstring_2(p): - """program : define NEWLINE""" - p[0] = p[1] + [p[2]] - - -def p_program_char(p): - """program : program include_file - | program line - | program init - | program undef - | program ifdef - | program require - | program pragma - | program errormsg - | program warningmsg - """ - p[0] = p[1] + p[2] - - -def p_program_newline(p): - """program : program defs NEWLINE""" - tmp = expand_macros(p[2], p.lineno(3)) - if tmp is None: - p[0] = [] - return - - p[0] = p[1] - p[0].append(tmp) - - -def p_program_newline_2(p): - """program : program define NEWLINE""" - p[0] = p[1] + [f'#line {p.lineno(3) + 1} "{output.CURRENT_FILE[-1]}"\n'] - - -def p_token(p): - """token : STRING - | TOKEN - | CONTINUE - | SEPARATOR - | NUMBER - """ - p[0] = p[1] - - -def p_include_file(p): - """include_file : include NEWLINE program _ENDFILE_""" - global CURRENT_DIR - p[0] = [p[1] + p[2]] + p[3] + [p[4]] - output.CURRENT_FILE.pop() # Remove top of the stack - CURRENT_DIR = os.path.dirname(output.CURRENT_FILE[-1]) - - -def p_include_file_empty(p): - """include_file : include NEWLINE _ENDFILE_""" # This happens when an IFDEF is FALSE - p[0] = [p[2]] - - -def p_include_once_empty(p): - """include_file : include_once NEWLINE _ENDFILE_""" - p[0] = [p[2]] # Include once already included. Nothing done. - - -def p_include_once_ok(p): - """include_file : include_once NEWLINE program _ENDFILE_""" - global CURRENT_DIR - p[0] = [p[1] + p[2]] + p[3] + [p[4]] - output.CURRENT_FILE.pop() # Remove top of the stack - CURRENT_DIR = os.path.dirname(output.CURRENT_FILE[-1]) - - -def p_include_fname(p): - """include : INCLUDE FILENAME""" - if ENABLED: - p[0] = include_file(p[2], p.lineno(2), local_first=False) - else: - p[0] = [] - p.lexer.next_token = "_ENDFILE_" - - -def p_include_macro(p): - """include : INCLUDE expr""" - global_fist = RE_GLOBAL_FIRST_FILENAME.match(p[2]) - local_first = RE_LOCAL_FIRST_FILENAME.match(p[2]) - if global_fist is None and local_first is None: - error(p.lineno(1), f"invalid filename {p[2]}") - p[0] = [] - return - - if ENABLED: - p[0] = include_file(p[2][1:-1], p.lineno(2), local_first=local_first is not None) - else: - p[0] = [] - p.lexer.next_token = "_ENDFILE_" - - -def p_include_once(p): - """include_once : INCLUDE ONCE STRING""" - if ENABLED: - p[0] = include_once(p[3][1:-1], p.lineno(3), local_first=True) - else: - p[0] = [] - - if not p[0]: - p.lexer.next_token = "_ENDFILE_" - - -def p_include_once_fname(p): - """include_once : INCLUDE ONCE FILENAME""" - p[0] = [] - - if ENABLED: - p[0] = include_once(p[3], p.lineno(3), local_first=False) - else: - p[0] = [] - - if not p[0]: - p.lexer.next_token = "_ENDFILE_" - - -def p_line(p): - """line : LINE INTEGER NEWLINE""" - if ENABLED: - p[0] = ["#%s %s%s" % (p[1], p[2], p[3])] - else: - p[0] = [] - - -def p_line_file(p): - """line : LINE INTEGER STRING NEWLINE""" - if ENABLED: - p[0] = ['#%s %s "%s"%s' % (p[1], p[2], p[3], p[4])] - else: - p[0] = [] - - -def p_require_file(p): - """require : REQUIRE STRING NEWLINE""" - p[0] = ["#%s %s\n" % (p[1], utils.sanitize_filename(p[2]))] - - -def p_init(p): - """init : INIT ID NEWLINE""" - p[0] = ['#%s "%s"\n' % (p[1], p[2])] - - -def p_init_str(p): - """init : INIT STRING NEWLINE""" - p[0] = ["#%s %s\n" % (p[1], p[2])] - - -def p_undef(p): - """undef : UNDEF ID""" - if ENABLED: - ID_TABLE.undef(p[2]) - - p[0] = [] - - -def p_errormsg(p): - """errormsg : ERROR TEXT""" - if ENABLED: - error(p.lineno(1), p[2]) - p[0] = [] - - -def p_warningmsg(p): - """warningmsg : WARNING TEXT""" - if ENABLED: - warning(p.lineno(1), p[2]) - p[0] = [] - - -def p_define(p): - """define : DEFINE ID params defs""" - id_ = p[2] - params = p[3] - defs = p[4] - - if ENABLED: - if defs: - if isinstance(defs[0], str) and defs[0] in " \t": # remove leading whitespaces - defs[0] = defs[0].lstrip(" \t") - else: - output.warning_missing_whitespace_after_macro(p.lineno(1), p.lexer.current_file) - - ID_TABLE.define( - id_, - args=params, - value=defs, - lineno=p.lineno(2), - fname=output.CURRENT_FILE[-1], - ) - p[0] = [] - - -def p_define_params_epsilon(p): - """params :""" - p[0] = None - - -def p_define_params_empty(p): - """params : LP RP""" - # Defines the 'epsilon' parameter - p[0] = [ID("", value="", args=None, lineno=p.lineno(1), fname=output.CURRENT_FILE[-1])] - - -def p_define_params_paramlist(p): - """params : LP paramlist RP""" - for i in p[2]: - if not isinstance(i, ID): - error(p.lineno(3), '"%s" might not appear in a macro parameter list' % str(i)) - p[0] = None - return - - names = [x.name for x in p[2]] - for i in range(len(names)): - if names[i] in names[i + 1 :]: - error(p.lineno(3), 'Duplicated name parameter "%s"' % (names[i])) - p[0] = None - return - - p[0] = p[2] - - -def p_paramlist_single(p): - """paramlist : ID""" - p[0] = [ID(p[1], value="", args=None, lineno=p.lineno(1), fname=output.CURRENT_FILE[-1])] - - -def p_paramlist_paramlist(p): - """paramlist : paramlist COMMA ID""" - p[0] = p[1] + [ID(p[3], value="", args=None, lineno=p.lineno(1), fname=output.CURRENT_FILE[-1])] - +class PPToken(Token): + pass -def p_pragma_id(p): - """pragma : PRAGMA ID""" - p[0] = ["#%s %s" % (p[1], p[2])] +class LarkLexerAdapter(Lexer): + def __init__(self, lexer_conf: Any) -> None: + pass -def p_pragma_id_expr(p): - """pragma : PRAGMA ID EQ ID - | PRAGMA ID EQ INTEGER - """ - p[0] = ["#%s %s %s %s" % (p[1], p[2], p[3], p[4])] - - -def p_pragma_id_string(p): - """pragma : PRAGMA ID EQ STRING""" - p[0] = ["#%s %s %s %s" % (p[1], p[2], p[3], p[4][1:-1])] - - -def p_pragma_push(p): - """pragma : PRAGMA PUSH LP ID RP - | PRAGMA POP LP ID RP - """ - p[0] = ["#%s %s%s%s%s" % (p[1], p[2], p[3], p[4], p[5])] - - -def p_pragma_once(p): - """pragma : PRAGMA ONCE""" - abs_filename = utils.get_absolute_filename_path(output.CURRENT_FILE[-1]) - if abs_filename not in INCLUDED: - INCLUDED[abs_filename] = IncludedFileInfo(once=False, parents=[]) - - INCLUDED[abs_filename].once = True - p[0] = [] + def lex(self, data: Any, parser_state: Any = None) -> Any: # type: ignore[override] + lexer = data + while True: + if lexer.next_token is not None: + tok_type = "ENDFILE" if lexer.next_token == "_ENDFILE_" else lexer.next_token + lexer.next_token = None + t = PPToken(tok_type, "", line=lexer.lineno, column=1) + t.fname = lexer.current_file + yield t + continue + tok = lexer.token() + if tok is None: + break -def p_ifdef(p): - """ifdef : if_header NEWLINE program ENDIF""" - global ENABLED - - if ENABLED: - p[0] = [p[2]] + p[3] - else: - p[0] = [] - - p[0] += ['#line %i "%s"' % (p.lineno(4) + 1, output.CURRENT_FILE[-1])] - ENABLED = IFDEFS.pop().enabled - - -def p_ifdef_else(p): - """ifdef : ifdefelsea ifdefelseb ENDIF""" - global ENABLED - - ENABLED = IFDEFS.pop().enabled - if ENABLED: - p[0] = p[1] + p[2] - else: - p[0] = [] - - p[0] += ['#line %i "%s"' % (p.lineno(3) + 1, output.CURRENT_FILE[-1])] - - -def p_ifdef_else_a(p): - """ifdefelsea : if_header NEWLINE program""" - global ENABLED - - p[0] = [] - if IFDEFS[-1].enabled: - if p[1]: - p[0] = [p[2]] + p[3] - ENABLED = not p[1] - - -def p_ifdef_else_b(p): - """ifdefelseb : ELSE NEWLINE program""" - global ENABLED - - if ENABLED: - p[0] = ['#line %i "%s"%s' % (p.lineno(1) + 1, output.CURRENT_FILE[-1], p[2])] - p[0] += p[3] - else: - p[0] = [] - - -def p_if_header(p): - """if_header : IFDEF ID""" - global ENABLED - - IFDEFS.append(IfDef(ENABLED, p.lineno(2))) - if ENABLED: - ENABLED = ID_TABLE.defined(p[2]) - - p[0] = ENABLED - - -def p_ifn_header(p): - """if_header : IFNDEF ID""" - global ENABLED - - IFDEFS.append(IfDef(ENABLED, p.lineno(2))) - if ENABLED: - ENABLED = not ID_TABLE.defined(p[2]) - - p[0] = ENABLED - - -def p_if_expr_header(p): - """if_header : IF expr""" - global ENABLED - - IFDEFS.append(IfDef(ENABLED, p.lineno(2))) - if ENABLED: - ENABLED = bool(int(p[2])) if p[2].isdigit() else ID_TABLE.defined(p[2]) - - p[0] = ENABLED - - -def p_expr(p): - """expr : macrocall""" - p[0] = str(p[1]()).strip() - - -def p_expr_val(p): - """expr : NUMBER""" - p[0] = p[1] - - -def p_expr_str(p): - """expr : STRING""" - p[0] = p[1] - + tok_type = "ENDFILE" if tok.type == "_ENDFILE_" else tok.type + t = PPToken( + tok_type, tok.value, line=tok.lineno, column=lexer.find_column(tok) if tok.type != "_ENDFILE_" else 1 + ) + t.fname = tok.fname + yield t -def p_exprand(p): - """expr : expr AND expr""" - p[0] = "1" if to_bool(p[1]) and to_bool(p[3]) else "0" +class ZxbppTransformer(Transformer): + def start(self, items): + global OUTPUT + OUTPUT += "".join(items[0]) + return items[0] -def p_expror(p): - """expr : expr OR expr""" - p[0] = "1" if to_bool(p[1]) or to_bool(p[3]) else "0" + def program(self, items): + return items[0] + def program_tokenstring(self, items): + tmp = expand_macros(items[0], items[1].line) + if tmp is None: + return [] + return [tmp] -def p_exprne(p): - """expr : expr NE expr""" - p[0] = "1" if p[1] != p[3] else "0" + def program_tokenstring_2(self, items): + return items[0] + [items[1]] + def program_char(self, items): + return items[0] + items[1] -def p_expreq(p): - """expr : expr EQ expr""" - p[0] = "1" if p[1] == p[3] else "0" + def program_newline(self, items): + tmp = expand_macros(items[1], items[2].line) + if tmp is None: + return [] + res = list(items[0]) + res.append(tmp) + return res + def program_newline_2(self, items): + return items[0] + [f'#line {items[2].line + 1} "{items[2].fname}"\n'] -def p_exprlt(p): - """expr : expr LT expr""" - p[0] = "1" if to_int(p[1]) < to_int(p[3]) else "0" + def token(self, items): + return items[0] + def include_file(self, items): + p0 = [items[0] + items[1]] + items[2] + [items[3]] + output.CURRENT_FILE.pop() + global CURRENT_DIR + CURRENT_DIR = os.path.dirname(output.CURRENT_FILE[-1]) + return p0 -def p_exprle(p): - """expr : expr LE expr""" - p[0] = "1" if to_int(p[1]) <= to_int(p[3]) else "0" + def include_file_empty(self, items): + return [items[1]] + def include_once_empty(self, items): + return [items[1]] -def p_exprgt(p): - """expr : expr GT expr""" - p[0] = "1" if to_int(p[1]) > to_int(p[3]) else "0" + def include_once_ok(self, items): + p0 = [items[0] + items[1]] + items[2] + [items[3]] + output.CURRENT_FILE.pop() + global CURRENT_DIR + CURRENT_DIR = os.path.dirname(output.CURRENT_FILE[-1]) + return p0 + + def include_fname(self, items): + modifier = items[1] + if modifier is None: + return [] + filename = items[2] + if ENABLED: + arch = modifier.get("arch", "") + return include_file(filename, items[2].line, local_first=False, arch=arch) + LEXER.next_token = "_ENDFILE_" + return [] + + def include_macro(self, items): + modifier = items[1] + if modifier is None: + return [] + expr = items[2] + global_first = RE_GLOBAL_FIRST_FILENAME.match(expr) + local_first = RE_LOCAL_FIRST_FILENAME.match(expr) + if global_first is None and local_first is None: + error(items[0].line, f"invalid filename {expr}") + return [] + if ENABLED: + arch = modifier.get("arch", "") + return include_file(expr[1:-1], items[0].line, local_first=local_first is not None, arch=arch) + LEXER.next_token = "_ENDFILE_" + return [] + + def include_once_str(self, items): + modifier = items[2] + if modifier is None: + return [] + string = items[3] + p0 = [] + if ENABLED: + arch = modifier.get("arch", "") + p0 = include_once(string[1:-1], items[3].line, local_first=True, arch=arch) + if not p0: + LEXER.next_token = "_ENDFILE_" + return p0 + + def include_once_fname(self, items): + modifier = items[2] + if modifier is None: + return [] + filename = items[3] + p0 = [] + if ENABLED: + arch = modifier.get("arch", "") + p0 = include_once(filename, items[3].line, local_first=False, arch=arch) + if not p0: + LEXER.next_token = "_ENDFILE_" + return p0 + + def include_modifier_empty(self, items): + return {} + + def include_modifier_arch(self, items): + modifier = items[1] + value = items[3] + if modifier == "arch": + return {"arch": value} + error(items[0].line, f"unknown modifier {modifier}") + return None + def line(self, items): + if ENABLED: + return ["#%s %s%s" % (items[0], items[1], items[2])] + return [] + + def line_file(self, items): + if ENABLED: + return ['#%s %s "%s"%s' % (items[0], items[1], items[2], items[3])] + return [] + + def require(self, items): + return ["#%s %s\n" % (items[0], utils.sanitize_filename(items[1]))] + + def init_id(self, items): + return ['#%s "%s"\n' % (items[0], items[1])] + + def init_str(self, items): + return ["#%s %s\n" % (items[0], items[1])] + + def undef(self, items): + if ENABLED: + ID_TABLE.undef(items[1]) + return [] + + def errormsg(self, items): + if ENABLED: + error(items[0].line, items[1]) + return [] + + def warningmsg(self, items): + if ENABLED: + warning(items[0].line, items[1]) + return [] + + def define(self, items): + id_ = items[1] + params = items[2] + defs = items[3] + if ENABLED: + if defs: + if isinstance(defs[0], str) and defs[0] in " \t": + defs[0] = defs[0].lstrip(" \t") + else: + output.warning_missing_whitespace_after_macro(items[0].line, LEXER.current_file) + ID_TABLE.define( + id_, + args=params, + value=defs, + lineno=items[1].line, + fname=items[1].fname, + ) + return [] -def p_exprge(p): - """expr : expr GE expr""" - p[0] = "1" if to_int(p[1]) >= to_int(p[3]) else "0" + def params_epsilon(self, items): + return None + def params_empty(self, items): + return [ID("", value="", args=None, lineno=items[0].line, fname=items[0].fname)] -def p_expr_par(p): - """expr : LLP expr RRP""" - p[0] = p[2] + def params_paramlist(self, items): + params = items[1] + if params is None: + return None + for i in params: + if not isinstance(i, ID): + error(items[2].line, '"%s" might not appear in a macro parameter list' % str(i)) + return None + names = [x.name for x in params] + for i in range(len(names)): + if names[i] in names[i + 1 :]: + error(items[2].line, 'Duplicated name parameter "%s"' % (names[i])) + return None + return params + + def paramlist_single(self, items): + return [ID(items[0], value="", args=None, lineno=items[0].line, fname=items[0].fname)] + + def paramlist_paramlist(self, items): + return items[0] + [ID(items[2], value="", args=None, lineno=items[2].line, fname=items[2].fname)] + + def pragma_id(self, items): + return ["#%s %s" % (items[0], items[1])] + + def pragma_id_expr(self, items): + return ["#%s %s %s %s" % (items[0], items[1], items[2], items[3])] + + def pragma_id_string(self, items): + return ["#%s %s %s %s" % (items[0], items[1], items[2], items[3][1:-1])] + + def pragma_push(self, items): + return ["#%s %s%s%s%s" % (items[0], items[1], items[2], items[3], items[4])] + + def pragma_once(self, items): + abs_filename = utils.get_absolute_filename_path(output.CURRENT_FILE[-1]) + if abs_filename not in INCLUDED: + INCLUDED[abs_filename] = IncludedFileInfo(once=False, parents=[]) + INCLUDED[abs_filename].once = True + return [] + + def ifdef(self, items): + global ENABLED + p0 = [] + if ENABLED: + p0 = [items[1]] + items[2] + p0 += ['#line %i "%s"' % (items[3].line + 1, items[3].fname)] + ENABLED = IFDEFS.pop().enabled + return p0 + + def ifdef_else(self, items): + global ENABLED + ENABLED = IFDEFS.pop().enabled + p0 = [] + if ENABLED: + p0 = items[0] + items[1] + p0 += ['#line %i "%s"' % (items[2].line + 1, items[2].fname)] + return p0 + + def ifdefelsea(self, items): + global ENABLED + p0 = [] + if IFDEFS[-1].enabled: + if items[0]: + p0 = [items[1]] + items[2] + ENABLED = not items[0] + return p0 + + def ifdefelseb(self, items): + if ENABLED: + p0 = ['#line %i "%s"%s' % (items[0].line + 1, items[0].fname, items[1])] + p0 += items[2] + else: + p0 = [] + return p0 + def ifdef_header(self, items): + global ENABLED + IFDEFS.append(IfDef(ENABLED, items[1].line)) + if ENABLED: + ENABLED = ID_TABLE.defined(items[1]) + return ENABLED -def p_defs_list_eps(p): - """defs :""" - p[0] = [] + def ifndef_header(self, items): + global ENABLED + IFDEFS.append(IfDef(ENABLED, items[1].line)) + if ENABLED: + ENABLED = not ID_TABLE.defined(items[1]) + return ENABLED + def if_expr_header(self, items): + global ENABLED + IFDEFS.append(IfDef(ENABLED, items[1].line if hasattr(items[1], "line") else items[0].line)) + if ENABLED: + val = items[1] + ENABLED = bool(int(val)) if (isinstance(val, str) and val.isdigit()) else ID_TABLE.defined(val) + return ENABLED -def p_defs_list(p): - """defs : defs def""" - p[0] = p[1] - p[0].append(p[2]) + def expr_macrocall(self, items): + return str(items[0]()).strip() + def expr_val(self, items): + return items[0] -def p_def(p): - """def : token - | COMMA - | RRP - | LLP - """ - p[0] = p[1] + def expr_str(self, items): + return items[0] + def expr_par(self, items): + return items[1] -def p_def_macrocall(p): - """def : macrocall %prec DUMMY""" - p[0] = p[1] + def expreq(self, items): + return "1" if items[0] == items[2] else "0" + def exprne(self, items): + return "1" if items[0] != items[2] else "0" -def p_macrocall(p): - """macrocall : ID""" - p[0] = MacroCall(p.lexer.current_file, p.lineno(1), ID_TABLE, p[1], None) + def exprlt(self, items): + return "1" if to_int(items[0]) < to_int(items[2]) else "0" + def exprle(self, items): + return "1" if to_int(items[0]) <= to_int(items[2]) else "0" -def p_macrocall_args(p): - """macrocall : macrocall args""" - p[0] = MacroCall(p.lexer.current_file, p[2].end_lineno, ID_TABLE, p[1], p[2]) + def exprgt(self, items): + return "1" if to_int(items[0]) > to_int(items[2]) else "0" + def exprge(self, items): + return "1" if to_int(items[0]) >= to_int(items[2]) else "0" -def p_macrocall_paste(p): - """macrocall : macrocall PASTE macrocall""" - p[0] = Concatenation(p.lexer.current_file, p[1].lineno, ID_TABLE, p[1], p[3]) + def exprand(self, items): + return "1" if to_bool(items[0]) and to_bool(items[2]) else "0" + def expror(self, items): + return "1" if to_bool(items[0]) or to_bool(items[2]) else "0" -def p_macrocall_stringizing(p): - """macrocall : STRINGIZING macrocall""" - p[0] = Stringizing(p.lexer.current_file, p[2].lineno, ID_TABLE, p[2]) + def defs_list_eps(self, items): + return [] + def defs_list(self, items): + return items[0] + [items[1]] -def p_args(p): - """args : LLP arglist RRP""" - p[0] = p[2] - p[0].start_lineno = p.slice[1].lineno - p[0].end_lineno = p.slice[3].lineno + def def_item_val(self, items): + return items[0] + def def_macrocall(self, items): + return items[0] -def p_arglist(p): - """arglist : arglist COMMA arg""" - p[1].addNewArg(p[3]) - p[0] = p[1] + def macrocall_id(self, items): + return MacroCall(items[0].fname, items[0].line, ID_TABLE, items[0], None) + def macrocall_args(self, items): + return MacroCall(items[0].fname, items[1].end_lineno, ID_TABLE, items[0], items[1]) -def p_arglist_arg(p): - """arglist : arg""" - p[0] = ArgList(ID_TABLE) - p[0].addNewArg(p[1]) + def macrocall_paste(self, items): + return Concatenation(items[0].fname, items[0].lineno, ID_TABLE, items[0], items[2]) + def macrocall_stringizing(self, items): + return Stringizing(items[1].fname, items[1].lineno, ID_TABLE, items[1]) -def p_arg_eps(p): - """arg :""" - p[0] = Arg() + def args(self, items): + arglist = items[1] + arglist.start_lineno = items[0].line + arglist.end_lineno = items[2].line + return arglist + def arglist_single(self, items): + al = ArgList(ID_TABLE) + al.addNewArg(items[0]) + return al -def p_arg_argstring(p): - """arg : argstring""" - p[0] = p[1] + def arglist(self, items): + items[0].addNewArg(items[2]) + return items[0] + def arg_eps(self, items): + return Arg() -def p_argstring(p): - """argstring : token - | macrocall %prec DUMMY - """ - p[0] = Arg(p[1]) + def arg_val(self, items): + return items[0] + def argstring_token_single(self, items): + return Arg(items[0]) -def p_argstring_argslist(p): - """argstring : LLP arglist RRP""" - p[0] = Arg(p[2]) + def argstring_macrocall_single(self, items): + return Arg(items[0]) + def argstring_argslist(self, items): + return Arg(items[1]) -def p_argstring_token(p): - """argstring : argstring token - | argstring macrocall %prec DUMMY - """ - p[0] = p[1] - p[0].addToken(p[2]) + def argstring_token(self, items): + items[0].addToken(items[1]) + return items[0] + def argstring_macrocall(self, items): + items[0].addToken(items[1]) + return items[0] -def p_argstring_argstring(p): - """argstring : argstring LLP arglist RRP""" - p[0] = p[1] - p[0].addToken(p[3]) + def argstring_argstring(self, items): + items[0].addToken(items[2]) + return items[0] -# --- YYERROR +lark_parser = Lark_StandAlone(lexer=LarkLexerAdapter, transformer=ZxbppTransformer()) -def p_error(p): - if p is not None: - if p.type == "NEWLINE": - error( - p.lineno, - "Syntax error. Unexpected end of line", - output.CURRENT_FILE[-1], - ) - elif p.type == "_ENDFILE_": - error( - p.lineno, - "Syntax error. Unexpected end of file", - output.CURRENT_FILE[-1], - ) +def parse_with_lark(): + try: + lark_parser.parse(LEXER) + except UnexpectedInput as e: + from .zxbpp_standalone import UnexpectedToken + + if isinstance(e, UnexpectedToken): + tok = e.token + if tok.type == "$END": + if global_.has_errors == 0: + error( + tok.line, + "Syntax error. Unexpected end of file", + output.CURRENT_FILE[-1], + ) + global_.has_errors += 1 + return + + if tok.type == "ENDFILE": + error( + tok.line, + "Syntax error. Unexpected end of file", + output.CURRENT_FILE[-1], + ) + elif tok.type == "NEWLINE": + error( + tok.line, + "Syntax error. Unexpected end of line", + output.CURRENT_FILE[-1], + ) + else: + value = tok.value + value = "".join(["|%s|" % hex(ord(x)) if x < " " else x for x in value]) + error( + tok.line, + "Syntax error. Unexpected token '%s' [%s]" % (value, tok.type), + output.CURRENT_FILE[-1], + ) + + # Skip remaining tokens on the same line if error wasn't on newline/endfile + if tok.type not in ("NEWLINE", "ENDFILE"): + while True: + t = LEXER.token() + if t is None or t.type in ("NEWLINE", "_ENDFILE_"): + break else: - value = p.value - value = "".join(["|%s|" % hex(ord(x)) if x < " " else x for x in value]) error( - p.lineno, - "Syntax error. Unexpected token '%s' [%s]" % (value, p.type), + e.line, + "Syntax error. Unexpected input", output.CURRENT_FILE[-1], ) - else: - config.OPTIONS.stderr.write("General syntax error at preprocessor (unexpected End of File?)") - global_.has_errors += 1 + global_.has_errors += 1 + parse_with_lark() def filter_(input_, filename="", state="INITIAL"): @@ -861,7 +773,7 @@ def filter_(input_, filename="", state="INITIAL"): CURRENT_DIR = os.path.dirname(output.CURRENT_FILE[-1]) LEXER.input(input_, filename) LEXER.lex.begin(state) - parser.parse(lexer=LEXER, debug=config.OPTIONS.debug_zxbpp) + parse_with_lark() output.CURRENT_FILE.pop() CURRENT_DIR = prev_dir @@ -877,6 +789,7 @@ def main(argv): output.CURRENT_FILE.append(argv[0]) else: output.CURRENT_FILE.append(global_.FILENAME) + CURRENT_DIR = os.path.dirname(output.CURRENT_FILE[-1]) if config.OPTIONS.sinclair: @@ -888,7 +801,7 @@ def main(argv): if OUTPUT and OUTPUT[-1] != "\n": OUTPUT += "\n" - parser.parse(lexer=LEXER, debug=config.OPTIONS.debug_zxbpp) + parse_with_lark() output.CURRENT_FILE.pop() CURRENT_DIR = os.path.dirname(output.CURRENT_FILE[-1]) @@ -898,13 +811,13 @@ def main(argv): if OUTPUT and OUTPUT[-1] != "\n": OUTPUT += "\n" - parser.parse(lexer=LEXER, debug=config.OPTIONS.debug_zxbpp) + parse_with_lark() output.CURRENT_FILE.pop() global_.FILENAME = prev_file return global_.has_errors -parser = utils.get_or_create("zxbpp", lambda: yacc.yacc(debug=True)) +parser = lark_parser parser.defaulted_states = {} @@ -954,9 +867,9 @@ def entry_point(args=None): parser.add_argument( "--arch", type=str, - default=arch.AVAILABLE_ARCHITECTURES[0], - help=f"Target architecture (defaults is'{arch.AVAILABLE_ARCHITECTURES[0]}'). " - f"Available architectures: {','.join(arch.AVAILABLE_ARCHITECTURES)}", + default=AVAILABLE_ARCHITECTURES[0], + help=f"Target architecture (defaults is'{AVAILABLE_ARCHITECTURES[0]}'). " + f"Available architectures: {','.join(AVAILABLE_ARCHITECTURES)}", ) parser.add_argument( "--expect-warnings", @@ -970,7 +883,7 @@ def entry_point(args=None): config.OPTIONS.debug_zxbpp = config.OPTIONS.debug_level > 0 config.OPTIONS.expected_warnings = options.expect_warnings - if options.arch not in arch.AVAILABLE_ARCHITECTURES: + if options.arch not in AVAILABLE_ARCHITECTURES: parser.error(f"Invalid architecture '{options.arch}'") # Exits with error config.OPTIONS.architecture = options.arch diff --git a/src/zxbpp/zxbpp_standalone.py b/src/zxbpp/zxbpp_standalone.py new file mode 100644 index 000000000..3ccdb4966 --- /dev/null +++ b/src/zxbpp/zxbpp_standalone.py @@ -0,0 +1,3574 @@ +# The file was automatically generated by Lark v1.3.1 +__version__ = "1.3.1" + +# +# +# Lark Stand-alone Generator Tool +# ---------------------------------- +# Generates a stand-alone LALR(1) parser +# +# Git: https://github.com/erezsh/lark +# Author: Erez Shinan (erezshin@gmail.com) +# +# +# >>> LICENSE +# +# This tool and its generated code use a separate license from Lark, +# and are subject to the terms of the Mozilla Public License, v. 2.0. +# If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +# If you wish to purchase a commercial license for this tool and its +# generated code, you may contact me via email or otherwise. +# +# If MPL2 is incompatible with your free or open-source project, +# contact me and we'll work it out. +# +# + +from copy import deepcopy +from abc import ABC, abstractmethod +from types import ModuleType +from typing import ( + TypeVar, Generic, Type, Tuple, List, Dict, Iterator, Collection, Callable, Optional, FrozenSet, Any, + Union, Iterable, IO, TYPE_CHECKING, overload, Sequence, + Pattern as REPattern, ClassVar, Set, Mapping +) + + +class LarkError(Exception): + pass + + +class ConfigurationError(LarkError, ValueError): + pass + + +def assert_config(value, options: Collection, msg='Got %r, expected one of %s'): + if value not in options: + raise ConfigurationError(msg % (value, options)) + + +class GrammarError(LarkError): + pass + + +class ParseError(LarkError): + pass + + +class LexError(LarkError): + pass + +T = TypeVar('T') + +class UnexpectedInput(LarkError): + #-- + line: int + column: int + pos_in_stream = None + state: Any + _terminals_by_name = None + interactive_parser: 'InteractiveParser' + + def get_context(self, text: str, span: int=40) -> str: + #-- + pos = self.pos_in_stream or 0 + start = max(pos - span, 0) + end = pos + span + if not isinstance(text, bytes): + before = text[start:pos].rsplit('\n', 1)[-1] + after = text[pos:end].split('\n', 1)[0] + return before + after + '\n' + ' ' * len(before.expandtabs()) + '^\n' + else: + before = text[start:pos].rsplit(b'\n', 1)[-1] + after = text[pos:end].split(b'\n', 1)[0] + return (before + after + b'\n' + b' ' * len(before.expandtabs()) + b'^\n').decode("ascii", "backslashreplace") + + def match_examples(self, parse_fn: 'Callable[[str], Tree]', + examples: Union[Mapping[T, Iterable[str]], Iterable[Tuple[T, Iterable[str]]]], + token_type_match_fallback: bool=False, + use_accepts: bool=True + ) -> Optional[T]: + #-- + assert self.state is not None, "Not supported for this exception" + + if isinstance(examples, Mapping): + examples = examples.items() + + candidate = (None, False) + for i, (label, example) in enumerate(examples): + assert not isinstance(example, str), "Expecting a list" + + for j, malformed in enumerate(example): + try: + parse_fn(malformed) + except UnexpectedInput as ut: + if ut.state == self.state: + if ( + use_accepts + and isinstance(self, UnexpectedToken) + and isinstance(ut, UnexpectedToken) + and ut.accepts != self.accepts + ): + logger.debug("Different accepts with same state[%d]: %s != %s at example [%s][%s]" % + (self.state, self.accepts, ut.accepts, i, j)) + continue + if ( + isinstance(self, (UnexpectedToken, UnexpectedEOF)) + and isinstance(ut, (UnexpectedToken, UnexpectedEOF)) + ): + if ut.token == self.token: ## + + logger.debug("Exact Match at example [%s][%s]" % (i, j)) + return label + + if token_type_match_fallback: + ## + + if (ut.token.type == self.token.type) and not candidate[-1]: + logger.debug("Token Type Fallback at example [%s][%s]" % (i, j)) + candidate = label, True + + if candidate[0] is None: + logger.debug("Same State match at example [%s][%s]" % (i, j)) + candidate = label, False + + return candidate[0] + + def _format_expected(self, expected): + if self._terminals_by_name: + d = self._terminals_by_name + expected = [d[t_name].user_repr() if t_name in d else t_name for t_name in expected] + return "Expected one of: \n\t* %s\n" % '\n\t* '.join(expected) + + +class UnexpectedEOF(ParseError, UnexpectedInput): + #-- + expected: 'List[Token]' + + def __init__(self, expected, state=None, terminals_by_name=None): + super(UnexpectedEOF, self).__init__() + + self.expected = expected + self.state = state + from .lexer import Token + self.token = Token("", "") ## + + self.pos_in_stream = -1 + self.line = -1 + self.column = -1 + self._terminals_by_name = terminals_by_name + + + def __str__(self): + message = "Unexpected end-of-input. " + message += self._format_expected(self.expected) + return message + + +class UnexpectedCharacters(LexError, UnexpectedInput): + #-- + + allowed: Set[str] + considered_tokens: Set[Any] + + def __init__(self, seq, lex_pos, line, column, allowed=None, considered_tokens=None, state=None, token_history=None, + terminals_by_name=None, considered_rules=None): + super(UnexpectedCharacters, self).__init__() + + ## + + self.line = line + self.column = column + self.pos_in_stream = lex_pos + self.state = state + self._terminals_by_name = terminals_by_name + + self.allowed = allowed + self.considered_tokens = considered_tokens + self.considered_rules = considered_rules + self.token_history = token_history + + if isinstance(seq, bytes): + self.char = seq[lex_pos:lex_pos + 1].decode("ascii", "backslashreplace") + else: + self.char = seq[lex_pos] + self._context = self.get_context(seq) + + + def __str__(self): + message = "No terminal matches '%s' in the current parser context, at line %d col %d" % (self.char, self.line, self.column) + message += '\n\n' + self._context + if self.allowed: + message += self._format_expected(self.allowed) + if self.token_history: + message += '\nPrevious tokens: %s\n' % ', '.join(repr(t) for t in self.token_history) + return message + + +class UnexpectedToken(ParseError, UnexpectedInput): + #-- + + expected: Set[str] + considered_rules: Set[str] + + def __init__(self, token, expected, considered_rules=None, state=None, interactive_parser=None, terminals_by_name=None, token_history=None): + super(UnexpectedToken, self).__init__() + + ## + + self.line = getattr(token, 'line', '?') + self.column = getattr(token, 'column', '?') + self.pos_in_stream = getattr(token, 'start_pos', None) + self.state = state + + self.token = token + self.expected = expected ## + + self._accepts = NO_VALUE + self.considered_rules = considered_rules + self.interactive_parser = interactive_parser + self._terminals_by_name = terminals_by_name + self.token_history = token_history + + + @property + def accepts(self) -> Set[str]: + if self._accepts is NO_VALUE: + self._accepts = self.interactive_parser and self.interactive_parser.accepts() + return self._accepts + + def __str__(self): + message = ("Unexpected token %r at line %s, column %s.\n%s" + % (self.token, self.line, self.column, self._format_expected(self.accepts or self.expected))) + if self.token_history: + message += "Previous tokens: %r\n" % self.token_history + + return message + + + +class VisitError(LarkError): + #-- + + obj: 'Union[Tree, Token]' + orig_exc: Exception + + def __init__(self, rule, obj, orig_exc): + message = 'Error trying to process rule "%s":\n\n%s' % (rule, orig_exc) + super(VisitError, self).__init__(message) + + self.rule = rule + self.obj = obj + self.orig_exc = orig_exc + + +class MissingVariableError(LarkError): + pass + + +import sys, re +import logging +from dataclasses import dataclass +from typing import Generic, AnyStr + +logger: logging.Logger = logging.getLogger("lark") +logger.addHandler(logging.StreamHandler()) +## + +## + +logger.setLevel(logging.CRITICAL) + + +NO_VALUE = object() + +T = TypeVar("T") + + +def classify(seq: Iterable, key: Optional[Callable] = None, value: Optional[Callable] = None) -> Dict: + d: Dict[Any, Any] = {} + for item in seq: + k = key(item) if (key is not None) else item + v = value(item) if (value is not None) else item + try: + d[k].append(v) + except KeyError: + d[k] = [v] + return d + + +def _deserialize(data: Any, namespace: Dict[str, Any], memo: Dict) -> Any: + if isinstance(data, dict): + if '__type__' in data: ## + + class_ = namespace[data['__type__']] + return class_.deserialize(data, memo) + elif '@' in data: + return memo[data['@']] + return {key:_deserialize(value, namespace, memo) for key, value in data.items()} + elif isinstance(data, list): + return [_deserialize(value, namespace, memo) for value in data] + return data + + +_T = TypeVar("_T", bound="Serialize") + +class Serialize: + #-- + + def memo_serialize(self, types_to_memoize: List) -> Any: + memo = SerializeMemoizer(types_to_memoize) + return self.serialize(memo), memo.serialize() + + def serialize(self, memo = None) -> Dict[str, Any]: + if memo and memo.in_types(self): + return {'@': memo.memoized.get(self)} + + fields = getattr(self, '__serialize_fields__') + res = {f: _serialize(getattr(self, f), memo) for f in fields} + res['__type__'] = type(self).__name__ + if hasattr(self, '_serialize'): + self._serialize(res, memo) + return res + + @classmethod + def deserialize(cls: Type[_T], data: Dict[str, Any], memo: Dict[int, Any]) -> _T: + namespace = getattr(cls, '__serialize_namespace__', []) + namespace = {c.__name__:c for c in namespace} + + fields = getattr(cls, '__serialize_fields__') + + if '@' in data: + return memo[data['@']] + + inst = cls.__new__(cls) + for f in fields: + try: + setattr(inst, f, _deserialize(data[f], namespace, memo)) + except KeyError as e: + raise KeyError("Cannot find key for class", cls, e) + + if hasattr(inst, '_deserialize'): + inst._deserialize() + + return inst + + +class SerializeMemoizer(Serialize): + #-- + + __serialize_fields__ = 'memoized', + + def __init__(self, types_to_memoize: List) -> None: + self.types_to_memoize = tuple(types_to_memoize) + self.memoized = Enumerator() + + def in_types(self, value: Serialize) -> bool: + return isinstance(value, self.types_to_memoize) + + def serialize(self) -> Dict[int, Any]: ## + + return _serialize(self.memoized.reversed(), None) + + @classmethod + def deserialize(cls, data: Dict[int, Any], namespace: Dict[str, Any], memo: Dict[Any, Any]) -> Dict[int, Any]: ## + + return _deserialize(data, namespace, memo) + + +try: + import regex + _has_regex = True +except ImportError: + _has_regex = False + +if sys.version_info >= (3, 11): + import re._parser as sre_parse + import re._constants as sre_constants +else: + import sre_parse + import sre_constants + +categ_pattern = re.compile(r'\\p{[A-Za-z_]+}') + +def get_regexp_width(expr: str) -> Union[Tuple[int, int], List[int]]: + if _has_regex: + ## + + ## + + ## + + regexp_final = re.sub(categ_pattern, 'A', expr) + else: + if re.search(categ_pattern, expr): + raise ImportError('`regex` module must be installed in order to use Unicode categories.', expr) + regexp_final = expr + try: + ## + + return [int(x) for x in sre_parse.parse(regexp_final).getwidth()] + except sre_constants.error: + if not _has_regex: + raise ValueError(expr) + else: + ## + + ## + + c = regex.compile(regexp_final) + ## + + ## + + MAXWIDTH = getattr(sre_parse, "MAXWIDTH", sre_constants.MAXREPEAT) + if c.match('') is None: + ## + + return 1, int(MAXWIDTH) + else: + return 0, int(MAXWIDTH) + + +@dataclass(frozen=True) +class TextSlice(Generic[AnyStr]): + #-- + text: AnyStr + start: int + end: int + + def __post_init__(self): + if not isinstance(self.text, (str, bytes)): + raise TypeError("text must be str or bytes") + + if self.start < 0: + object.__setattr__(self, 'start', self.start + len(self.text)) + assert self.start >=0 + + if self.end is None: + object.__setattr__(self, 'end', len(self.text)) + elif self.end < 0: + object.__setattr__(self, 'end', self.end + len(self.text)) + assert self.end <= len(self.text) + + @classmethod + def cast_from(cls, text: 'TextOrSlice') -> 'TextSlice[AnyStr]': + if isinstance(text, TextSlice): + return text + + return cls(text, 0, len(text)) + + def is_complete_text(self): + return self.start == 0 and self.end == len(self.text) + + def __len__(self): + return self.end - self.start + + def count(self, substr: AnyStr): + return self.text.count(substr, self.start, self.end) + + def rindex(self, substr: AnyStr): + return self.text.rindex(substr, self.start, self.end) + + +TextOrSlice = Union[AnyStr, 'TextSlice[AnyStr]'] +LarkInput = Union[AnyStr, TextSlice[AnyStr], Any] + + + +class Meta: + + empty: bool + line: int + column: int + start_pos: int + end_line: int + end_column: int + end_pos: int + orig_expansion: 'List[TerminalDef]' + match_tree: bool + + def __init__(self): + self.empty = True + + +_Leaf_T = TypeVar("_Leaf_T") +Branch = Union[_Leaf_T, 'Tree[_Leaf_T]'] + + +class Tree(Generic[_Leaf_T]): + #-- + + data: str + children: 'List[Branch[_Leaf_T]]' + + def __init__(self, data: str, children: 'List[Branch[_Leaf_T]]', meta: Optional[Meta]=None) -> None: + self.data = data + self.children = children + self._meta = meta + + @property + def meta(self) -> Meta: + if self._meta is None: + self._meta = Meta() + return self._meta + + def __repr__(self): + return 'Tree(%r, %r)' % (self.data, self.children) + + __match_args__ = ("data", "children") + + def _pretty_label(self): + return self.data + + def _pretty(self, level, indent_str): + yield f'{indent_str*level}{self._pretty_label()}' + if len(self.children) == 1 and not isinstance(self.children[0], Tree): + yield f'\t{self.children[0]}\n' + else: + yield '\n' + for n in self.children: + if isinstance(n, Tree): + yield from n._pretty(level+1, indent_str) + else: + yield f'{indent_str*(level+1)}{n}\n' + + def pretty(self, indent_str: str=' ') -> str: + #-- + return ''.join(self._pretty(0, indent_str)) + + def __rich__(self, parent:Optional['rich.tree.Tree']=None) -> 'rich.tree.Tree': + #-- + return self._rich(parent) + + def _rich(self, parent): + if parent: + tree = parent.add(f'[bold]{self.data}[/bold]') + else: + import rich.tree + tree = rich.tree.Tree(self.data) + + for c in self.children: + if isinstance(c, Tree): + c._rich(tree) + else: + tree.add(f'[green]{c}[/green]') + + return tree + + def __eq__(self, other): + try: + return self.data == other.data and self.children == other.children + except AttributeError: + return False + + def __ne__(self, other): + return not (self == other) + + def __hash__(self) -> int: + return hash((self.data, tuple(self.children))) + + def iter_subtrees(self) -> 'Iterator[Tree[_Leaf_T]]': + #-- + queue = [self] + subtrees = dict() + for subtree in queue: + subtrees[id(subtree)] = subtree + queue += [c for c in reversed(subtree.children) + if isinstance(c, Tree) and id(c) not in subtrees] + + del queue + return reversed(list(subtrees.values())) + + def iter_subtrees_topdown(self): + #-- + stack = [self] + stack_append = stack.append + stack_pop = stack.pop + while stack: + node = stack_pop() + if not isinstance(node, Tree): + continue + yield node + for child in reversed(node.children): + stack_append(child) + + def find_pred(self, pred: 'Callable[[Tree[_Leaf_T]], bool]') -> 'Iterator[Tree[_Leaf_T]]': + #-- + return filter(pred, self.iter_subtrees()) + + def find_data(self, data: str) -> 'Iterator[Tree[_Leaf_T]]': + #-- + return self.find_pred(lambda t: t.data == data) + + +from functools import wraps, update_wrapper +from inspect import getmembers, getmro + +_Return_T = TypeVar('_Return_T') +_Return_V = TypeVar('_Return_V') +_Leaf_T = TypeVar('_Leaf_T') +_Leaf_U = TypeVar('_Leaf_U') +_R = TypeVar('_R') +_FUNC = Callable[..., _Return_T] +_DECORATED = Union[_FUNC, type] + +class _DiscardType: + #-- + + def __repr__(self): + return "lark.visitors.Discard" + +Discard = _DiscardType() + +## + + +class _Decoratable: + #-- + + @classmethod + def _apply_v_args(cls, visit_wrapper): + mro = getmro(cls) + assert mro[0] is cls + libmembers = {name for _cls in mro[1:] for name, _ in getmembers(_cls)} + for name, value in getmembers(cls): + + ## + + if name.startswith('_') or (name in libmembers and name not in cls.__dict__): + continue + if not callable(value): + continue + + ## + + if isinstance(cls.__dict__[name], _VArgsWrapper): + continue + + setattr(cls, name, _VArgsWrapper(cls.__dict__[name], visit_wrapper)) + return cls + + def __class_getitem__(cls, _): + return cls + + +class Transformer(_Decoratable, ABC, Generic[_Leaf_T, _Return_T]): + #-- + __visit_tokens__ = True ## + + + def __init__(self, visit_tokens: bool=True) -> None: + self.__visit_tokens__ = visit_tokens + + def _call_userfunc(self, tree, new_children=None): + ## + + children = new_children if new_children is not None else tree.children + try: + f = getattr(self, tree.data) + except AttributeError: + return self.__default__(tree.data, children, tree.meta) + else: + try: + wrapper = getattr(f, 'visit_wrapper', None) + if wrapper is not None: + return f.visit_wrapper(f, tree.data, children, tree.meta) + else: + return f(children) + except GrammarError: + raise + except Exception as e: + raise VisitError(tree.data, tree, e) + + def _call_userfunc_token(self, token): + try: + f = getattr(self, token.type) + except AttributeError: + return self.__default_token__(token) + else: + try: + return f(token) + except GrammarError: + raise + except Exception as e: + raise VisitError(token.type, token, e) + + def _transform_children(self, children): + for c in children: + if isinstance(c, Tree): + res = self._transform_tree(c) + elif self.__visit_tokens__ and isinstance(c, Token): + res = self._call_userfunc_token(c) + else: + res = c + + if res is not Discard: + yield res + + def _transform_tree(self, tree): + children = list(self._transform_children(tree.children)) + return self._call_userfunc(tree, children) + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + #-- + res = list(self._transform_children([tree])) + if not res: + return None ## + + assert len(res) == 1 + return res[0] + + def __mul__( + self: 'Transformer[_Leaf_T, Tree[_Leaf_U]]', + other: 'Union[Transformer[_Leaf_U, _Return_V], TransformerChain[_Leaf_U, _Return_V,]]' + ) -> 'TransformerChain[_Leaf_T, _Return_V]': + #-- + return TransformerChain(self, other) + + def __default__(self, data, children, meta): + #-- + return Tree(data, children, meta) + + def __default_token__(self, token): + #-- + return token + + +def merge_transformers(base_transformer=None, **transformers_to_merge): + #-- + if base_transformer is None: + base_transformer = Transformer() + for prefix, transformer in transformers_to_merge.items(): + for method_name in dir(transformer): + method = getattr(transformer, method_name) + if not callable(method): + continue + if method_name.startswith("_") or method_name == "transform": + continue + prefixed_method = prefix + "__" + method_name + if hasattr(base_transformer, prefixed_method): + raise AttributeError("Cannot merge: method '%s' appears more than once" % prefixed_method) + + setattr(base_transformer, prefixed_method, method) + + return base_transformer + + +class InlineTransformer(Transformer): ## + + def _call_userfunc(self, tree, new_children=None): + ## + + children = new_children if new_children is not None else tree.children + try: + f = getattr(self, tree.data) + except AttributeError: + return self.__default__(tree.data, children, tree.meta) + else: + return f(*children) + + +class TransformerChain(Generic[_Leaf_T, _Return_T]): + + transformers: 'Tuple[Union[Transformer, TransformerChain], ...]' + + def __init__(self, *transformers: 'Union[Transformer, TransformerChain]') -> None: + self.transformers = transformers + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + for t in self.transformers: + tree = t.transform(tree) + return cast(_Return_T, tree) + + def __mul__( + self: 'TransformerChain[_Leaf_T, Tree[_Leaf_U]]', + other: 'Union[Transformer[_Leaf_U, _Return_V], TransformerChain[_Leaf_U, _Return_V]]' + ) -> 'TransformerChain[_Leaf_T, _Return_V]': + return TransformerChain(*self.transformers + (other,)) + + +class Transformer_InPlace(Transformer[_Leaf_T, _Return_T]): + #-- + def _transform_tree(self, tree): ## + + return self._call_userfunc(tree) + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + for subtree in tree.iter_subtrees(): + subtree.children = list(self._transform_children(subtree.children)) + + return self._transform_tree(tree) + + +class Transformer_NonRecursive(Transformer[_Leaf_T, _Return_T]): + #-- + + def transform(self, tree: Tree[_Leaf_T]) -> _Return_T: + ## + + rev_postfix = [] + q: List[Branch[_Leaf_T]] = [tree] + while q: + t = q.pop() + rev_postfix.append(t) + if isinstance(t, Tree): + q += t.children + + ## + + stack: List = [] + for x in reversed(rev_postfix): + if isinstance(x, Tree): + size = len(x.children) + if size: + args = stack[-size:] + del stack[-size:] + else: + args = [] + + res = self._call_userfunc(x, args) + if res is not Discard: + stack.append(res) + + elif self.__visit_tokens__ and isinstance(x, Token): + res = self._call_userfunc_token(x) + if res is not Discard: + stack.append(res) + else: + stack.append(x) + + result, = stack ## + + ## + + ## + + ## + + return cast(_Return_T, result) + + +class Transformer_InPlaceRecursive(Transformer[_Leaf_T, _Return_T]): + #-- + def _transform_tree(self, tree): + tree.children = list(self._transform_children(tree.children)) + return self._call_userfunc(tree) + + +## + + +class VisitorBase: + def _call_userfunc(self, tree): + return getattr(self, tree.data, self.__default__)(tree) + + def __default__(self, tree): + #-- + return tree + + def __class_getitem__(cls, _): + return cls + + +class Visitor(VisitorBase, ABC, Generic[_Leaf_T]): + #-- + + def visit(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + for subtree in tree.iter_subtrees(): + self._call_userfunc(subtree) + return tree + + def visit_topdown(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + for subtree in tree.iter_subtrees_topdown(): + self._call_userfunc(subtree) + return tree + + +class Visitor_Recursive(VisitorBase, Generic[_Leaf_T]): + #-- + + def visit(self, tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + for child in tree.children: + if isinstance(child, Tree): + self.visit(child) + + self._call_userfunc(tree) + return tree + + def visit_topdown(self,tree: Tree[_Leaf_T]) -> Tree[_Leaf_T]: + #-- + self._call_userfunc(tree) + + for child in tree.children: + if isinstance(child, Tree): + self.visit_topdown(child) + + return tree + + +class Interpreter(_Decoratable, ABC, Generic[_Leaf_T, _Return_T]): + #-- + + def visit(self, tree: Tree[_Leaf_T]) -> _Return_T: + ## + + ## + + ## + + return self._visit_tree(tree) + + def _visit_tree(self, tree: Tree[_Leaf_T]): + f = getattr(self, tree.data) + wrapper = getattr(f, 'visit_wrapper', None) + if wrapper is not None: + return f.visit_wrapper(f, tree.data, tree.children, tree.meta) + else: + return f(tree) + + def visit_children(self, tree: Tree[_Leaf_T]) -> List: + return [self._visit_tree(child) if isinstance(child, Tree) else child + for child in tree.children] + + def __getattr__(self, name): + return self.__default__ + + def __default__(self, tree): + return self.visit_children(tree) + + +_InterMethod = Callable[[Type[Interpreter], _Return_T], _R] + +def visit_children_decor(func: _InterMethod) -> _InterMethod: + #-- + @wraps(func) + def inner(cls, tree): + values = cls.visit_children(tree) + return func(cls, values) + return inner + +## + + +def _apply_v_args(obj, visit_wrapper): + try: + _apply = obj._apply_v_args + except AttributeError: + return _VArgsWrapper(obj, visit_wrapper) + else: + return _apply(visit_wrapper) + + +class _VArgsWrapper: + #-- + base_func: Callable + + def __init__(self, func: Callable, visit_wrapper: Callable[[Callable, str, list, Any], Any]): + if isinstance(func, _VArgsWrapper): + func = func.base_func + self.base_func = func + self.visit_wrapper = visit_wrapper + update_wrapper(self, func) + + def __call__(self, *args, **kwargs): + return self.base_func(*args, **kwargs) + + def __get__(self, instance, owner=None): + try: + ## + + ## + + g = type(self.base_func).__get__ + except AttributeError: + return self + else: + return _VArgsWrapper(g(self.base_func, instance, owner), self.visit_wrapper) + + def __set_name__(self, owner, name): + try: + f = type(self.base_func).__set_name__ + except AttributeError: + return + else: + f(self.base_func, owner, name) + + +def _vargs_inline(f, _data, children, _meta): + return f(*children) +def _vargs_meta_inline(f, _data, children, meta): + return f(meta, *children) +def _vargs_meta(f, _data, children, meta): + return f(meta, children) +def _vargs_tree(f, data, children, meta): + return f(Tree(data, children, meta)) + + +def v_args(inline: bool = False, meta: bool = False, tree: bool = False, wrapper: Optional[Callable] = None) -> Callable[[_DECORATED], _DECORATED]: + #-- + if tree and (meta or inline): + raise ValueError("Visitor functions cannot combine 'tree' with 'meta' or 'inline'.") + + func = None + if meta: + if inline: + func = _vargs_meta_inline + else: + func = _vargs_meta + elif inline: + func = _vargs_inline + elif tree: + func = _vargs_tree + + if wrapper is not None: + if func is not None: + raise ValueError("Cannot use 'wrapper' along with 'tree', 'meta' or 'inline'.") + func = wrapper + + def _visitor_args_dec(obj): + return _apply_v_args(obj, func) + return _visitor_args_dec + + + +TOKEN_DEFAULT_PRIORITY = 0 + + +class Symbol(Serialize): + __slots__ = ('name',) + + name: str + is_term: ClassVar[bool] = NotImplemented + + def __init__(self, name: str) -> None: + self.name = name + + def __eq__(self, other): + if not isinstance(other, Symbol): + return NotImplemented + return self.is_term == other.is_term and self.name == other.name + + def __ne__(self, other): + return not (self == other) + + def __hash__(self): + return hash(self.name) + + def __repr__(self): + return '%s(%r)' % (type(self).__name__, self.name) + + fullrepr = property(__repr__) + + def renamed(self, f): + return type(self)(f(self.name)) + + +class Terminal(Symbol): + __serialize_fields__ = 'name', 'filter_out' + + is_term: ClassVar[bool] = True + + def __init__(self, name: str, filter_out: bool = False) -> None: + self.name = name + self.filter_out = filter_out + + @property + def fullrepr(self): + return '%s(%r, %r)' % (type(self).__name__, self.name, self.filter_out) + + def renamed(self, f): + return type(self)(f(self.name), self.filter_out) + + +class NonTerminal(Symbol): + __serialize_fields__ = 'name', + + is_term: ClassVar[bool] = False + + def serialize(self, memo=None) -> Dict[str, Any]: + ## + + ## + + return {'name': str(self.name), '__type__': 'NonTerminal'} + + +class RuleOptions(Serialize): + __serialize_fields__ = 'keep_all_tokens', 'expand1', 'priority', 'template_source', 'empty_indices' + + keep_all_tokens: bool + expand1: bool + priority: Optional[int] + template_source: Optional[str] + empty_indices: Tuple[bool, ...] + + def __init__(self, keep_all_tokens: bool=False, expand1: bool=False, priority: Optional[int]=None, template_source: Optional[str]=None, empty_indices: Tuple[bool, ...]=()) -> None: + self.keep_all_tokens = keep_all_tokens + self.expand1 = expand1 + self.priority = priority + self.template_source = template_source + self.empty_indices = empty_indices + + def __repr__(self): + return 'RuleOptions(%r, %r, %r, %r)' % ( + self.keep_all_tokens, + self.expand1, + self.priority, + self.template_source + ) + + +class Rule(Serialize): + #-- + __slots__ = ('origin', 'expansion', 'alias', 'options', 'order', '_hash') + + __serialize_fields__ = 'origin', 'expansion', 'order', 'alias', 'options' + __serialize_namespace__ = Terminal, NonTerminal, RuleOptions + + origin: NonTerminal + expansion: Sequence[Symbol] + order: int + alias: Optional[str] + options: RuleOptions + _hash: int + + def __init__(self, origin: NonTerminal, expansion: Sequence[Symbol], + order: int=0, alias: Optional[str]=None, options: Optional[RuleOptions]=None): + self.origin = origin + self.expansion = expansion + self.alias = alias + self.order = order + self.options = options or RuleOptions() + self._hash = hash((self.origin, tuple(self.expansion))) + + def _deserialize(self): + self._hash = hash((self.origin, tuple(self.expansion))) + + def __str__(self): + return '<%s : %s>' % (self.origin.name, ' '.join(x.name for x in self.expansion)) + + def __repr__(self): + return 'Rule(%r, %r, %r, %r)' % (self.origin, self.expansion, self.alias, self.options) + + def __hash__(self): + return self._hash + + def __eq__(self, other): + if not isinstance(other, Rule): + return False + return self.origin == other.origin and self.expansion == other.expansion + + + +from contextlib import suppress +from copy import copy + +try: ## + + has_interegular = bool(interegular) +except NameError: + has_interegular = False + +class Pattern(Serialize, ABC): + #-- + + value: str + flags: Collection[str] + raw: Optional[str] + type: ClassVar[str] + + def __init__(self, value: str, flags: Collection[str] = (), raw: Optional[str] = None) -> None: + self.value = value + self.flags = frozenset(flags) + self.raw = raw + + def __repr__(self): + return repr(self.to_regexp()) + + ## + + def __hash__(self): + return hash((type(self), self.value, self.flags)) + + def __eq__(self, other): + return type(self) == type(other) and self.value == other.value and self.flags == other.flags + + @abstractmethod + def to_regexp(self) -> str: + raise NotImplementedError() + + @property + @abstractmethod + def min_width(self) -> int: + raise NotImplementedError() + + @property + @abstractmethod + def max_width(self) -> int: + raise NotImplementedError() + + def _get_flags(self, value): + for f in self.flags: + value = ('(?%s:%s)' % (f, value)) + return value + + +class PatternStr(Pattern): + __serialize_fields__ = 'value', 'flags', 'raw' + + type: ClassVar[str] = "str" + + def to_regexp(self) -> str: + return self._get_flags(re.escape(self.value)) + + @property + def min_width(self) -> int: + return len(self.value) + + @property + def max_width(self) -> int: + return len(self.value) + + +class PatternRE(Pattern): + __serialize_fields__ = 'value', 'flags', 'raw', '_width' + + type: ClassVar[str] = "re" + + def to_regexp(self) -> str: + return self._get_flags(self.value) + + _width = None + def _get_width(self): + if self._width is None: + self._width = get_regexp_width(self.to_regexp()) + return self._width + + @property + def min_width(self) -> int: + return self._get_width()[0] + + @property + def max_width(self) -> int: + return self._get_width()[1] + + +class TerminalDef(Serialize): + #-- + __serialize_fields__ = 'name', 'pattern', 'priority' + __serialize_namespace__ = PatternStr, PatternRE + + name: str + pattern: Pattern + priority: int + + def __init__(self, name: str, pattern: Pattern, priority: int = TOKEN_DEFAULT_PRIORITY) -> None: + assert isinstance(pattern, Pattern), pattern + self.name = name + self.pattern = pattern + self.priority = priority + + def __repr__(self): + return '%s(%r, %r)' % (type(self).__name__, self.name, self.pattern) + + def user_repr(self) -> str: + if self.name.startswith('__'): ## + + return self.pattern.raw or self.name + else: + return self.name + +_T = TypeVar('_T', bound="Token") + +class Token(str): + #-- + __slots__ = ('type', 'start_pos', 'value', 'line', 'column', 'end_line', 'end_column', 'end_pos') + + __match_args__ = ('type', 'value') + + type: str + start_pos: Optional[int] + value: Any + line: Optional[int] + column: Optional[int] + end_line: Optional[int] + end_column: Optional[int] + end_pos: Optional[int] + + + @overload + def __new__( + cls, + type: str, + value: Any, + start_pos: Optional[int] = None, + line: Optional[int] = None, + column: Optional[int] = None, + end_line: Optional[int] = None, + end_column: Optional[int] = None, + end_pos: Optional[int] = None + ) -> 'Token': + ... + + @overload + def __new__( + cls, + type_: str, + value: Any, + start_pos: Optional[int] = None, + line: Optional[int] = None, + column: Optional[int] = None, + end_line: Optional[int] = None, + end_column: Optional[int] = None, + end_pos: Optional[int] = None + ) -> 'Token': ... + + def __new__(cls, *args, **kwargs): + if "type_" in kwargs: + warnings.warn("`type_` is deprecated use `type` instead", DeprecationWarning) + + if "type" in kwargs: + raise TypeError("Error: using both 'type' and the deprecated 'type_' as arguments.") + kwargs["type"] = kwargs.pop("type_") + + return cls._future_new(*args, **kwargs) + + + @classmethod + def _future_new(cls, type, value, start_pos=None, line=None, column=None, end_line=None, end_column=None, end_pos=None): + inst = super(Token, cls).__new__(cls, value) + + inst.type = type + inst.start_pos = start_pos + inst.value = value + inst.line = line + inst.column = column + inst.end_line = end_line + inst.end_column = end_column + inst.end_pos = end_pos + return inst + + @overload + def update(self, type: Optional[str] = None, value: Optional[Any] = None) -> 'Token': + ... + + @overload + def update(self, type_: Optional[str] = None, value: Optional[Any] = None) -> 'Token': + ... + + def update(self, *args, **kwargs): + if "type_" in kwargs: + warnings.warn("`type_` is deprecated use `type` instead", DeprecationWarning) + + if "type" in kwargs: + raise TypeError("Error: using both 'type' and the deprecated 'type_' as arguments.") + kwargs["type"] = kwargs.pop("type_") + + return self._future_update(*args, **kwargs) + + def _future_update(self, type: Optional[str] = None, value: Optional[Any] = None) -> 'Token': + return Token.new_borrow_pos( + type if type is not None else self.type, + value if value is not None else self.value, + self + ) + + @classmethod + def new_borrow_pos(cls: Type[_T], type_: str, value: Any, borrow_t: 'Token') -> _T: + return cls(type_, value, borrow_t.start_pos, borrow_t.line, borrow_t.column, borrow_t.end_line, borrow_t.end_column, borrow_t.end_pos) + + def __reduce__(self): + return (self.__class__, (self.type, self.value, self.start_pos, self.line, self.column)) + + def __repr__(self): + return 'Token(%r, %r)' % (self.type, self.value) + + def __deepcopy__(self, memo): + return Token(self.type, self.value, self.start_pos, self.line, self.column) + + def __eq__(self, other): + if isinstance(other, Token) and self.type != other.type: + return False + + return str.__eq__(self, other) + + __hash__ = str.__hash__ + + +class LineCounter: + #-- + + __slots__ = 'char_pos', 'line', 'column', 'line_start_pos', 'newline_char' + + def __init__(self, newline_char): + self.newline_char = newline_char + self.char_pos = 0 + self.line = 1 + self.column = 1 + self.line_start_pos = 0 + + def __eq__(self, other): + if not isinstance(other, LineCounter): + return NotImplemented + + return self.char_pos == other.char_pos and self.newline_char == other.newline_char + + def feed(self, token: TextOrSlice, test_newline=True): + #-- + if test_newline: + newlines = token.count(self.newline_char) + if newlines: + self.line += newlines + self.line_start_pos = self.char_pos + token.rindex(self.newline_char) + 1 + + self.char_pos += len(token) + self.column = self.char_pos - self.line_start_pos + 1 + + +class UnlessCallback: + def __init__(self, scanner: 'Scanner'): + self.scanner = scanner + + def __call__(self, t: Token): + res = self.scanner.fullmatch(t.value) + if res is not None: + t.type = res + return t + + +class CallChain: + def __init__(self, callback1, callback2, cond): + self.callback1 = callback1 + self.callback2 = callback2 + self.cond = cond + + def __call__(self, t): + t2 = self.callback1(t) + return self.callback2(t) if self.cond(t2) else t2 + + +def _get_match(re_, regexp, s, flags): + m = re_.match(regexp, s, flags) + if m: + return m.group(0) + +def _create_unless(terminals, g_regex_flags, re_, use_bytes): + tokens_by_type = classify(terminals, lambda t: type(t.pattern)) + assert len(tokens_by_type) <= 2, tokens_by_type.keys() + embedded_strs = set() + callback = {} + for retok in tokens_by_type.get(PatternRE, []): + unless = [] + for strtok in tokens_by_type.get(PatternStr, []): + if strtok.priority != retok.priority: + continue + s = strtok.pattern.value + if s == _get_match(re_, retok.pattern.to_regexp(), s, g_regex_flags): + unless.append(strtok) + if strtok.pattern.flags <= retok.pattern.flags: + embedded_strs.add(strtok) + if unless: + callback[retok.name] = UnlessCallback(Scanner(unless, g_regex_flags, re_, use_bytes=use_bytes)) + + new_terminals = [t for t in terminals if t not in embedded_strs] + return new_terminals, callback + + +class Scanner: + def __init__(self, terminals, g_regex_flags, re_, use_bytes): + self.terminals = terminals + self.g_regex_flags = g_regex_flags + self.re_ = re_ + self.use_bytes = use_bytes + + self.allowed_types = {t.name for t in self.terminals} + + self._mres = self._build_mres(terminals, len(terminals)) + + def _build_mres(self, terminals, max_size): + ## + + ## + + ## + + mres = [] + while terminals: + pattern = u'|'.join(u'(?P<%s>%s)' % (t.name, t.pattern.to_regexp()) for t in terminals[:max_size]) + if self.use_bytes: + pattern = pattern.encode('latin-1') + try: + mre = self.re_.compile(pattern, self.g_regex_flags) + except AssertionError: ## + + return self._build_mres(terminals, max_size // 2) + + mres.append(mre) + terminals = terminals[max_size:] + return mres + + def match(self, text: TextSlice, pos): + for mre in self._mres: + m = mre.match(text.text, pos, text.end) + if m: + return m.group(0), m.lastgroup + + + def fullmatch(self, text: str) -> Optional[str]: + for mre in self._mres: + m = mre.fullmatch(text) + if m: + return m.lastgroup + return None + +def _regexp_has_newline(r: str): + #-- + return '\n' in r or '\\n' in r or '\\s' in r or '[^' in r or ('(?s' in r and '.' in r) + + +class LexerState: + #-- + + __slots__ = 'text', 'line_ctr', 'last_token' + + text: TextSlice + line_ctr: LineCounter + last_token: Optional[Token] + + def __init__(self, text: TextSlice, line_ctr: Optional[LineCounter] = None, last_token: Optional[Token]=None): + if isinstance(text, TextSlice): + if line_ctr is None: + line_ctr = LineCounter(b'\n' if isinstance(text.text, bytes) else '\n') + + if text.start > 0: + ## + + line_ctr.feed(TextSlice(text.text, 0, text.start)) + + if not (text.start <= line_ctr.char_pos <= text.end): + raise ValueError("LineCounter.char_pos is out of bounds") + + self.text = text + if line_ctr is None: + line_ctr = LineCounter(b'\n' if isinstance(text, bytes) or (hasattr(text, 'text') and isinstance(text.text, bytes)) else '\n') + self.line_ctr = line_ctr + self.last_token = last_token + + + def __eq__(self, other): + if not isinstance(other, LexerState): + return NotImplemented + + return self.text == other.text and self.line_ctr == other.line_ctr and self.last_token == other.last_token + + def __copy__(self): + return type(self)(self.text, copy(self.line_ctr), self.last_token) + + +class LexerThread: + #-- + + def __init__(self, lexer: 'Lexer', lexer_state: Optional[LexerState]): + self.lexer = lexer + self.state = lexer_state + + @classmethod + def from_text(cls, lexer: 'Lexer', text_or_slice: TextOrSlice) -> 'LexerThread': + text = TextSlice.cast_from(text_or_slice) + return cls(lexer, LexerState(text)) + + @classmethod + def from_custom_input(cls, lexer: 'Lexer', text: Any) -> 'LexerThread': + return cls(lexer, LexerState(text)) + + def lex(self, parser_state): + if self.state is None: + raise TypeError("Cannot lex: No text assigned to lexer state") + return self.lexer.lex(self.state, parser_state) + + def __copy__(self): + return type(self)(self.lexer, copy(self.state)) + + _Token = Token + + +_Callback = Callable[[Token], Token] + +class Lexer(ABC): + #-- + @abstractmethod + def lex(self, lexer_state: LexerState, parser_state: Any) -> Iterator[Token]: + return NotImplemented + + def make_lexer_state(self, text: str): + #-- + return LexerState(TextSlice.cast_from(text)) + + +def _check_regex_collisions(terminal_to_regexp: Dict[TerminalDef, str], comparator, strict_mode, max_collisions_to_show=8): + if not comparator: + comparator = interegular.Comparator.from_regexes(terminal_to_regexp) + + ## + + ## + + max_time = 2 if strict_mode else 0.2 + + ## + + if comparator.count_marked_pairs() >= max_collisions_to_show: + return + for group in classify(terminal_to_regexp, lambda t: t.priority).values(): + for a, b in comparator.check(group, skip_marked=True): + assert a.priority == b.priority + ## + + comparator.mark(a, b) + + ## + + message = f"Collision between Terminals {a.name} and {b.name}. " + try: + example = comparator.get_example_overlap(a, b, max_time).format_multiline() + except ValueError: + ## + + example = "No example could be found fast enough. However, the collision does still exists" + if strict_mode: + raise LexError(f"{message}\n{example}") + logger.warning("%s The lexer will choose between them arbitrarily.\n%s", message, example) + if comparator.count_marked_pairs() >= max_collisions_to_show: + logger.warning("Found 8 regex collisions, will not check for more.") + return + + +class AbstractBasicLexer(Lexer): + terminals_by_name: Dict[str, TerminalDef] + + @abstractmethod + def __init__(self, conf: 'LexerConf', comparator=None) -> None: + ... + + @abstractmethod + def next_token(self, lex_state: LexerState, parser_state: Any = None) -> Token: + ... + + def lex(self, state: LexerState, parser_state: Any) -> Iterator[Token]: + with suppress(EOFError): + while True: + yield self.next_token(state, parser_state) + + +class BasicLexer(AbstractBasicLexer): + terminals: Collection[TerminalDef] + ignore_types: FrozenSet[str] + newline_types: FrozenSet[str] + user_callbacks: Dict[str, _Callback] + callback: Dict[str, _Callback] + re: ModuleType + + def __init__(self, conf: 'LexerConf', comparator=None) -> None: + terminals = list(conf.terminals) + assert all(isinstance(t, TerminalDef) for t in terminals), terminals + + self.re = conf.re_module + + if not conf.skip_validation: + ## + + terminal_to_regexp = {} + for t in terminals: + regexp = t.pattern.to_regexp() + try: + self.re.compile(regexp, conf.g_regex_flags) + except self.re.error: + raise LexError("Cannot compile token %s: %s" % (t.name, t.pattern)) + + if t.pattern.min_width == 0: + raise LexError("Lexer does not allow zero-width terminals. (%s: %s)" % (t.name, t.pattern)) + if t.pattern.type == "re": + terminal_to_regexp[t] = regexp + + if not (set(conf.ignore) <= {t.name for t in terminals}): + raise LexError("Ignore terminals are not defined: %s" % (set(conf.ignore) - {t.name for t in terminals})) + + if has_interegular: + _check_regex_collisions(terminal_to_regexp, comparator, conf.strict) + elif conf.strict: + raise LexError("interegular must be installed for strict mode. Use `pip install 'lark[interegular]'`.") + + ## + + self.newline_types = frozenset(t.name for t in terminals if _regexp_has_newline(t.pattern.to_regexp())) + self.ignore_types = frozenset(conf.ignore) + + terminals.sort(key=lambda x: (-x.priority, -x.pattern.max_width, -len(x.pattern.value), x.name)) + self.terminals = terminals + self.user_callbacks = conf.callbacks + self.g_regex_flags = conf.g_regex_flags + self.use_bytes = conf.use_bytes + self.terminals_by_name = conf.terminals_by_name + + self._scanner: Optional[Scanner] = None + + def _build_scanner(self) -> Scanner: + terminals, self.callback = _create_unless(self.terminals, self.g_regex_flags, self.re, self.use_bytes) + assert all(self.callback.values()) + + for type_, f in self.user_callbacks.items(): + if type_ in self.callback: + ## + + self.callback[type_] = CallChain(self.callback[type_], f, lambda t: t.type == type_) + else: + self.callback[type_] = f + + return Scanner(terminals, self.g_regex_flags, self.re, self.use_bytes) + + @property + def scanner(self) -> Scanner: + if self._scanner is None: + self._scanner = self._build_scanner() + return self._scanner + + def match(self, text, pos): + return self.scanner.match(text, pos) + + def next_token(self, lex_state: LexerState, parser_state: Any = None) -> Token: + line_ctr = lex_state.line_ctr + while line_ctr.char_pos < lex_state.text.end: + res = self.match(lex_state.text, line_ctr.char_pos) + if not res: + allowed = self.scanner.allowed_types - self.ignore_types + if not allowed: + allowed = {""} + raise UnexpectedCharacters(lex_state.text.text, line_ctr.char_pos, line_ctr.line, line_ctr.column, + allowed=allowed, token_history=lex_state.last_token and [lex_state.last_token], + state=parser_state, terminals_by_name=self.terminals_by_name) + + value, type_ = res + + ignored = type_ in self.ignore_types + t = None + if not ignored or type_ in self.callback: + t = Token(type_, value, line_ctr.char_pos, line_ctr.line, line_ctr.column) + line_ctr.feed(value, type_ in self.newline_types) + if t is not None: + t.end_line = line_ctr.line + t.end_column = line_ctr.column + t.end_pos = line_ctr.char_pos + if t.type in self.callback: + t = self.callback[t.type](t) + if not ignored: + if not isinstance(t, Token): + raise LexError("Callbacks must return a token (returned %r)" % t) + lex_state.last_token = t + return t + + ## + + raise EOFError(self) + + +class ContextualLexer(Lexer): + lexers: Dict[int, AbstractBasicLexer] + root_lexer: AbstractBasicLexer + + BasicLexer: Type[AbstractBasicLexer] = BasicLexer + + def __init__(self, conf: 'LexerConf', states: Dict[int, Collection[str]], always_accept: Collection[str]=()) -> None: + terminals = list(conf.terminals) + terminals_by_name = conf.terminals_by_name + + trad_conf = copy(conf) + trad_conf.terminals = terminals + + if has_interegular and not conf.skip_validation: + comparator = interegular.Comparator.from_regexes({t: t.pattern.to_regexp() for t in terminals}) + else: + comparator = None + lexer_by_tokens: Dict[FrozenSet[str], AbstractBasicLexer] = {} + self.lexers = {} + for state, accepts in states.items(): + key = frozenset(accepts) + try: + lexer = lexer_by_tokens[key] + except KeyError: + accepts = set(accepts) | set(conf.ignore) | set(always_accept) + lexer_conf = copy(trad_conf) + lexer_conf.terminals = [terminals_by_name[n] for n in accepts if n in terminals_by_name] + lexer = self.BasicLexer(lexer_conf, comparator) + lexer_by_tokens[key] = lexer + + self.lexers[state] = lexer + + assert trad_conf.terminals is terminals + trad_conf.skip_validation = True ## + + self.root_lexer = self.BasicLexer(trad_conf, comparator) + + def lex(self, lexer_state: LexerState, parser_state: 'ParserState') -> Iterator[Token]: + try: + while True: + lexer = self.lexers[parser_state.position] + yield lexer.next_token(lexer_state, parser_state) + except EOFError: + pass + except UnexpectedCharacters as e: + ## + + ## + + try: + last_token = lexer_state.last_token ## + + token = self.root_lexer.next_token(lexer_state, parser_state) + raise UnexpectedToken(token, e.allowed, state=parser_state, token_history=[last_token], terminals_by_name=self.root_lexer.terminals_by_name) + except UnexpectedCharacters: + raise e ## + + + + +_ParserArgType: 'TypeAlias' = 'Literal["earley", "lalr", "cyk", "auto"]' +_LexerArgType: 'TypeAlias' = 'Union[Literal["auto", "basic", "contextual", "dynamic", "dynamic_complete"], Type[Lexer]]' +_LexerCallback = Callable[[Token], Token] +ParserCallbacks = Dict[str, Callable] + +class LexerConf(Serialize): + __serialize_fields__ = 'terminals', 'ignore', 'g_regex_flags', 'use_bytes', 'lexer_type' + __serialize_namespace__ = TerminalDef, + + terminals: Collection[TerminalDef] + re_module: ModuleType + ignore: Collection[str] + postlex: 'Optional[PostLex]' + callbacks: Dict[str, _LexerCallback] + g_regex_flags: int + skip_validation: bool + use_bytes: bool + lexer_type: Optional[_LexerArgType] + strict: bool + + def __init__(self, terminals: Collection[TerminalDef], re_module: ModuleType, ignore: Collection[str]=(), postlex: 'Optional[PostLex]'=None, + callbacks: Optional[Dict[str, _LexerCallback]]=None, g_regex_flags: int=0, skip_validation: bool=False, use_bytes: bool=False, strict: bool=False): + self.terminals = terminals + self.terminals_by_name = {t.name: t for t in self.terminals} + assert len(self.terminals) == len(self.terminals_by_name) + self.ignore = ignore + self.postlex = postlex + self.callbacks = callbacks or {} + self.g_regex_flags = g_regex_flags + self.re_module = re_module + self.skip_validation = skip_validation + self.use_bytes = use_bytes + self.strict = strict + self.lexer_type = None + + def _deserialize(self): + self.terminals_by_name = {t.name: t for t in self.terminals} + + def __deepcopy__(self, memo=None): + return type(self)( + deepcopy(self.terminals, memo), + self.re_module, + deepcopy(self.ignore, memo), + deepcopy(self.postlex, memo), + deepcopy(self.callbacks, memo), + deepcopy(self.g_regex_flags, memo), + deepcopy(self.skip_validation, memo), + deepcopy(self.use_bytes, memo), + ) + +class ParserConf(Serialize): + __serialize_fields__ = 'rules', 'start', 'parser_type' + + rules: List['Rule'] + callbacks: ParserCallbacks + start: List[str] + parser_type: _ParserArgType + + def __init__(self, rules: List['Rule'], callbacks: ParserCallbacks, start: List[str]): + assert isinstance(start, list) + self.rules = rules + self.callbacks = callbacks + self.start = start + + +from functools import partial, wraps +from itertools import product + + +class ExpandSingleChild: + def __init__(self, node_builder): + self.node_builder = node_builder + + def __call__(self, children): + if len(children) == 1: + return children[0] + else: + return self.node_builder(children) + + + +class PropagatePositions: + def __init__(self, node_builder, node_filter=None): + self.node_builder = node_builder + self.node_filter = node_filter + + def __call__(self, children): + res = self.node_builder(children) + + if isinstance(res, Tree): + ## + + ## + + ## + + ## + + + res_meta = res.meta + + first_meta = self._pp_get_meta(children) + if first_meta is not None: + if not hasattr(res_meta, 'line'): + ## + + res_meta.line = getattr(first_meta, 'container_line', first_meta.line) + res_meta.column = getattr(first_meta, 'container_column', first_meta.column) + res_meta.start_pos = getattr(first_meta, 'container_start_pos', first_meta.start_pos) + res_meta.empty = False + + res_meta.container_line = getattr(first_meta, 'container_line', first_meta.line) + res_meta.container_column = getattr(first_meta, 'container_column', first_meta.column) + res_meta.container_start_pos = getattr(first_meta, 'container_start_pos', first_meta.start_pos) + + last_meta = self._pp_get_meta(reversed(children)) + if last_meta is not None: + if not hasattr(res_meta, 'end_line'): + res_meta.end_line = getattr(last_meta, 'container_end_line', last_meta.end_line) + res_meta.end_column = getattr(last_meta, 'container_end_column', last_meta.end_column) + res_meta.end_pos = getattr(last_meta, 'container_end_pos', last_meta.end_pos) + res_meta.empty = False + + res_meta.container_end_line = getattr(last_meta, 'container_end_line', last_meta.end_line) + res_meta.container_end_column = getattr(last_meta, 'container_end_column', last_meta.end_column) + res_meta.container_end_pos = getattr(last_meta, 'container_end_pos', last_meta.end_pos) + + return res + + def _pp_get_meta(self, children): + for c in children: + if self.node_filter is not None and not self.node_filter(c): + continue + if isinstance(c, Tree): + if not c.meta.empty: + return c.meta + elif isinstance(c, Token): + return c + elif hasattr(c, '__lark_meta__'): + return c.__lark_meta__() + +def make_propagate_positions(option): + if callable(option): + return partial(PropagatePositions, node_filter=option) + elif option is True: + return PropagatePositions + elif option is False: + return None + + raise ConfigurationError('Invalid option for propagate_positions: %r' % option) + + +class ChildFilter: + def __init__(self, to_include, append_none, node_builder): + self.node_builder = node_builder + self.to_include = to_include + self.append_none = append_none + + def __call__(self, children): + filtered = [] + + for i, to_expand, add_none in self.to_include: + if add_none: + filtered += [None] * add_none + if to_expand: + filtered += children[i].children + else: + filtered.append(children[i]) + + if self.append_none: + filtered += [None] * self.append_none + + return self.node_builder(filtered) + + +class ChildFilterLALR(ChildFilter): + #-- + + def __call__(self, children): + filtered = [] + for i, to_expand, add_none in self.to_include: + if add_none: + filtered += [None] * add_none + if to_expand: + if filtered: + filtered += children[i].children + else: ## + + filtered = children[i].children + else: + filtered.append(children[i]) + + if self.append_none: + filtered += [None] * self.append_none + + return self.node_builder(filtered) + + +class ChildFilterLALR_NoPlaceholders(ChildFilter): + #-- + def __init__(self, to_include, node_builder): + self.node_builder = node_builder + self.to_include = to_include + + def __call__(self, children): + filtered = [] + for i, to_expand in self.to_include: + if to_expand: + if filtered: + filtered += children[i].children + else: ## + + filtered = children[i].children + else: + filtered.append(children[i]) + return self.node_builder(filtered) + + +def _should_expand(sym): + return not sym.is_term and sym.name.startswith('_') + + +def maybe_create_child_filter(expansion, keep_all_tokens, ambiguous, _empty_indices: List[bool]): + ## + + if _empty_indices: + assert _empty_indices.count(False) == len(expansion) + s = ''.join(str(int(b)) for b in _empty_indices) + empty_indices = [len(ones) for ones in s.split('0')] + assert len(empty_indices) == len(expansion)+1, (empty_indices, len(expansion)) + else: + empty_indices = [0] * (len(expansion)+1) + + to_include = [] + nones_to_add = 0 + for i, sym in enumerate(expansion): + nones_to_add += empty_indices[i] + if keep_all_tokens or not (sym.is_term and sym.filter_out): + to_include.append((i, _should_expand(sym), nones_to_add)) + nones_to_add = 0 + + nones_to_add += empty_indices[len(expansion)] + + if _empty_indices or len(to_include) < len(expansion) or any(to_expand for i, to_expand,_ in to_include): + if _empty_indices or ambiguous: + return partial(ChildFilter if ambiguous else ChildFilterLALR, to_include, nones_to_add) + else: + ## + + return partial(ChildFilterLALR_NoPlaceholders, [(i, x) for i,x,_ in to_include]) + + +class AmbiguousExpander: + #-- + def __init__(self, to_expand, tree_class, node_builder): + self.node_builder = node_builder + self.tree_class = tree_class + self.to_expand = to_expand + + def __call__(self, children): + def _is_ambig_tree(t): + return hasattr(t, 'data') and t.data == '_ambig' + + ## + + ## + + ## + + ## + + ambiguous = [] + for i, child in enumerate(children): + if _is_ambig_tree(child): + if i in self.to_expand: + ambiguous.append(i) + + child.expand_kids_by_data('_ambig') + + if not ambiguous: + return self.node_builder(children) + + expand = [child.children if i in ambiguous else (child,) for i, child in enumerate(children)] + return self.tree_class('_ambig', [self.node_builder(list(f)) for f in product(*expand)]) + + +def maybe_create_ambiguous_expander(tree_class, expansion, keep_all_tokens): + to_expand = [i for i, sym in enumerate(expansion) + if keep_all_tokens or ((not (sym.is_term and sym.filter_out)) and _should_expand(sym))] + if to_expand: + return partial(AmbiguousExpander, to_expand, tree_class) + + +class AmbiguousIntermediateExpander: + #-- + + def __init__(self, tree_class, node_builder): + self.node_builder = node_builder + self.tree_class = tree_class + + def __call__(self, children): + def _is_iambig_tree(child): + return hasattr(child, 'data') and child.data == '_iambig' + + def _collapse_iambig(children): + #-- + + ## + + ## + + if children and _is_iambig_tree(children[0]): + iambig_node = children[0] + result = [] + for grandchild in iambig_node.children: + collapsed = _collapse_iambig(grandchild.children) + if collapsed: + for child in collapsed: + child.children += children[1:] + result += collapsed + else: + new_tree = self.tree_class('_inter', grandchild.children + children[1:]) + result.append(new_tree) + return result + + collapsed = _collapse_iambig(children) + if collapsed: + processed_nodes = [self.node_builder(c.children) for c in collapsed] + return self.tree_class('_ambig', processed_nodes) + + return self.node_builder(children) + + + +def inplace_transformer(func): + @wraps(func) + def f(children): + ## + + tree = Tree(func.__name__, children) + return func(tree) + return f + + +def apply_visit_wrapper(func, name, wrapper): + if wrapper is _vargs_meta or wrapper is _vargs_meta_inline: + raise NotImplementedError("Meta args not supported for internal transformer; use YourTransformer().transform(parser.parse()) instead") + + @wraps(func) + def f(children): + return wrapper(func, name, children, None) + return f + + +class ParseTreeBuilder: + def __init__(self, rules, tree_class, propagate_positions=False, ambiguous=False, maybe_placeholders=False): + self.tree_class = tree_class + self.propagate_positions = propagate_positions + self.ambiguous = ambiguous + self.maybe_placeholders = maybe_placeholders + + self.rule_builders = list(self._init_builders(rules)) + + def _init_builders(self, rules): + propagate_positions = make_propagate_positions(self.propagate_positions) + + for rule in rules: + options = rule.options + keep_all_tokens = options.keep_all_tokens + expand_single_child = options.expand1 + + wrapper_chain = list(filter(None, [ + (expand_single_child and not rule.alias) and ExpandSingleChild, + maybe_create_child_filter(rule.expansion, keep_all_tokens, self.ambiguous, options.empty_indices if self.maybe_placeholders else None), + propagate_positions, + self.ambiguous and maybe_create_ambiguous_expander(self.tree_class, rule.expansion, keep_all_tokens), + self.ambiguous and partial(AmbiguousIntermediateExpander, self.tree_class) + ])) + + yield rule, wrapper_chain + + def create_callback(self, transformer=None): + callbacks = {} + + default_handler = getattr(transformer, '__default__', None) + if default_handler: + def default_callback(data, children): + return default_handler(data, children, None) + else: + default_callback = self.tree_class + + for rule, wrapper_chain in self.rule_builders: + + user_callback_name = rule.alias or rule.options.template_source or rule.origin.name + try: + f = getattr(transformer, user_callback_name) + wrapper = getattr(f, 'visit_wrapper', None) + if wrapper is not None: + f = apply_visit_wrapper(f, user_callback_name, wrapper) + elif isinstance(transformer, Transformer_InPlace): + f = inplace_transformer(f) + except AttributeError: + f = partial(default_callback, user_callback_name) + + for w in wrapper_chain: + f = w(f) + + if rule in callbacks: + raise GrammarError("Rule '%s' already exists" % (rule,)) + + callbacks[rule] = f + + return callbacks + + + +class Action: + def __init__(self, name): + self.name = name + def __str__(self): + return self.name + def __repr__(self): + return str(self) + +Shift = Action('Shift') +Reduce = Action('Reduce') + +StateT = TypeVar("StateT") + +class ParseTableBase(Generic[StateT]): + states: Dict[StateT, Dict[str, Tuple]] + start_states: Dict[str, StateT] + end_states: Dict[str, StateT] + + def __init__(self, states, start_states, end_states): + self.states = states + self.start_states = start_states + self.end_states = end_states + + def serialize(self, memo): + tokens = Enumerator() + + states = { + state: {tokens.get(token): ((1, arg.serialize(memo)) if action is Reduce else (0, arg)) + for token, (action, arg) in actions.items()} + for state, actions in self.states.items() + } + + return { + 'tokens': tokens.reversed(), + 'states': states, + 'start_states': self.start_states, + 'end_states': self.end_states, + } + + @classmethod + def deserialize(cls, data, memo): + tokens = data['tokens'] + states = { + state: {tokens[token]: ((Reduce, Rule.deserialize(arg, memo)) if action==1 else (Shift, arg)) + for token, (action, arg) in actions.items()} + for state, actions in data['states'].items() + } + return cls(states, data['start_states'], data['end_states']) + +class ParseTable(ParseTableBase['State']): + #-- + pass + + +class IntParseTable(ParseTableBase[int]): + #-- + + @classmethod + def from_ParseTable(cls, parse_table: ParseTable): + enum = list(parse_table.states) + state_to_idx: Dict['State', int] = {s:i for i,s in enumerate(enum)} + int_states = {} + + for s, la in parse_table.states.items(): + la = {k:(v[0], state_to_idx[v[1]]) if v[0] is Shift else v + for k,v in la.items()} + int_states[ state_to_idx[s] ] = la + + + start_states = {start:state_to_idx[s] for start, s in parse_table.start_states.items()} + end_states = {start:state_to_idx[s] for start, s in parse_table.end_states.items()} + return cls(int_states, start_states, end_states) + + + +class ParseConf(Generic[StateT]): + __slots__ = 'parse_table', 'callbacks', 'start', 'start_state', 'end_state', 'states' + + parse_table: ParseTableBase[StateT] + callbacks: ParserCallbacks + start: str + + start_state: StateT + end_state: StateT + states: Dict[StateT, Dict[str, tuple]] + + def __init__(self, parse_table: ParseTableBase[StateT], callbacks: ParserCallbacks, start: str): + self.parse_table = parse_table + + self.start_state = self.parse_table.start_states[start] + self.end_state = self.parse_table.end_states[start] + self.states = self.parse_table.states + + self.callbacks = callbacks + self.start = start + +class ParserState(Generic[StateT]): + __slots__ = 'parse_conf', 'lexer', 'state_stack', 'value_stack' + + parse_conf: ParseConf[StateT] + lexer: LexerThread + state_stack: List[StateT] + value_stack: list + + def __init__(self, parse_conf: ParseConf[StateT], lexer: LexerThread, state_stack=None, value_stack=None): + self.parse_conf = parse_conf + self.lexer = lexer + self.state_stack = state_stack or [self.parse_conf.start_state] + self.value_stack = value_stack or [] + + @property + def position(self) -> StateT: + return self.state_stack[-1] + + ## + + def __eq__(self, other) -> bool: + if not isinstance(other, ParserState): + return NotImplemented + return len(self.state_stack) == len(other.state_stack) and self.position == other.position + + def __copy__(self): + return self.copy() + + def copy(self, deepcopy_values=True) -> 'ParserState[StateT]': + return type(self)( + self.parse_conf, + self.lexer, ## + + copy(self.state_stack), + deepcopy(self.value_stack) if deepcopy_values else copy(self.value_stack), + ) + + def feed_token(self, token: Token, is_end=False) -> Any: + state_stack = self.state_stack + value_stack = self.value_stack + states = self.parse_conf.states + end_state = self.parse_conf.end_state + callbacks = self.parse_conf.callbacks + + while True: + state = state_stack[-1] + try: + action, arg = states[state][token.type] + except KeyError: + expected = {s for s in states[state].keys() if s.isupper()} + raise UnexpectedToken(token, expected, state=self, interactive_parser=None) + + assert arg != end_state + + if action is Shift: + ## + + assert not is_end + state_stack.append(arg) + value_stack.append(token if token.type not in callbacks else callbacks[token.type](token)) + return + else: + ## + + rule = arg + size = len(rule.expansion) + if size: + s = value_stack[-size:] + del state_stack[-size:] + del value_stack[-size:] + else: + s = [] + + value = callbacks[rule](s) if callbacks else s + + _action, new_state = states[state_stack[-1]][rule.origin.name] + assert _action is Shift + state_stack.append(new_state) + value_stack.append(value) + + if is_end and state_stack[-1] == end_state: + return value_stack[-1] + + +class LALR_Parser(Serialize): + def __init__(self, parser_conf: ParserConf, debug: bool=False, strict: bool=False): + analysis = LALR_Analyzer(parser_conf, debug=debug, strict=strict) + analysis.compute_lalr() + callbacks = parser_conf.callbacks + + self._parse_table = analysis.parse_table + self.parser_conf = parser_conf + self.parser = _Parser(analysis.parse_table, callbacks, debug) + + @classmethod + def deserialize(cls, data, memo, callbacks, debug=False): + inst = cls.__new__(cls) + inst._parse_table = IntParseTable.deserialize(data, memo) + inst.parser = _Parser(inst._parse_table, callbacks, debug) + return inst + + def serialize(self, memo: Any = None) -> Dict[str, Any]: + return self._parse_table.serialize(memo) + + def parse_interactive(self, lexer: LexerThread, start: str): + return self.parser.parse(lexer, start, start_interactive=True) + + def parse(self, lexer, start, on_error=None): + try: + return self.parser.parse(lexer, start) + except UnexpectedInput as e: + if on_error is None: + raise + + while True: + if isinstance(e, UnexpectedCharacters): + s = e.interactive_parser.lexer_thread.state + p = s.line_ctr.char_pos + + if not on_error(e): + raise e + + if isinstance(e, UnexpectedCharacters): + ## + + if p == s.line_ctr.char_pos: + s.line_ctr.feed(s.text.text[p:p+1]) + + try: + return e.interactive_parser.resume_parse() + except UnexpectedToken as e2: + if (isinstance(e, UnexpectedToken) + and e.token.type == e2.token.type == '$END' + and e.interactive_parser == e2.interactive_parser): + ## + + raise e2 + e = e2 + except UnexpectedCharacters as e2: + e = e2 + + +class _Parser: + parse_table: ParseTableBase + callbacks: ParserCallbacks + debug: bool + + def __init__(self, parse_table: ParseTableBase, callbacks: ParserCallbacks, debug: bool=False): + self.parse_table = parse_table + self.callbacks = callbacks + self.debug = debug + + def parse(self, lexer: LexerThread, start: str, value_stack=None, state_stack=None, start_interactive=False): + parse_conf = ParseConf(self.parse_table, self.callbacks, start) + parser_state = ParserState(parse_conf, lexer, state_stack, value_stack) + if start_interactive: + return InteractiveParser(self, parser_state, parser_state.lexer) + return self.parse_from_state(parser_state) + + + def parse_from_state(self, state: ParserState, last_token: Optional[Token]=None): + #-- + try: + token = last_token + for token in state.lexer.lex(state): + assert token is not None + state.feed_token(token) + + end_token = Token.new_borrow_pos('$END', '', token) if token else Token('$END', '', 0, 1, 1) + return state.feed_token(end_token, True) + except UnexpectedInput as e: + try: + e.interactive_parser = InteractiveParser(self, state, state.lexer) + except NameError: + pass + raise e + except Exception as e: + if self.debug: + print("") + print("STATE STACK DUMP") + print("----------------") + for i, s in enumerate(state.state_stack): + print('%d)' % i , s) + print("") + + raise + + +class InteractiveParser: + #-- + def __init__(self, parser, parser_state: ParserState, lexer_thread: LexerThread): + self.parser = parser + self.parser_state = parser_state + self.lexer_thread = lexer_thread + self.result = None + + @property + def lexer_state(self) -> LexerThread: + warnings.warn("lexer_state will be removed in subsequent releases. Use lexer_thread instead.", DeprecationWarning) + return self.lexer_thread + + def feed_token(self, token: Token): + #-- + return self.parser_state.feed_token(token, token.type == '$END') + + def iter_parse(self) -> Iterator[Token]: + #-- + for token in self.lexer_thread.lex(self.parser_state): + yield token + self.result = self.feed_token(token) + + def exhaust_lexer(self) -> List[Token]: + #-- + return list(self.iter_parse()) + + + def feed_eof(self, last_token=None): + #-- + eof = Token.new_borrow_pos('$END', '', last_token) if last_token is not None else self.lexer_thread._Token('$END', '', 0, 1, 1) + return self.feed_token(eof) + + + def __copy__(self): + #-- + return self.copy() + + def copy(self, deepcopy_values=True): + return type(self)( + self.parser, + self.parser_state.copy(deepcopy_values=deepcopy_values), + copy(self.lexer_thread), + ) + + def __eq__(self, other): + if not isinstance(other, InteractiveParser): + return False + + return self.parser_state == other.parser_state and self.lexer_thread == other.lexer_thread + + def as_immutable(self): + #-- + p = copy(self) + return ImmutableInteractiveParser(p.parser, p.parser_state, p.lexer_thread) + + def pretty(self): + #-- + out = ["Parser choices:"] + for k, v in self.choices().items(): + out.append('\t- %s -> %r' % (k, v)) + out.append('stack size: %s' % len(self.parser_state.state_stack)) + return '\n'.join(out) + + def choices(self): + #-- + return self.parser_state.parse_conf.parse_table.states[self.parser_state.position] + + def accepts(self): + #-- + accepts = set() + conf_no_callbacks = copy(self.parser_state.parse_conf) + ## + + ## + + conf_no_callbacks.callbacks = {} + for t in self.choices(): + if t.isupper(): ## + + new_cursor = self.copy(deepcopy_values=False) + new_cursor.parser_state.parse_conf = conf_no_callbacks + try: + new_cursor.feed_token(self.lexer_thread._Token(t, '')) + except UnexpectedToken: + pass + else: + accepts.add(t) + return accepts + + def resume_parse(self): + #-- + return self.parser.parse_from_state(self.parser_state, last_token=self.lexer_thread.state.last_token) + + + +class ImmutableInteractiveParser(InteractiveParser): + #-- + + result = None + + def __hash__(self): + return hash((self.parser_state, self.lexer_thread)) + + def feed_token(self, token): + c = copy(self) + c.result = InteractiveParser.feed_token(c, token) + return c + + def exhaust_lexer(self): + #-- + cursor = self.as_mutable() + cursor.exhaust_lexer() + return cursor.as_immutable() + + def as_mutable(self): + #-- + p = copy(self) + return InteractiveParser(p.parser, p.parser_state, p.lexer_thread) + + + +def _wrap_lexer(lexer_class): + future_interface = getattr(lexer_class, '__future_interface__', 0) + if future_interface == 2: + return lexer_class + elif future_interface == 1: + class CustomLexerWrapper1(Lexer): + def __init__(self, lexer_conf): + self.lexer = lexer_class(lexer_conf) + def lex(self, lexer_state, parser_state): + if isinstance(lexer_state.text, TextSlice) and not lexer_state.text.is_complete_text(): + raise TypeError("Interface=1 Custom Lexer don't support TextSlice") + lexer_state.text = lexer_state.text + return self.lexer.lex(lexer_state, parser_state) + return CustomLexerWrapper1 + elif future_interface == 0: + class CustomLexerWrapper0(Lexer): + def __init__(self, lexer_conf): + self.lexer = lexer_class(lexer_conf) + + def lex(self, lexer_state, parser_state): + if isinstance(lexer_state.text, TextSlice): + if not lexer_state.text.is_complete_text(): + raise TypeError("Interface=0 Custom Lexer don't support TextSlice") + return self.lexer.lex(lexer_state.text.text) + return self.lexer.lex(lexer_state.text) + return CustomLexerWrapper0 + else: + raise ValueError(f"Unknown __future_interface__ value {future_interface}, integer 0-2 expected") + + +def _deserialize_parsing_frontend(data, memo, lexer_conf, callbacks, options): + parser_conf = ParserConf.deserialize(data['parser_conf'], memo) + cls = (options and options._plugins.get('LALR_Parser')) or LALR_Parser + parser = cls.deserialize(data['parser'], memo, callbacks, options.debug) + parser_conf.callbacks = callbacks + return ParsingFrontend(lexer_conf, parser_conf, options, parser=parser) + + +_parser_creators: 'Dict[str, Callable[[LexerConf, Any, Any], Any]]' = {} + + +class ParsingFrontend(Serialize): + __serialize_fields__ = 'lexer_conf', 'parser_conf', 'parser' + + lexer_conf: LexerConf + parser_conf: ParserConf + options: Any + + def __init__(self, lexer_conf: LexerConf, parser_conf: ParserConf, options, parser=None): + self.parser_conf = parser_conf + self.lexer_conf = lexer_conf + self.options = options + + ## + + if parser: ## + + self.parser = parser + else: + create_parser = _parser_creators.get(parser_conf.parser_type) + assert create_parser is not None, "{} is not supported in standalone mode".format( + parser_conf.parser_type + ) + self.parser = create_parser(lexer_conf, parser_conf, options) + + ## + + lexer_type = options.lexer if (options and options.lexer) else lexer_conf.lexer_type + self.skip_lexer = False + if lexer_type in ('dynamic', 'dynamic_complete'): + assert lexer_conf.postlex is None + self.skip_lexer = True + return + + if isinstance(lexer_type, type): + assert issubclass(lexer_type, Lexer) or hasattr(lexer_type, 'lex') + self.lexer = _wrap_lexer(lexer_type)(lexer_conf) + elif isinstance(lexer_type, str): + create_lexer = { + 'basic': create_basic_lexer, + 'contextual': create_contextual_lexer, + }[lexer_type] + self.lexer = create_lexer(lexer_conf, self.parser, lexer_conf.postlex, options) + else: + raise TypeError("Bad value for lexer_type: {lexer_type}") + + if lexer_conf.postlex: + self.lexer = PostLexConnector(self.lexer, lexer_conf.postlex) + + def _verify_start(self, start=None): + if start is None: + start_decls = self.parser_conf.start + if len(start_decls) > 1: + raise ConfigurationError("Lark initialized with more than 1 possible start rule. Must specify which start rule to parse", start_decls) + start ,= start_decls + elif start not in self.parser_conf.start: + raise ConfigurationError("Unknown start rule %s. Must be one of %r" % (start, self.parser_conf.start)) + return start + + def _make_lexer_thread(self, text: Optional[LarkInput]) -> Union[LarkInput, LexerThread, None]: + cls = (self.options and self.options._plugins.get('LexerThread')) or LexerThread + if self.skip_lexer: + return text + if text is None: + return cls(self.lexer, None) + if isinstance(text, (str, bytes, TextSlice)): + return cls.from_text(self.lexer, text) + return cls.from_custom_input(self.lexer, text) + + def parse(self, text: Optional[LarkInput], start=None, on_error=None): + if self.lexer_conf.lexer_type in ("dynamic", "dynamic_complete"): + if isinstance(text, TextSlice) and not text.is_complete_text(): + raise TypeError(f"Lexer {self.lexer_conf.lexer_type} does not support text slices.") + + chosen_start = self._verify_start(start) + kw = {} if on_error is None else {'on_error': on_error} + stream = self._make_lexer_thread(text) + return self.parser.parse(stream, chosen_start, **kw) + + def parse_interactive(self, text: Optional[TextOrSlice]=None, start=None): + ## + + ## + + chosen_start = self._verify_start(start) + if self.parser_conf.parser_type != 'lalr': + raise ConfigurationError("parse_interactive() currently only works with parser='lalr' ") + stream = self._make_lexer_thread(text) + return self.parser.parse_interactive(stream, chosen_start) + + +def _validate_frontend_args(parser, lexer) -> None: + assert_config(parser, ('lalr', 'earley', 'cyk')) + if not isinstance(lexer, type): ## + + expected = { + 'lalr': ('basic', 'contextual'), + 'earley': ('basic', 'dynamic', 'dynamic_complete'), + 'cyk': ('basic', ), + }[parser] + assert_config(lexer, expected, 'Parser %r does not support lexer %%r, expected one of %%s' % parser) + + +def _get_lexer_callbacks(transformer, terminals): + result = {} + for terminal in terminals: + callback = getattr(transformer, terminal.name, None) + if callback is not None: + result[terminal.name] = callback + return result + +class PostLexConnector: + def __init__(self, lexer, postlexer): + self.lexer = lexer + self.postlexer = postlexer + + def lex(self, lexer_state, parser_state): + i = self.lexer.lex(lexer_state, parser_state) + return self.postlexer.process(i) + + + +def create_basic_lexer(lexer_conf, parser, postlex, options) -> BasicLexer: + cls = (options and options._plugins.get('BasicLexer')) or BasicLexer + return cls(lexer_conf) + +def create_contextual_lexer(lexer_conf: LexerConf, parser, postlex, options) -> ContextualLexer: + cls = (options and options._plugins.get('ContextualLexer')) or ContextualLexer + parse_table: ParseTableBase[int] = parser._parse_table + states: Dict[int, Collection[str]] = {idx:list(t.keys()) for idx, t in parse_table.states.items()} + always_accept: Collection[str] = postlex.always_accept if postlex else () + return cls(lexer_conf, states, always_accept=always_accept) + +def create_lalr_parser(lexer_conf: LexerConf, parser_conf: ParserConf, options=None) -> LALR_Parser: + debug = options.debug if options else False + strict = options.strict if options else False + cls = (options and options._plugins.get('LALR_Parser')) or LALR_Parser + return cls(parser_conf, debug=debug, strict=strict) + +_parser_creators['lalr'] = create_lalr_parser + + + + +class PostLex(ABC): + @abstractmethod + def process(self, stream: Iterator[Token]) -> Iterator[Token]: + return stream + + always_accept: Iterable[str] = () + +class LarkOptions(Serialize): + #-- + + start: List[str] + debug: bool + strict: bool + transformer: 'Optional[Transformer]' + propagate_positions: Union[bool, str] + maybe_placeholders: bool + cache: Union[bool, str] + cache_grammar: bool + regex: bool + g_regex_flags: int + keep_all_tokens: bool + tree_class: Optional[Callable[[str, List], Any]] + parser: _ParserArgType + lexer: _LexerArgType + ambiguity: 'Literal["auto", "resolve", "explicit", "forest"]' + postlex: Optional[PostLex] + priority: 'Optional[Literal["auto", "normal", "invert"]]' + lexer_callbacks: Dict[str, Callable[[Token], Token]] + use_bytes: bool + ordered_sets: bool + edit_terminals: Optional[Callable[[TerminalDef], TerminalDef]] + import_paths: 'List[Union[str, Callable[[Union[None, str, PackageResource], str], Tuple[str, str]]]]' + source_path: Optional[str] + + OPTIONS_DOC = r""" + **=== General Options ===** + + start + The start symbol. Either a string, or a list of strings for multiple possible starts (Default: "start") + debug + Display debug information and extra warnings. Use only when debugging (Default: ``False``) + When used with Earley, it generates a forest graph as "sppf.png", if 'dot' is installed. + strict + Throw an exception on any potential ambiguity, including shift/reduce conflicts, and regex collisions. + transformer + Applies the transformer to every parse tree (equivalent to applying it after the parse, but faster) + propagate_positions + Propagates positional attributes into the 'meta' attribute of all tree branches. + Sets attributes: (line, column, end_line, end_column, start_pos, end_pos, + container_line, container_column, container_end_line, container_end_column) + Accepts ``False``, ``True``, or a callable, which will filter which nodes to ignore when propagating. + maybe_placeholders + When ``True``, the ``[]`` operator returns ``None`` when not matched. + When ``False``, ``[]`` behaves like the ``?`` operator, and returns no value at all. + (default= ``True``) + cache + Cache the results of the Lark grammar analysis, for x2 to x3 faster loading. LALR only for now. + + - When ``False``, does nothing (default) + - When ``True``, caches to a temporary file in the local directory + - When given a string, caches to the path pointed by the string + cache_grammar + For use with ``cache`` option. When ``True``, the unanalyzed grammar is also included in the cache. + Useful for classes that require the ``Lark.grammar`` to be present (e.g. Reconstructor). + (default= ``False``) + regex + When True, uses the ``regex`` module instead of the stdlib ``re``. + g_regex_flags + Flags that are applied to all terminals (both regex and strings) + keep_all_tokens + Prevent the tree builder from automagically removing "punctuation" tokens (Default: ``False``) + tree_class + Lark will produce trees comprised of instances of this class instead of the default ``lark.Tree``. + + **=== Algorithm Options ===** + + parser + Decides which parser engine to use. Accepts "earley" or "lalr". (Default: "earley"). + (there is also a "cyk" option for legacy) + lexer + Decides whether or not to use a lexer stage + + - "auto" (default): Choose for me based on the parser + - "basic": Use a basic lexer + - "contextual": Stronger lexer (only works with parser="lalr") + - "dynamic": Flexible and powerful (only with parser="earley") + - "dynamic_complete": Same as dynamic, but tries *every* variation of tokenizing possible. + ambiguity + Decides how to handle ambiguity in the parse. Only relevant if parser="earley" + + - "resolve": The parser will automatically choose the simplest derivation + (it chooses consistently: greedy for tokens, non-greedy for rules) + - "explicit": The parser will return all derivations wrapped in "_ambig" tree nodes (i.e. a forest). + - "forest": The parser will return the root of the shared packed parse forest. + + **=== Misc. / Domain Specific Options ===** + + postlex + Lexer post-processing (Default: ``None``) Only works with the basic and contextual lexers. + priority + How priorities should be evaluated - "auto", ``None``, "normal", "invert" (Default: "auto") + lexer_callbacks + Dictionary of callbacks for the lexer. May alter tokens during lexing. Use with caution. + use_bytes + Accept an input of type ``bytes`` instead of ``str``. + ordered_sets + Should Earley use ordered-sets to achieve stable output (~10% slower than regular sets. Default: True) + edit_terminals + A callback for editing the terminals before parse. + import_paths + A List of either paths or loader functions to specify from where grammars are imported + source_path + Override the source of from where the grammar was loaded. Useful for relative imports and unconventional grammar loading + **=== End of Options ===** + """ + if __doc__: + __doc__ += OPTIONS_DOC + + + ## + + ## + + ## + + ## + + ## + + ## + + _defaults: Dict[str, Any] = { + 'debug': False, + 'strict': False, + 'keep_all_tokens': False, + 'tree_class': None, + 'cache': False, + 'cache_grammar': False, + 'postlex': None, + 'parser': 'earley', + 'lexer': 'auto', + 'transformer': None, + 'start': 'start', + 'priority': 'auto', + 'ambiguity': 'auto', + 'regex': False, + 'propagate_positions': False, + 'lexer_callbacks': {}, + 'maybe_placeholders': True, + 'edit_terminals': None, + 'g_regex_flags': 0, + 'use_bytes': False, + 'ordered_sets': True, + 'import_paths': [], + 'source_path': None, + '_plugins': {}, + } + + def __init__(self, options_dict: Dict[str, Any]) -> None: + o = dict(options_dict) + + options = {} + for name, default in self._defaults.items(): + if name in o: + value = o.pop(name) + if isinstance(default, bool) and name not in ('cache', 'use_bytes', 'propagate_positions'): + value = bool(value) + else: + value = default + + options[name] = value + + if isinstance(options['start'], str): + options['start'] = [options['start']] + + self.__dict__['options'] = options + + + assert_config(self.parser, ('earley', 'lalr', 'cyk', None)) + + if self.parser == 'earley' and self.transformer: + raise ConfigurationError('Cannot specify an embedded transformer when using the Earley algorithm. ' + 'Please use your transformer on the resulting parse tree, or use a different algorithm (i.e. LALR)') + + if self.cache_grammar and not self.cache: + raise ConfigurationError('cache_grammar cannot be set when cache is disabled') + + if o: + raise ConfigurationError("Unknown options: %s" % o.keys()) + + def __getattr__(self, name: str) -> Any: + try: + return self.__dict__['options'][name] + except KeyError as e: + raise AttributeError(e) + + def __setattr__(self, name: str, value: str) -> None: + assert_config(name, self.options.keys(), "%r isn't a valid option. Expected one of: %s") + self.options[name] = value + + def serialize(self, memo = None) -> Dict[str, Any]: + return self.options + + @classmethod + def deserialize(cls, data: Dict[str, Any], memo: Dict[int, Union[TerminalDef, Rule]]) -> "LarkOptions": + return cls(data) + + +## + +## + +_LOAD_ALLOWED_OPTIONS = {'lexer', 'postlex', 'transformer', 'lexer_callbacks', 'use_bytes', 'debug', 'g_regex_flags', 'regex', 'propagate_positions', 'tree_class', '_plugins'} + +_VALID_PRIORITY_OPTIONS = ('auto', 'normal', 'invert', None) +_VALID_AMBIGUITY_OPTIONS = ('auto', 'resolve', 'explicit', 'forest') + + +_T = TypeVar('_T', bound="Lark") + +class Lark(Serialize): + #-- + + source_path: str + source_grammar: str + grammar: 'Grammar' + options: LarkOptions + lexer: Lexer + parser: 'ParsingFrontend' + terminals: Collection[TerminalDef] + + __serialize_fields__ = ['parser', 'rules', 'options'] + + def __init__(self, grammar: 'Union[Grammar, str, IO[str]]', **options) -> None: + self.options = LarkOptions(options) + re_module: types.ModuleType + + ## + + if self.options.cache_grammar: + self.__serialize_fields__ = self.__serialize_fields__ + ['grammar'] + + ## + + use_regex = self.options.regex + if use_regex: + if _has_regex: + re_module = regex + else: + raise ImportError('`regex` module must be installed if calling `Lark(regex=True)`.') + else: + re_module = re + + ## + + if self.options.source_path is None: + try: + self.source_path = grammar.name ## + + except AttributeError: + self.source_path = '' + else: + self.source_path = self.options.source_path + + ## + + try: + read = grammar.read ## + + except AttributeError: + pass + else: + grammar = read() + + cache_fn = None + cache_sha256 = None + if isinstance(grammar, str): + self.source_grammar = grammar + if self.options.use_bytes: + if not grammar.isascii(): + raise ConfigurationError("Grammar must be ascii only, when use_bytes=True") + + if self.options.cache: + if self.options.parser != 'lalr': + raise ConfigurationError("cache only works with parser='lalr' for now") + + unhashable = ('transformer', 'postlex', 'lexer_callbacks', 'edit_terminals', '_plugins') + options_str = ''.join(k+str(v) for k, v in options.items() if k not in unhashable) + from . import __version__ + s = grammar + options_str + __version__ + str(sys.version_info[:2]) + cache_sha256 = sha256_digest(s) + + if isinstance(self.options.cache, str): + cache_fn = self.options.cache + else: + if self.options.cache is not True: + raise ConfigurationError("cache argument must be bool or str") + + try: + username = getpass.getuser() + except Exception: + ## + + ## + + ## + + username = "unknown" + + + cache_fn = tempfile.gettempdir() + "/.lark_%s_%s_%s_%s_%s.tmp" % ( + "cache_grammar" if self.options.cache_grammar else "cache", username, cache_sha256, *sys.version_info[:2]) + + old_options = self.options + try: + with FS.open(cache_fn, 'rb') as f: + logger.debug('Loading grammar from cache: %s', cache_fn) + ## + + for name in (set(options) - _LOAD_ALLOWED_OPTIONS): + del options[name] + file_sha256 = f.readline().rstrip(b'\n') + cached_used_files = pickle.load(f) + if file_sha256 == cache_sha256.encode('utf8') and verify_used_files(cached_used_files): + cached_parser_data = pickle.load(f) + self._load(cached_parser_data, **options) + return + except FileNotFoundError: + ## + + pass + except Exception: ## + + logger.exception("Failed to load Lark from cache: %r. We will try to carry on.", cache_fn) + + ## + + ## + + self.options = old_options + + + ## + + self.grammar, used_files = load_grammar(grammar, self.source_path, self.options.import_paths, self.options.keep_all_tokens) + else: + assert isinstance(grammar, Grammar) + self.grammar = grammar + + + if self.options.lexer == 'auto': + if self.options.parser == 'lalr': + self.options.lexer = 'contextual' + elif self.options.parser == 'earley': + if self.options.postlex is not None: + logger.info("postlex can't be used with the dynamic lexer, so we use 'basic' instead. " + "Consider using lalr with contextual instead of earley") + self.options.lexer = 'basic' + else: + self.options.lexer = 'dynamic' + elif self.options.parser == 'cyk': + self.options.lexer = 'basic' + else: + assert False, self.options.parser + lexer = self.options.lexer + if isinstance(lexer, type): + assert issubclass(lexer, Lexer) ## + + else: + assert_config(lexer, ('basic', 'contextual', 'dynamic', 'dynamic_complete')) + if self.options.postlex is not None and 'dynamic' in lexer: + raise ConfigurationError("Can't use postlex with a dynamic lexer. Use basic or contextual instead") + + if self.options.ambiguity == 'auto': + if self.options.parser == 'earley': + self.options.ambiguity = 'resolve' + else: + assert_config(self.options.parser, ('earley', 'cyk'), "%r doesn't support disambiguation. Use one of these parsers instead: %s") + + if self.options.priority == 'auto': + self.options.priority = 'normal' + + if self.options.priority not in _VALID_PRIORITY_OPTIONS: + raise ConfigurationError("invalid priority option: %r. Must be one of %r" % (self.options.priority, _VALID_PRIORITY_OPTIONS)) + if self.options.ambiguity not in _VALID_AMBIGUITY_OPTIONS: + raise ConfigurationError("invalid ambiguity option: %r. Must be one of %r" % (self.options.ambiguity, _VALID_AMBIGUITY_OPTIONS)) + + if self.options.parser is None: + terminals_to_keep = '*' ## + + elif self.options.postlex is not None: + terminals_to_keep = set(self.options.postlex.always_accept) + else: + terminals_to_keep = set() + + ## + + self.terminals, self.rules, self.ignore_tokens = self.grammar.compile(self.options.start, terminals_to_keep) + + if self.options.edit_terminals: + for t in self.terminals: + self.options.edit_terminals(t) + + self._terminals_dict = {t.name: t for t in self.terminals} + + ## + + if self.options.priority == 'invert': + for rule in self.rules: + if rule.options.priority is not None: + rule.options.priority = -rule.options.priority + for term in self.terminals: + term.priority = -term.priority + ## + + ## + + ## + + elif self.options.priority is None: + for rule in self.rules: + if rule.options.priority is not None: + rule.options.priority = None + for term in self.terminals: + term.priority = 0 + + ## + + self.lexer_conf = LexerConf( + self.terminals, re_module, self.ignore_tokens, self.options.postlex, + self.options.lexer_callbacks, self.options.g_regex_flags, use_bytes=self.options.use_bytes, strict=self.options.strict + ) + + if self.options.parser: + self.parser = self._build_parser() + elif lexer: + self.lexer = self._build_lexer() + + if cache_fn: + logger.debug('Saving grammar to cache: %s', cache_fn) + try: + with FS.open(cache_fn, 'wb') as f: + assert cache_sha256 is not None + f.write(cache_sha256.encode('utf8') + b'\n') + pickle.dump(used_files, f) + self.save(f, _LOAD_ALLOWED_OPTIONS) + except IOError as e: + logger.exception("Failed to save Lark to cache: %r.", cache_fn, e) + + if __doc__: + __doc__ += "\n\n" + LarkOptions.OPTIONS_DOC + + def _build_lexer(self, dont_ignore: bool=False) -> BasicLexer: + lexer_conf = self.lexer_conf + if dont_ignore: + from copy import copy + lexer_conf = copy(lexer_conf) + lexer_conf.ignore = () + return BasicLexer(lexer_conf) + + def _prepare_callbacks(self) -> None: + self._callbacks = {} + ## + + if self.options.ambiguity != 'forest': + self._parse_tree_builder = ParseTreeBuilder( + self.rules, + self.options.tree_class or Tree, + self.options.propagate_positions, + self.options.parser != 'lalr' and self.options.ambiguity == 'explicit', + self.options.maybe_placeholders + ) + self._callbacks = self._parse_tree_builder.create_callback(self.options.transformer) + self._callbacks.update(_get_lexer_callbacks(self.options.transformer, self.terminals)) + + def _build_parser(self) -> "ParsingFrontend": + self._prepare_callbacks() + _validate_frontend_args(self.options.parser, self.options.lexer) + parser_conf = ParserConf(self.rules, self._callbacks, self.options.start) + return _construct_parsing_frontend( + self.options.parser, + self.options.lexer, + self.lexer_conf, + parser_conf, + options=self.options + ) + + def save(self, f, exclude_options: Collection[str] = ()) -> None: + #-- + if self.options.parser != 'lalr': + raise NotImplementedError("Lark.save() is only implemented for the LALR(1) parser.") + data, m = self.memo_serialize([TerminalDef, Rule]) + if exclude_options: + data["options"] = {n: v for n, v in data["options"].items() if n not in exclude_options} + pickle.dump({'data': data, 'memo': m}, f, protocol=pickle.HIGHEST_PROTOCOL) + + @classmethod + def load(cls: Type[_T], f) -> _T: + #-- + inst = cls.__new__(cls) + return inst._load(f) + + def _deserialize_lexer_conf(self, data: Dict[str, Any], memo: Dict[int, Union[TerminalDef, Rule]], options: LarkOptions) -> LexerConf: + lexer_conf = LexerConf.deserialize(data['lexer_conf'], memo) + lexer_conf.callbacks = options.lexer_callbacks or {} + lexer_conf.re_module = regex if options.regex else re + lexer_conf.use_bytes = options.use_bytes + lexer_conf.g_regex_flags = options.g_regex_flags + lexer_conf.skip_validation = True + lexer_conf.postlex = options.postlex + return lexer_conf + + def _load(self: _T, f: Any, **kwargs) -> _T: + if isinstance(f, dict): + d = f + else: + d = pickle.load(f) + memo_json = d['memo'] + data = d['data'] + + assert memo_json + memo = SerializeMemoizer.deserialize(memo_json, {'Rule': Rule, 'TerminalDef': TerminalDef}, {}) + if 'grammar' in data: + self.grammar = Grammar.deserialize(data['grammar'], memo) + options = dict(data['options']) + if (set(kwargs) - _LOAD_ALLOWED_OPTIONS) & set(LarkOptions._defaults): + raise ConfigurationError("Some options are not allowed when loading a Parser: {}" + .format(set(kwargs) - _LOAD_ALLOWED_OPTIONS)) + options.update(kwargs) + self.options = LarkOptions.deserialize(options, memo) + self.rules = [Rule.deserialize(r, memo) for r in data['rules']] + self.source_path = '' + _validate_frontend_args(self.options.parser, self.options.lexer) + self.lexer_conf = self._deserialize_lexer_conf(data['parser'], memo, self.options) + self.terminals = self.lexer_conf.terminals + self._prepare_callbacks() + self._terminals_dict = {t.name: t for t in self.terminals} + self.parser = _deserialize_parsing_frontend( + data['parser'], + memo, + self.lexer_conf, + self._callbacks, + self.options, ## + + ) + return self + + @classmethod + def _load_from_dict(cls, data, memo, **kwargs): + inst = cls.__new__(cls) + return inst._load({'data': data, 'memo': memo}, **kwargs) + + @classmethod + def open(cls: Type[_T], grammar_filename: str, rel_to: Optional[str]=None, **options) -> _T: + #-- + if rel_to: + basepath = os.path.dirname(rel_to) + grammar_filename = os.path.join(basepath, grammar_filename) + with open(grammar_filename, encoding='utf8') as f: + return cls(f, **options) + + @classmethod + def open_from_package(cls: Type[_T], package: str, grammar_path: str, search_paths: 'Sequence[str]'=[""], **options) -> _T: + #-- + package_loader = FromPackageLoader(package, search_paths) + full_path, text = package_loader(None, grammar_path) + options.setdefault('source_path', full_path) + options.setdefault('import_paths', []) + options['import_paths'].append(package_loader) + return cls(text, **options) + + def __repr__(self): + return 'Lark(open(%r), parser=%r, lexer=%r, ...)' % (self.source_path, self.options.parser, self.options.lexer) + + + def lex(self, text: TextOrSlice, dont_ignore: bool=False) -> Iterator[Token]: + #-- + lexer: Lexer + if not hasattr(self, 'lexer') or dont_ignore: + lexer = self._build_lexer(dont_ignore) + else: + lexer = self.lexer + lexer_thread = LexerThread.from_text(lexer, text) + stream = lexer_thread.lex(None) + if self.options.postlex: + return self.options.postlex.process(stream) + return stream + + def get_terminal(self, name: str) -> TerminalDef: + #-- + return self._terminals_dict[name] + + def parse_interactive(self, text: Optional[LarkInput]=None, start: Optional[str]=None) -> 'InteractiveParser': + #-- + return self.parser.parse_interactive(text, start=start) + + def parse(self, text: LarkInput, start: Optional[str]=None, on_error: 'Optional[Callable[[UnexpectedInput], bool]]'=None) -> 'ParseTree': + #-- + if on_error is not None and self.options.parser != 'lalr': + raise NotImplementedError("The on_error option is only implemented for the LALR(1) parser.") + return self.parser.parse(text, start=start, on_error=on_error) + + + + +class DedentError(LarkError): + pass + +class Indenter(PostLex, ABC): + #-- + paren_level: int + indent_level: List[int] + + def __init__(self) -> None: + self.paren_level = 0 + self.indent_level = [0] + assert self.tab_len > 0 + + def handle_NL(self, token: Token) -> Iterator[Token]: + if self.paren_level > 0: + return + + yield token + + indent_str = token.rsplit('\n', 1)[1] ## + + indent = indent_str.count(' ') + indent_str.count('\t') * self.tab_len + + if indent > self.indent_level[-1]: + self.indent_level.append(indent) + yield Token.new_borrow_pos(self.INDENT_type, indent_str, token) + else: + while indent < self.indent_level[-1]: + self.indent_level.pop() + yield Token.new_borrow_pos(self.DEDENT_type, indent_str, token) + + if indent != self.indent_level[-1]: + raise DedentError('Unexpected dedent to column %s. Expected dedent to %s' % (indent, self.indent_level[-1])) + + def _process(self, stream): + token = None + for token in stream: + if token.type == self.NL_type: + yield from self.handle_NL(token) + else: + yield token + + if token.type in self.OPEN_PAREN_types: + self.paren_level += 1 + elif token.type in self.CLOSE_PAREN_types: + self.paren_level -= 1 + assert self.paren_level >= 0 + + while len(self.indent_level) > 1: + self.indent_level.pop() + yield Token.new_borrow_pos(self.DEDENT_type, '', token) if token else Token(self.DEDENT_type, '', 0, 0, 0, 0, 0, 0) + + assert self.indent_level == [0], self.indent_level + + def process(self, stream): + self.paren_level = 0 + self.indent_level = [0] + return self._process(stream) + + ## + + @property + def always_accept(self): + return (self.NL_type,) + + @property + @abstractmethod + def NL_type(self) -> str: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def OPEN_PAREN_types(self) -> List[str]: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def CLOSE_PAREN_types(self) -> List[str]: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def INDENT_type(self) -> str: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def DEDENT_type(self) -> str: + #-- + raise NotImplementedError() + + @property + @abstractmethod + def tab_len(self) -> int: + #-- + raise NotImplementedError() + + +class PythonIndenter(Indenter): + #-- + + NL_type = '_NEWLINE' + OPEN_PAREN_types = ['LPAR', 'LSQB', 'LBRACE'] + CLOSE_PAREN_types = ['RPAR', 'RSQB', 'RBRACE'] + INDENT_type = '_INDENT' + DEDENT_type = '_DEDENT' + tab_len = 8 + + +import pickle, zlib, base64 +DATA = ( +{'parser': {'lexer_conf': {'terminals': [], 'ignore': [], 'g_regex_flags': 0, 'use_bytes': False, 'lexer_type': 'contextual', '__type__': 'LexerConf'}, 'parser_conf': {'rules': [{'@': 0}, {'@': 1}, {'@': 2}, {'@': 3}, {'@': 4}, {'@': 5}, {'@': 6}, {'@': 7}, {'@': 8}, {'@': 9}, {'@': 10}, {'@': 11}, {'@': 12}, {'@': 13}, {'@': 14}, {'@': 15}, {'@': 16}, {'@': 17}, {'@': 18}, {'@': 19}, {'@': 20}, {'@': 21}, {'@': 22}, {'@': 23}, {'@': 24}, {'@': 25}, {'@': 26}, {'@': 27}, {'@': 28}, {'@': 29}, {'@': 30}, {'@': 31}, {'@': 32}, {'@': 33}, {'@': 34}, {'@': 35}, {'@': 36}, {'@': 37}, {'@': 38}, {'@': 39}, {'@': 40}, {'@': 41}, {'@': 42}, {'@': 43}, {'@': 44}, {'@': 45}, {'@': 46}, {'@': 47}, {'@': 48}, {'@': 49}, {'@': 50}, {'@': 51}, {'@': 52}, {'@': 53}, {'@': 54}, {'@': 55}, {'@': 56}, {'@': 57}, {'@': 58}, {'@': 59}, {'@': 60}, {'@': 61}, {'@': 62}, {'@': 63}, {'@': 64}, {'@': 65}, {'@': 66}, {'@': 67}, {'@': 68}, {'@': 69}, {'@': 70}, {'@': 71}, {'@': 72}, {'@': 73}, {'@': 74}, {'@': 75}, {'@': 76}, {'@': 77}, {'@': 78}, {'@': 79}, {'@': 80}, {'@': 81}, {'@': 82}, {'@': 83}, {'@': 84}, {'@': 85}, {'@': 86}, {'@': 87}, {'@': 88}, {'@': 89}, {'@': 90}, {'@': 91}, {'@': 92}, {'@': 93}, {'@': 94}, {'@': 95}, {'@': 96}, {'@': 97}, {'@': 98}, {'@': 99}, {'@': 100}, {'@': 101}, {'@': 102}, {'@': 103}, {'@': 104}, {'@': 105}, {'@': 106}], 'start': ['start'], 'parser_type': 'lalr', '__type__': 'ParserConf'}, 'parser': {'tokens': {0: 'NUMBER', 1: 'TOKEN', 2: 'LLP', 3: 'ID', 4: 'COMMA', 5: 'RRP', 6: 'STRING', 7: 'NEWLINE', 8: 'STRINGIZING', 9: 'CONTINUE', 10: 'SEPARATOR', 11: 'args', 12: 'LT', 13: 'OR', 14: 'GE', 15: 'EQ', 16: 'AND', 17: 'GT', 18: 'LE', 19: 'NE', 20: 'ENDIF', 21: 'IFDEF', 22: 'ELSE', 23: 'WARNING', 24: 'PRAGMA', 25: 'INIT', 26: 'LINE', 27: 'ERROR', 28: 'IF', 29: 'DEFINE', 30: 'REQUIRE', 31: 'UNDEF', 32: 'INCLUDE', 33: 'IFNDEF', 34: 'ENDFILE', 35: '$END', 36: 'errormsg', 37: 'if_header', 38: 'defs', 39: 'define', 40: 'require', 41: 'include_file', 42: 'ifdef', 43: 'include', 44: 'pragma', 45: 'init', 46: 'line', 47: 'warningmsg', 48: 'include_once', 49: 'undef', 50: 'ifdefelsea', 51: 'PASTE', 52: 'CO', 53: 'macrocall', 54: 'macrocall_paste', 55: 'macrocall_call', 56: 'macrocall_unary', 57: 'token', 58: 'TEXT', 59: 'RP', 60: 'def_item', 61: 'expr_atom', 62: 'include_modifier', 63: 'LB', 64: 'ONCE', 65: 'FILENAME', 66: 'PUSH', 67: 'POP', 68: 'program', 69: 'LP', 70: 'expr_or', 71: 'expr_cmp', 72: 'expr_and', 73: 'expr', 74: 'argstring', 75: 'arg', 76: 'INTEGER', 77: 'arglist', 78: 'ifdefelseb', 79: 'start', 80: 'RB', 81: 'params', 82: 'paramlist'}, 'states': {0: {0: (1, {'@': 85}), 1: (1, {'@': 85}), 2: (1, {'@': 85}), 3: (1, {'@': 85}), 4: (1, {'@': 85}), 5: (1, {'@': 85}), 6: (1, {'@': 85}), 7: (1, {'@': 85}), 8: (1, {'@': 85}), 9: (1, {'@': 85}), 10: (1, {'@': 85})}, 1: {0: (1, {'@': 25}), 1: (1, {'@': 25}), 2: (1, {'@': 25}), 3: (1, {'@': 25}), 4: (1, {'@': 25}), 6: (1, {'@': 25}), 5: (1, {'@': 25}), 8: (1, {'@': 25}), 9: (1, {'@': 25}), 10: (1, {'@': 25}), 7: (1, {'@': 25})}, 2: {11: (0, 54), 2: (0, 107), 0: (1, {'@': 89}), 1: (1, {'@': 89}), 3: (1, {'@': 89}), 4: (1, {'@': 89}), 6: (1, {'@': 89}), 5: (1, {'@': 89}), 8: (1, {'@': 89}), 9: (1, {'@': 89}), 10: (1, {'@': 89}), 7: (1, {'@': 89}), 12: (1, {'@': 89}), 13: (1, {'@': 89}), 14: (1, {'@': 89}), 15: (1, {'@': 89}), 16: (1, {'@': 89}), 17: (1, {'@': 89}), 18: (1, {'@': 89}), 19: (1, {'@': 89})}, 3: {20: (1, {'@': 6}), 21: (1, {'@': 6}), 0: (1, {'@': 6}), 22: (1, {'@': 6}), 23: (1, {'@': 6}), 1: (1, {'@': 6}), 2: (1, {'@': 6}), 3: (1, {'@': 6}), 24: (1, {'@': 6}), 25: (1, {'@': 6}), 4: (1, {'@': 6}), 5: (1, {'@': 6}), 6: (1, {'@': 6}), 26: (1, {'@': 6}), 7: (1, {'@': 6}), 27: (1, {'@': 6}), 28: (1, {'@': 6}), 29: (1, {'@': 6}), 30: (1, {'@': 6}), 8: (1, {'@': 6}), 31: (1, {'@': 6}), 9: (1, {'@': 6}), 32: (1, {'@': 6}), 33: (1, {'@': 6}), 10: (1, {'@': 6}), 34: (1, {'@': 6}), 35: (1, {'@': 6})}, 4: {0: (1, {'@': 87}), 1: (1, {'@': 87}), 2: (1, {'@': 87}), 3: (1, {'@': 87}), 4: (1, {'@': 87}), 5: (1, {'@': 87}), 6: (1, {'@': 87}), 7: (1, {'@': 87}), 8: (1, {'@': 87}), 9: (1, {'@': 87}), 10: (1, {'@': 87})}, 5: {21: (0, 173), 36: (0, 157), 37: (0, 6), 31: (0, 170), 23: (0, 15), 38: (0, 135), 24: (0, 43), 32: (0, 38), 39: (0, 97), 40: (0, 99), 41: (0, 94), 42: (0, 109), 43: (0, 60), 44: (0, 112), 20: (0, 18), 25: (0, 93), 26: (0, 96), 45: (0, 114), 27: (0, 123), 46: (0, 103), 47: (0, 119), 48: (0, 105), 49: (0, 128), 29: (0, 106), 50: (0, 127), 33: (0, 155), 28: (0, 81), 30: (0, 12), 4: (1, {'@': 82}), 5: (1, {'@': 82}), 3: (1, {'@': 82}), 6: (1, {'@': 82}), 0: (1, {'@': 82}), 7: (1, {'@': 82}), 8: (1, {'@': 82}), 1: (1, {'@': 82}), 2: (1, {'@': 82}), 9: (1, {'@': 82}), 10: (1, {'@': 82}), 22: (1, {'@': 61})}, 6: {7: (0, 49)}, 7: {12: (1, {'@': 73}), 13: (1, {'@': 73}), 14: (1, {'@': 73}), 15: (1, {'@': 73}), 7: (1, {'@': 73}), 16: (1, {'@': 73}), 17: (1, {'@': 73}), 18: (1, {'@': 73}), 19: (1, {'@': 73}), 5: (1, {'@': 73})}, 8: {21: (0, 173), 36: (0, 157), 37: (0, 6), 31: (0, 170), 23: (0, 15), 38: (0, 135), 24: (0, 43), 39: (0, 97), 40: (0, 99), 32: (0, 38), 41: (0, 94), 42: (0, 109), 43: (0, 60), 44: (0, 112), 25: (0, 93), 26: (0, 96), 45: (0, 114), 27: (0, 123), 46: (0, 103), 47: (0, 119), 48: (0, 105), 49: (0, 128), 29: (0, 106), 50: (0, 127), 33: (0, 155), 28: (0, 81), 30: (0, 12), 4: (1, {'@': 82}), 5: (1, {'@': 82}), 3: (1, {'@': 82}), 6: (1, {'@': 82}), 0: (1, {'@': 82}), 7: (1, {'@': 82}), 8: (1, {'@': 82}), 1: (1, {'@': 82}), 2: (1, {'@': 82}), 9: (1, {'@': 82}), 10: (1, {'@': 82}), 35: (1, {'@': 0})}, 9: {0: (1, {'@': 95}), 4: (1, {'@': 95}), 6: (1, {'@': 95}), 5: (1, {'@': 95}), 3: (1, {'@': 95}), 10: (1, {'@': 95}), 8: (1, {'@': 95}), 1: (1, {'@': 95}), 2: (1, {'@': 95}), 9: (1, {'@': 95}), 51: (1, {'@': 95}), 7: (1, {'@': 95}), 12: (1, {'@': 95}), 13: (1, {'@': 95}), 14: (1, {'@': 95}), 15: (1, {'@': 95}), 16: (1, {'@': 95}), 17: (1, {'@': 95}), 18: (1, {'@': 95}), 19: (1, {'@': 95})}, 10: {20: (1, {'@': 40}), 21: (1, {'@': 40}), 0: (1, {'@': 40}), 22: (1, {'@': 40}), 23: (1, {'@': 40}), 1: (1, {'@': 40}), 2: (1, {'@': 40}), 3: (1, {'@': 40}), 24: (1, {'@': 40}), 25: (1, {'@': 40}), 4: (1, {'@': 40}), 5: (1, {'@': 40}), 6: (1, {'@': 40}), 26: (1, {'@': 40}), 7: (1, {'@': 40}), 27: (1, {'@': 40}), 28: (1, {'@': 40}), 29: (1, {'@': 40}), 30: (1, {'@': 40}), 8: (1, {'@': 40}), 31: (1, {'@': 40}), 9: (1, {'@': 40}), 32: (1, {'@': 40}), 33: (1, {'@': 40}), 10: (1, {'@': 40}), 35: (1, {'@': 40}), 34: (1, {'@': 40})}, 11: {0: (1, {'@': 106}), 6: (1, {'@': 106}), 4: (1, {'@': 106}), 5: (1, {'@': 106}), 10: (1, {'@': 106}), 8: (1, {'@': 106}), 1: (1, {'@': 106}), 2: (1, {'@': 106}), 9: (1, {'@': 106}), 3: (1, {'@': 106})}, 12: {6: (0, 90)}, 13: {52: (0, 17)}, 14: {53: (0, 29), 54: (0, 33), 8: (0, 151), 55: (0, 2), 3: (0, 9), 2: (0, 141), 9: (0, 1), 0: (0, 41), 6: (0, 28), 10: (0, 160), 1: (0, 159), 56: (0, 31), 57: (0, 158), 4: (1, {'@': 100}), 5: (1, {'@': 100})}, 15: {58: (0, 125)}, 16: {20: (1, {'@': 54}), 21: (1, {'@': 54}), 0: (1, {'@': 54}), 22: (1, {'@': 54}), 23: (1, {'@': 54}), 1: (1, {'@': 54}), 2: (1, {'@': 54}), 3: (1, {'@': 54}), 24: (1, {'@': 54}), 25: (1, {'@': 54}), 4: (1, {'@': 54}), 5: (1, {'@': 54}), 6: (1, {'@': 54}), 26: (1, {'@': 54}), 7: (1, {'@': 54}), 27: (1, {'@': 54}), 28: (1, {'@': 54}), 29: (1, {'@': 54}), 30: (1, {'@': 54}), 8: (1, {'@': 54}), 31: (1, {'@': 54}), 9: (1, {'@': 54}), 32: (1, {'@': 54}), 33: (1, {'@': 54}), 10: (1, {'@': 54}), 35: (1, {'@': 54}), 34: (1, {'@': 54})}, 17: {3: (0, 138)}, 18: {20: (1, {'@': 59}), 21: (1, {'@': 59}), 0: (1, {'@': 59}), 22: (1, {'@': 59}), 23: (1, {'@': 59}), 1: (1, {'@': 59}), 2: (1, {'@': 59}), 3: (1, {'@': 59}), 24: (1, {'@': 59}), 25: (1, {'@': 59}), 4: (1, {'@': 59}), 5: (1, {'@': 59}), 6: (1, {'@': 59}), 26: (1, {'@': 59}), 7: (1, {'@': 59}), 27: (1, {'@': 59}), 28: (1, {'@': 59}), 29: (1, {'@': 59}), 30: (1, {'@': 59}), 8: (1, {'@': 59}), 31: (1, {'@': 59}), 9: (1, {'@': 59}), 32: (1, {'@': 59}), 33: (1, {'@': 59}), 10: (1, {'@': 59}), 35: (1, {'@': 59}), 34: (1, {'@': 59})}, 19: {59: (0, 167)}, 20: {7: (0, 142), 8: (0, 151), 4: (0, 0), 9: (0, 1), 55: (0, 2), 10: (0, 160), 1: (0, 159), 5: (0, 148), 2: (0, 4), 3: (0, 9), 54: (0, 33), 6: (0, 28), 60: (0, 35), 56: (0, 31), 57: (0, 27), 0: (0, 41), 53: (0, 47)}, 21: {0: (0, 108), 54: (0, 33), 8: (0, 151), 55: (0, 2), 3: (0, 9), 53: (0, 116), 61: (0, 82), 6: (0, 124), 2: (0, 129), 56: (0, 31)}, 22: {38: (0, 144), 4: (1, {'@': 82}), 5: (1, {'@': 82}), 3: (1, {'@': 82}), 6: (1, {'@': 82}), 0: (1, {'@': 82}), 7: (1, {'@': 82}), 8: (1, {'@': 82}), 1: (1, {'@': 82}), 2: (1, {'@': 82}), 9: (1, {'@': 82}), 10: (1, {'@': 82})}, 23: {12: (1, {'@': 71}), 13: (1, {'@': 71}), 14: (1, {'@': 71}), 15: (1, {'@': 71}), 7: (1, {'@': 71}), 16: (1, {'@': 71}), 17: (1, {'@': 71}), 18: (1, {'@': 71}), 19: (1, {'@': 71}), 5: (1, {'@': 71})}, 24: {20: (1, {'@': 41}), 21: (1, {'@': 41}), 0: (1, {'@': 41}), 22: (1, {'@': 41}), 23: (1, {'@': 41}), 1: (1, {'@': 41}), 2: (1, {'@': 41}), 3: (1, {'@': 41}), 24: (1, {'@': 41}), 25: (1, {'@': 41}), 4: (1, {'@': 41}), 5: (1, {'@': 41}), 6: (1, {'@': 41}), 26: (1, {'@': 41}), 7: (1, {'@': 41}), 27: (1, {'@': 41}), 28: (1, {'@': 41}), 29: (1, {'@': 41}), 30: (1, {'@': 41}), 8: (1, {'@': 41}), 31: (1, {'@': 41}), 9: (1, {'@': 41}), 32: (1, {'@': 41}), 33: (1, {'@': 41}), 10: (1, {'@': 41}), 35: (1, {'@': 41}), 34: (1, {'@': 41})}, 25: {0: (0, 108), 54: (0, 33), 8: (0, 151), 55: (0, 2), 3: (0, 9), 53: (0, 116), 61: (0, 45), 6: (0, 124), 2: (0, 129), 56: (0, 31)}, 26: {14: (1, {'@': 81}), 16: (1, {'@': 81}), 18: (1, {'@': 81}), 12: (1, {'@': 81}), 13: (1, {'@': 81}), 5: (1, {'@': 81}), 15: (1, {'@': 81}), 7: (1, {'@': 81}), 17: (1, {'@': 81}), 19: (1, {'@': 81})}, 27: {0: (1, {'@': 84}), 1: (1, {'@': 84}), 2: (1, {'@': 84}), 3: (1, {'@': 84}), 4: (1, {'@': 84}), 5: (1, {'@': 84}), 6: (1, {'@': 84}), 7: (1, {'@': 84}), 8: (1, {'@': 84}), 9: (1, {'@': 84}), 10: (1, {'@': 84})}, 28: {0: (1, {'@': 23}), 1: (1, {'@': 23}), 2: (1, {'@': 23}), 3: (1, {'@': 23}), 4: (1, {'@': 23}), 6: (1, {'@': 23}), 5: (1, {'@': 23}), 8: (1, {'@': 23}), 9: (1, {'@': 23}), 10: (1, {'@': 23}), 7: (1, {'@': 23})}, 29: {0: (1, {'@': 105}), 6: (1, {'@': 105}), 4: (1, {'@': 105}), 5: (1, {'@': 105}), 10: (1, {'@': 105}), 8: (1, {'@': 105}), 1: (1, {'@': 105}), 2: (1, {'@': 105}), 9: (1, {'@': 105}), 3: (1, {'@': 105})}, 30: {4: (1, {'@': 49}), 5: (1, {'@': 49}), 3: (1, {'@': 49}), 6: (1, {'@': 49}), 0: (1, {'@': 49}), 7: (1, {'@': 49}), 8: (1, {'@': 49}), 1: (1, {'@': 49}), 2: (1, {'@': 49}), 9: (1, {'@': 49}), 10: (1, {'@': 49})}, 31: {0: (1, {'@': 93}), 4: (1, {'@': 93}), 6: (1, {'@': 93}), 5: (1, {'@': 93}), 3: (1, {'@': 93}), 10: (1, {'@': 93}), 8: (1, {'@': 93}), 1: (1, {'@': 93}), 2: (1, {'@': 93}), 9: (1, {'@': 93}), 51: (1, {'@': 93}), 7: (1, {'@': 93}), 12: (1, {'@': 93}), 13: (1, {'@': 93}), 14: (1, {'@': 93}), 15: (1, {'@': 93}), 16: (1, {'@': 93}), 17: (1, {'@': 93}), 18: (1, {'@': 93}), 19: (1, {'@': 93})}, 32: {0: (0, 108), 54: (0, 33), 8: (0, 151), 55: (0, 2), 3: (0, 9), 53: (0, 116), 61: (0, 7), 6: (0, 124), 2: (0, 129), 56: (0, 31)}, 33: {51: (0, 59), 0: (1, {'@': 91}), 4: (1, {'@': 91}), 6: (1, {'@': 91}), 5: (1, {'@': 91}), 10: (1, {'@': 91}), 8: (1, {'@': 91}), 1: (1, {'@': 91}), 2: (1, {'@': 91}), 9: (1, {'@': 91}), 3: (1, {'@': 91}), 7: (1, {'@': 91}), 12: (1, {'@': 91}), 13: (1, {'@': 91}), 14: (1, {'@': 91}), 15: (1, {'@': 91}), 16: (1, {'@': 91}), 17: (1, {'@': 91}), 18: (1, {'@': 91}), 19: (1, {'@': 91})}, 34: {20: (1, {'@': 56}), 21: (1, {'@': 56}), 0: (1, {'@': 56}), 22: (1, {'@': 56}), 23: (1, {'@': 56}), 1: (1, {'@': 56}), 2: (1, {'@': 56}), 3: (1, {'@': 56}), 24: (1, {'@': 56}), 25: (1, {'@': 56}), 4: (1, {'@': 56}), 5: (1, {'@': 56}), 6: (1, {'@': 56}), 26: (1, {'@': 56}), 7: (1, {'@': 56}), 27: (1, {'@': 56}), 28: (1, {'@': 56}), 29: (1, {'@': 56}), 30: (1, {'@': 56}), 8: (1, {'@': 56}), 31: (1, {'@': 56}), 9: (1, {'@': 56}), 32: (1, {'@': 56}), 33: (1, {'@': 56}), 10: (1, {'@': 56}), 35: (1, {'@': 56}), 34: (1, {'@': 56})}, 35: {4: (1, {'@': 83}), 5: (1, {'@': 83}), 3: (1, {'@': 83}), 6: (1, {'@': 83}), 0: (1, {'@': 83}), 7: (1, {'@': 83}), 8: (1, {'@': 83}), 1: (1, {'@': 83}), 2: (1, {'@': 83}), 9: (1, {'@': 83}), 10: (1, {'@': 83})}, 36: {0: (0, 108), 54: (0, 33), 8: (0, 151), 55: (0, 2), 3: (0, 9), 53: (0, 116), 6: (0, 124), 2: (0, 129), 61: (0, 46), 56: (0, 31)}, 37: {4: (0, 70), 5: (0, 11)}, 38: {62: (0, 53), 63: (0, 64), 64: (0, 66), 0: (1, {'@': 36}), 6: (1, {'@': 36}), 65: (1, {'@': 36}), 8: (1, {'@': 36}), 2: (1, {'@': 36}), 3: (1, {'@': 36})}, 39: {6: (0, 104), 65: (0, 86)}, 40: {4: (0, 70), 5: (0, 57)}, 41: {0: (1, {'@': 27}), 1: (1, {'@': 27}), 2: (1, {'@': 27}), 3: (1, {'@': 27}), 4: (1, {'@': 27}), 6: (1, {'@': 27}), 5: (1, {'@': 27}), 8: (1, {'@': 27}), 9: (1, {'@': 27}), 10: (1, {'@': 27}), 7: (1, {'@': 27})}, 42: {20: (1, {'@': 29}), 21: (1, {'@': 29}), 0: (1, {'@': 29}), 22: (1, {'@': 29}), 23: (1, {'@': 29}), 1: (1, {'@': 29}), 2: (1, {'@': 29}), 3: (1, {'@': 29}), 24: (1, {'@': 29}), 25: (1, {'@': 29}), 4: (1, {'@': 29}), 5: (1, {'@': 29}), 6: (1, {'@': 29}), 26: (1, {'@': 29}), 7: (1, {'@': 29}), 27: (1, {'@': 29}), 28: (1, {'@': 29}), 29: (1, {'@': 29}), 30: (1, {'@': 29}), 8: (1, {'@': 29}), 31: (1, {'@': 29}), 9: (1, {'@': 29}), 32: (1, {'@': 29}), 33: (1, {'@': 29}), 10: (1, {'@': 29}), 35: (1, {'@': 29}), 34: (1, {'@': 29})}, 43: {3: (0, 48), 66: (0, 51), 67: (0, 74), 64: (0, 76)}, 44: {0: (1, {'@': 92}), 4: (1, {'@': 92}), 6: (1, {'@': 92}), 5: (1, {'@': 92}), 3: (1, {'@': 92}), 10: (1, {'@': 92}), 8: (1, {'@': 92}), 1: (1, {'@': 92}), 2: (1, {'@': 92}), 9: (1, {'@': 92}), 51: (1, {'@': 92}), 7: (1, {'@': 92}), 12: (1, {'@': 92}), 13: (1, {'@': 92}), 14: (1, {'@': 92}), 15: (1, {'@': 92}), 16: (1, {'@': 92}), 17: (1, {'@': 92}), 18: (1, {'@': 92}), 19: (1, {'@': 92})}, 45: {12: (1, {'@': 72}), 13: (1, {'@': 72}), 14: (1, {'@': 72}), 15: (1, {'@': 72}), 7: (1, {'@': 72}), 16: (1, {'@': 72}), 17: (1, {'@': 72}), 18: (1, {'@': 72}), 19: (1, {'@': 72}), 5: (1, {'@': 72})}, 46: {12: (1, {'@': 76}), 13: (1, {'@': 76}), 14: (1, {'@': 76}), 15: (1, {'@': 76}), 7: (1, {'@': 76}), 16: (1, {'@': 76}), 17: (1, {'@': 76}), 18: (1, {'@': 76}), 19: (1, {'@': 76}), 5: (1, {'@': 76})}, 47: {0: (1, {'@': 88}), 1: (1, {'@': 88}), 2: (1, {'@': 88}), 3: (1, {'@': 88}), 4: (1, {'@': 88}), 5: (1, {'@': 88}), 6: (1, {'@': 88}), 7: (1, {'@': 88}), 8: (1, {'@': 88}), 9: (1, {'@': 88}), 10: (1, {'@': 88})}, 48: {15: (0, 102), 20: (1, {'@': 52}), 21: (1, {'@': 52}), 0: (1, {'@': 52}), 22: (1, {'@': 52}), 23: (1, {'@': 52}), 1: (1, {'@': 52}), 2: (1, {'@': 52}), 3: (1, {'@': 52}), 24: (1, {'@': 52}), 25: (1, {'@': 52}), 4: (1, {'@': 52}), 5: (1, {'@': 52}), 6: (1, {'@': 52}), 26: (1, {'@': 52}), 7: (1, {'@': 52}), 27: (1, {'@': 52}), 28: (1, {'@': 52}), 29: (1, {'@': 52}), 30: (1, {'@': 52}), 8: (1, {'@': 52}), 31: (1, {'@': 52}), 9: (1, {'@': 52}), 32: (1, {'@': 52}), 33: (1, {'@': 52}), 10: (1, {'@': 52}), 35: (1, {'@': 52}), 34: (1, {'@': 52})}, 49: {68: (0, 5), 31: (0, 170), 21: (0, 173), 37: (0, 6), 40: (0, 3), 30: (0, 12), 23: (0, 15), 38: (0, 20), 24: (0, 43), 32: (0, 38), 45: (0, 55), 42: (0, 58), 43: (0, 60), 28: (0, 81), 39: (0, 80), 44: (0, 101), 25: (0, 93), 26: (0, 96), 49: (0, 84), 46: (0, 121), 27: (0, 123), 48: (0, 105), 29: (0, 106), 36: (0, 117), 47: (0, 118), 50: (0, 127), 33: (0, 155), 41: (0, 145), 4: (1, {'@': 82}), 5: (1, {'@': 82}), 3: (1, {'@': 82}), 6: (1, {'@': 82}), 0: (1, {'@': 82}), 7: (1, {'@': 82}), 8: (1, {'@': 82}), 1: (1, {'@': 82}), 2: (1, {'@': 82}), 9: (1, {'@': 82}), 10: (1, {'@': 82})}, 50: {59: (1, {'@': 50}), 4: (1, {'@': 50})}, 51: {69: (0, 79)}, 52: {4: (1, {'@': 97}), 5: (1, {'@': 97})}, 53: {70: (0, 113), 71: (0, 83), 65: (0, 162), 8: (0, 151), 55: (0, 2), 72: (0, 115), 73: (0, 120), 3: (0, 9), 53: (0, 116), 54: (0, 33), 6: (0, 124), 2: (0, 129), 56: (0, 31), 0: (0, 108), 61: (0, 130)}, 54: {0: (1, {'@': 90}), 4: (1, {'@': 90}), 6: (1, {'@': 90}), 5: (1, {'@': 90}), 10: (1, {'@': 90}), 8: (1, {'@': 90}), 1: (1, {'@': 90}), 2: (1, {'@': 90}), 9: (1, {'@': 90}), 3: (1, {'@': 90}), 7: (1, {'@': 90}), 12: (1, {'@': 90}), 13: (1, {'@': 90}), 14: (1, {'@': 90}), 15: (1, {'@': 90}), 16: (1, {'@': 90}), 17: (1, {'@': 90}), 18: (1, {'@': 90}), 19: (1, {'@': 90})}, 55: {20: (1, {'@': 3}), 21: (1, {'@': 3}), 0: (1, {'@': 3}), 22: (1, {'@': 3}), 23: (1, {'@': 3}), 1: (1, {'@': 3}), 2: (1, {'@': 3}), 3: (1, {'@': 3}), 24: (1, {'@': 3}), 25: (1, {'@': 3}), 4: (1, {'@': 3}), 5: (1, {'@': 3}), 6: (1, {'@': 3}), 26: (1, {'@': 3}), 7: (1, {'@': 3}), 27: (1, {'@': 3}), 28: (1, {'@': 3}), 29: (1, {'@': 3}), 30: (1, {'@': 3}), 8: (1, {'@': 3}), 31: (1, {'@': 3}), 9: (1, {'@': 3}), 32: (1, {'@': 3}), 33: (1, {'@': 3}), 10: (1, {'@': 3}), 34: (1, {'@': 3}), 35: (1, {'@': 3})}, 56: {3: (0, 19)}, 57: {0: (1, {'@': 103}), 6: (1, {'@': 103}), 4: (1, {'@': 103}), 5: (1, {'@': 103}), 10: (1, {'@': 103}), 8: (1, {'@': 103}), 1: (1, {'@': 103}), 2: (1, {'@': 103}), 9: (1, {'@': 103}), 3: (1, {'@': 103})}, 58: {20: (1, {'@': 5}), 21: (1, {'@': 5}), 0: (1, {'@': 5}), 22: (1, {'@': 5}), 23: (1, {'@': 5}), 1: (1, {'@': 5}), 2: (1, {'@': 5}), 3: (1, {'@': 5}), 24: (1, {'@': 5}), 25: (1, {'@': 5}), 4: (1, {'@': 5}), 5: (1, {'@': 5}), 6: (1, {'@': 5}), 26: (1, {'@': 5}), 7: (1, {'@': 5}), 27: (1, {'@': 5}), 28: (1, {'@': 5}), 29: (1, {'@': 5}), 30: (1, {'@': 5}), 8: (1, {'@': 5}), 31: (1, {'@': 5}), 9: (1, {'@': 5}), 32: (1, {'@': 5}), 33: (1, {'@': 5}), 10: (1, {'@': 5}), 34: (1, {'@': 5}), 35: (1, {'@': 5})}, 59: {56: (0, 44), 8: (0, 151), 3: (0, 9)}, 60: {7: (0, 133)}, 61: {21: (0, 173), 36: (0, 157), 37: (0, 6), 31: (0, 170), 23: (0, 15), 38: (0, 135), 24: (0, 43), 32: (0, 38), 39: (0, 97), 40: (0, 99), 41: (0, 94), 42: (0, 109), 43: (0, 60), 44: (0, 112), 25: (0, 93), 26: (0, 96), 45: (0, 114), 27: (0, 123), 46: (0, 103), 47: (0, 119), 48: (0, 105), 49: (0, 128), 34: (0, 168), 29: (0, 106), 50: (0, 127), 33: (0, 155), 28: (0, 81), 30: (0, 12), 4: (1, {'@': 82}), 5: (1, {'@': 82}), 3: (1, {'@': 82}), 6: (1, {'@': 82}), 0: (1, {'@': 82}), 7: (1, {'@': 82}), 8: (1, {'@': 82}), 1: (1, {'@': 82}), 2: (1, {'@': 82}), 9: (1, {'@': 82}), 10: (1, {'@': 82})}, 62: {59: (1, {'@': 51}), 4: (1, {'@': 51})}, 63: {3: (0, 62)}, 64: {3: (0, 13)}, 65: {59: (0, 34)}, 66: {62: (0, 39), 63: (0, 64), 65: (1, {'@': 36}), 6: (1, {'@': 36})}, 67: {5: (0, 26)}, 68: {20: (1, {'@': 55}), 21: (1, {'@': 55}), 0: (1, {'@': 55}), 22: (1, {'@': 55}), 23: (1, {'@': 55}), 1: (1, {'@': 55}), 2: (1, {'@': 55}), 3: (1, {'@': 55}), 24: (1, {'@': 55}), 25: (1, {'@': 55}), 4: (1, {'@': 55}), 5: (1, {'@': 55}), 6: (1, {'@': 55}), 26: (1, {'@': 55}), 7: (1, {'@': 55}), 27: (1, {'@': 55}), 28: (1, {'@': 55}), 29: (1, {'@': 55}), 30: (1, {'@': 55}), 8: (1, {'@': 55}), 31: (1, {'@': 55}), 9: (1, {'@': 55}), 32: (1, {'@': 55}), 33: (1, {'@': 55}), 10: (1, {'@': 55}), 35: (1, {'@': 55}), 34: (1, {'@': 55})}, 69: {20: (1, {'@': 60}), 21: (1, {'@': 60}), 0: (1, {'@': 60}), 22: (1, {'@': 60}), 23: (1, {'@': 60}), 1: (1, {'@': 60}), 2: (1, {'@': 60}), 3: (1, {'@': 60}), 24: (1, {'@': 60}), 25: (1, {'@': 60}), 4: (1, {'@': 60}), 5: (1, {'@': 60}), 6: (1, {'@': 60}), 26: (1, {'@': 60}), 7: (1, {'@': 60}), 27: (1, {'@': 60}), 28: (1, {'@': 60}), 29: (1, {'@': 60}), 30: (1, {'@': 60}), 8: (1, {'@': 60}), 31: (1, {'@': 60}), 9: (1, {'@': 60}), 32: (1, {'@': 60}), 33: (1, {'@': 60}), 10: (1, {'@': 60}), 35: (1, {'@': 60}), 34: (1, {'@': 60})}, 70: {74: (0, 14), 8: (0, 151), 53: (0, 154), 9: (0, 1), 55: (0, 2), 75: (0, 52), 10: (0, 160), 2: (0, 143), 1: (0, 159), 57: (0, 95), 3: (0, 9), 54: (0, 33), 6: (0, 28), 56: (0, 31), 0: (0, 41), 4: (1, {'@': 99}), 5: (1, {'@': 99})}, 71: {20: (1, {'@': 42}), 21: (1, {'@': 42}), 0: (1, {'@': 42}), 22: (1, {'@': 42}), 23: (1, {'@': 42}), 1: (1, {'@': 42}), 2: (1, {'@': 42}), 3: (1, {'@': 42}), 24: (1, {'@': 42}), 25: (1, {'@': 42}), 4: (1, {'@': 42}), 5: (1, {'@': 42}), 6: (1, {'@': 42}), 26: (1, {'@': 42}), 7: (1, {'@': 42}), 27: (1, {'@': 42}), 28: (1, {'@': 42}), 29: (1, {'@': 42}), 30: (1, {'@': 42}), 8: (1, {'@': 42}), 31: (1, {'@': 42}), 9: (1, {'@': 42}), 32: (1, {'@': 42}), 33: (1, {'@': 42}), 10: (1, {'@': 42}), 35: (1, {'@': 42}), 34: (1, {'@': 42})}, 72: {0: (1, {'@': 96}), 14: (1, {'@': 96}), 16: (1, {'@': 96}), 1: (1, {'@': 96}), 18: (1, {'@': 96}), 2: (1, {'@': 96}), 3: (1, {'@': 96}), 12: (1, {'@': 96}), 13: (1, {'@': 96}), 5: (1, {'@': 96}), 4: (1, {'@': 96}), 15: (1, {'@': 96}), 6: (1, {'@': 96}), 7: (1, {'@': 96}), 8: (1, {'@': 96}), 17: (1, {'@': 96}), 19: (1, {'@': 96}), 9: (1, {'@': 96}), 10: (1, {'@': 96})}, 73: {12: (1, {'@': 75}), 13: (1, {'@': 75}), 14: (1, {'@': 75}), 15: (1, {'@': 75}), 7: (1, {'@': 75}), 16: (1, {'@': 75}), 17: (1, {'@': 75}), 18: (1, {'@': 75}), 19: (1, {'@': 75}), 5: (1, {'@': 75})}, 74: {69: (0, 56)}, 75: {16: (0, 174), 13: (1, {'@': 67}), 7: (1, {'@': 67}), 5: (1, {'@': 67})}, 76: {20: (1, {'@': 58}), 21: (1, {'@': 58}), 0: (1, {'@': 58}), 22: (1, {'@': 58}), 23: (1, {'@': 58}), 1: (1, {'@': 58}), 2: (1, {'@': 58}), 3: (1, {'@': 58}), 24: (1, {'@': 58}), 25: (1, {'@': 58}), 4: (1, {'@': 58}), 5: (1, {'@': 58}), 6: (1, {'@': 58}), 26: (1, {'@': 58}), 7: (1, {'@': 58}), 27: (1, {'@': 58}), 28: (1, {'@': 58}), 29: (1, {'@': 58}), 30: (1, {'@': 58}), 8: (1, {'@': 58}), 31: (1, {'@': 58}), 9: (1, {'@': 58}), 32: (1, {'@': 58}), 33: (1, {'@': 58}), 10: (1, {'@': 58}), 35: (1, {'@': 58}), 34: (1, {'@': 58})}, 77: {0: (1, {'@': 94}), 4: (1, {'@': 94}), 6: (1, {'@': 94}), 5: (1, {'@': 94}), 3: (1, {'@': 94}), 10: (1, {'@': 94}), 8: (1, {'@': 94}), 1: (1, {'@': 94}), 2: (1, {'@': 94}), 9: (1, {'@': 94}), 51: (1, {'@': 94}), 7: (1, {'@': 94}), 12: (1, {'@': 94}), 13: (1, {'@': 94}), 14: (1, {'@': 94}), 15: (1, {'@': 94}), 16: (1, {'@': 94}), 17: (1, {'@': 94}), 18: (1, {'@': 94}), 19: (1, {'@': 94})}, 78: {20: (1, {'@': 39}), 21: (1, {'@': 39}), 0: (1, {'@': 39}), 22: (1, {'@': 39}), 23: (1, {'@': 39}), 1: (1, {'@': 39}), 2: (1, {'@': 39}), 3: (1, {'@': 39}), 24: (1, {'@': 39}), 25: (1, {'@': 39}), 4: (1, {'@': 39}), 5: (1, {'@': 39}), 6: (1, {'@': 39}), 26: (1, {'@': 39}), 7: (1, {'@': 39}), 27: (1, {'@': 39}), 28: (1, {'@': 39}), 29: (1, {'@': 39}), 30: (1, {'@': 39}), 8: (1, {'@': 39}), 31: (1, {'@': 39}), 9: (1, {'@': 39}), 32: (1, {'@': 39}), 33: (1, {'@': 39}), 10: (1, {'@': 39}), 35: (1, {'@': 39}), 34: (1, {'@': 39})}, 79: {3: (0, 65)}, 80: {7: (0, 100)}, 81: {70: (0, 113), 71: (0, 83), 8: (0, 151), 55: (0, 2), 72: (0, 115), 3: (0, 9), 53: (0, 116), 54: (0, 33), 73: (0, 111), 6: (0, 124), 2: (0, 129), 56: (0, 31), 0: (0, 108), 61: (0, 130)}, 82: {12: (1, {'@': 74}), 13: (1, {'@': 74}), 14: (1, {'@': 74}), 15: (1, {'@': 74}), 7: (1, {'@': 74}), 16: (1, {'@': 74}), 17: (1, {'@': 74}), 18: (1, {'@': 74}), 19: (1, {'@': 74}), 5: (1, {'@': 74})}, 83: {15: (0, 161), 17: (0, 152), 14: (0, 36), 12: (0, 32), 19: (0, 25), 18: (0, 21), 16: (1, {'@': 70}), 13: (1, {'@': 70}), 7: (1, {'@': 70}), 5: (1, {'@': 70})}, 84: {20: (1, {'@': 4}), 21: (1, {'@': 4}), 0: (1, {'@': 4}), 22: (1, {'@': 4}), 23: (1, {'@': 4}), 1: (1, {'@': 4}), 2: (1, {'@': 4}), 3: (1, {'@': 4}), 24: (1, {'@': 4}), 25: (1, {'@': 4}), 4: (1, {'@': 4}), 5: (1, {'@': 4}), 6: (1, {'@': 4}), 26: (1, {'@': 4}), 7: (1, {'@': 4}), 27: (1, {'@': 4}), 28: (1, {'@': 4}), 29: (1, {'@': 4}), 30: (1, {'@': 4}), 8: (1, {'@': 4}), 31: (1, {'@': 4}), 9: (1, {'@': 4}), 32: (1, {'@': 4}), 33: (1, {'@': 4}), 10: (1, {'@': 4}), 34: (1, {'@': 4}), 35: (1, {'@': 4})}, 85: {7: (0, 71)}, 86: {7: (1, {'@': 35})}, 87: {7: (0, 24)}, 88: {7: (1, {'@': 63})}, 89: {7: (0, 78)}, 90: {7: (0, 10)}, 91: {20: (1, {'@': 38}), 21: (1, {'@': 38}), 0: (1, {'@': 38}), 22: (1, {'@': 38}), 23: (1, {'@': 38}), 1: (1, {'@': 38}), 2: (1, {'@': 38}), 3: (1, {'@': 38}), 24: (1, {'@': 38}), 25: (1, {'@': 38}), 4: (1, {'@': 38}), 5: (1, {'@': 38}), 6: (1, {'@': 38}), 26: (1, {'@': 38}), 7: (1, {'@': 38}), 27: (1, {'@': 38}), 28: (1, {'@': 38}), 29: (1, {'@': 38}), 30: (1, {'@': 38}), 8: (1, {'@': 38}), 31: (1, {'@': 38}), 9: (1, {'@': 38}), 32: (1, {'@': 38}), 33: (1, {'@': 38}), 10: (1, {'@': 38}), 35: (1, {'@': 38}), 34: (1, {'@': 38})}, 92: {20: (1, {'@': 28}), 21: (1, {'@': 28}), 0: (1, {'@': 28}), 22: (1, {'@': 28}), 23: (1, {'@': 28}), 1: (1, {'@': 28}), 2: (1, {'@': 28}), 3: (1, {'@': 28}), 24: (1, {'@': 28}), 25: (1, {'@': 28}), 4: (1, {'@': 28}), 5: (1, {'@': 28}), 6: (1, {'@': 28}), 26: (1, {'@': 28}), 7: (1, {'@': 28}), 27: (1, {'@': 28}), 28: (1, {'@': 28}), 29: (1, {'@': 28}), 30: (1, {'@': 28}), 8: (1, {'@': 28}), 31: (1, {'@': 28}), 9: (1, {'@': 28}), 32: (1, {'@': 28}), 33: (1, {'@': 28}), 10: (1, {'@': 28}), 35: (1, {'@': 28}), 34: (1, {'@': 28})}, 93: {6: (0, 85), 3: (0, 87)}, 94: {20: (1, {'@': 12}), 21: (1, {'@': 12}), 0: (1, {'@': 12}), 22: (1, {'@': 12}), 23: (1, {'@': 12}), 1: (1, {'@': 12}), 2: (1, {'@': 12}), 3: (1, {'@': 12}), 24: (1, {'@': 12}), 25: (1, {'@': 12}), 4: (1, {'@': 12}), 5: (1, {'@': 12}), 6: (1, {'@': 12}), 26: (1, {'@': 12}), 7: (1, {'@': 12}), 27: (1, {'@': 12}), 28: (1, {'@': 12}), 29: (1, {'@': 12}), 30: (1, {'@': 12}), 8: (1, {'@': 12}), 31: (1, {'@': 12}), 9: (1, {'@': 12}), 32: (1, {'@': 12}), 33: (1, {'@': 12}), 10: (1, {'@': 12}), 34: (1, {'@': 12}), 35: (1, {'@': 12})}, 95: {0: (1, {'@': 101}), 6: (1, {'@': 101}), 4: (1, {'@': 101}), 5: (1, {'@': 101}), 10: (1, {'@': 101}), 8: (1, {'@': 101}), 1: (1, {'@': 101}), 2: (1, {'@': 101}), 9: (1, {'@': 101}), 3: (1, {'@': 101})}, 96: {76: (0, 126)}, 97: {7: (0, 146)}, 98: {15: (0, 161), 17: (0, 152), 14: (0, 36), 12: (0, 32), 19: (0, 25), 18: (0, 21), 16: (1, {'@': 69}), 13: (1, {'@': 69}), 7: (1, {'@': 69}), 5: (1, {'@': 69})}, 99: {20: (1, {'@': 17}), 21: (1, {'@': 17}), 0: (1, {'@': 17}), 22: (1, {'@': 17}), 23: (1, {'@': 17}), 1: (1, {'@': 17}), 2: (1, {'@': 17}), 3: (1, {'@': 17}), 24: (1, {'@': 17}), 25: (1, {'@': 17}), 4: (1, {'@': 17}), 5: (1, {'@': 17}), 6: (1, {'@': 17}), 26: (1, {'@': 17}), 7: (1, {'@': 17}), 27: (1, {'@': 17}), 28: (1, {'@': 17}), 29: (1, {'@': 17}), 30: (1, {'@': 17}), 8: (1, {'@': 17}), 31: (1, {'@': 17}), 9: (1, {'@': 17}), 32: (1, {'@': 17}), 33: (1, {'@': 17}), 10: (1, {'@': 17}), 34: (1, {'@': 17}), 35: (1, {'@': 17})}, 100: {20: (1, {'@': 11}), 21: (1, {'@': 11}), 0: (1, {'@': 11}), 22: (1, {'@': 11}), 23: (1, {'@': 11}), 1: (1, {'@': 11}), 2: (1, {'@': 11}), 3: (1, {'@': 11}), 24: (1, {'@': 11}), 25: (1, {'@': 11}), 4: (1, {'@': 11}), 5: (1, {'@': 11}), 6: (1, {'@': 11}), 26: (1, {'@': 11}), 7: (1, {'@': 11}), 27: (1, {'@': 11}), 28: (1, {'@': 11}), 29: (1, {'@': 11}), 30: (1, {'@': 11}), 8: (1, {'@': 11}), 31: (1, {'@': 11}), 9: (1, {'@': 11}), 32: (1, {'@': 11}), 33: (1, {'@': 11}), 10: (1, {'@': 11}), 34: (1, {'@': 11}), 35: (1, {'@': 11})}, 101: {20: (1, {'@': 7}), 21: (1, {'@': 7}), 0: (1, {'@': 7}), 22: (1, {'@': 7}), 23: (1, {'@': 7}), 1: (1, {'@': 7}), 2: (1, {'@': 7}), 3: (1, {'@': 7}), 24: (1, {'@': 7}), 25: (1, {'@': 7}), 4: (1, {'@': 7}), 5: (1, {'@': 7}), 6: (1, {'@': 7}), 26: (1, {'@': 7}), 7: (1, {'@': 7}), 27: (1, {'@': 7}), 28: (1, {'@': 7}), 29: (1, {'@': 7}), 30: (1, {'@': 7}), 8: (1, {'@': 7}), 31: (1, {'@': 7}), 9: (1, {'@': 7}), 32: (1, {'@': 7}), 33: (1, {'@': 7}), 10: (1, {'@': 7}), 34: (1, {'@': 7}), 35: (1, {'@': 7})}, 102: {6: (0, 68), 3: (0, 156), 76: (0, 16)}, 103: {20: (1, {'@': 13}), 21: (1, {'@': 13}), 0: (1, {'@': 13}), 22: (1, {'@': 13}), 23: (1, {'@': 13}), 1: (1, {'@': 13}), 2: (1, {'@': 13}), 3: (1, {'@': 13}), 24: (1, {'@': 13}), 25: (1, {'@': 13}), 4: (1, {'@': 13}), 5: (1, {'@': 13}), 6: (1, {'@': 13}), 26: (1, {'@': 13}), 7: (1, {'@': 13}), 27: (1, {'@': 13}), 28: (1, {'@': 13}), 29: (1, {'@': 13}), 30: (1, {'@': 13}), 8: (1, {'@': 13}), 31: (1, {'@': 13}), 9: (1, {'@': 13}), 32: (1, {'@': 13}), 33: (1, {'@': 13}), 10: (1, {'@': 13}), 34: (1, {'@': 13}), 35: (1, {'@': 13})}, 104: {7: (1, {'@': 34})}, 105: {7: (0, 149)}, 106: {3: (0, 147)}, 107: {74: (0, 14), 77: (0, 140), 75: (0, 137), 8: (0, 151), 53: (0, 154), 9: (0, 1), 55: (0, 2), 10: (0, 160), 2: (0, 143), 1: (0, 159), 57: (0, 95), 0: (0, 41), 54: (0, 33), 3: (0, 9), 6: (0, 28), 56: (0, 31), 4: (1, {'@': 99}), 5: (1, {'@': 99})}, 108: {14: (1, {'@': 79}), 16: (1, {'@': 79}), 18: (1, {'@': 79}), 12: (1, {'@': 79}), 13: (1, {'@': 79}), 5: (1, {'@': 79}), 15: (1, {'@': 79}), 7: (1, {'@': 79}), 17: (1, {'@': 79}), 19: (1, {'@': 79})}, 109: {20: (1, {'@': 16}), 21: (1, {'@': 16}), 0: (1, {'@': 16}), 22: (1, {'@': 16}), 23: (1, {'@': 16}), 1: (1, {'@': 16}), 2: (1, {'@': 16}), 3: (1, {'@': 16}), 24: (1, {'@': 16}), 25: (1, {'@': 16}), 4: (1, {'@': 16}), 5: (1, {'@': 16}), 6: (1, {'@': 16}), 26: (1, {'@': 16}), 7: (1, {'@': 16}), 27: (1, {'@': 16}), 28: (1, {'@': 16}), 29: (1, {'@': 16}), 30: (1, {'@': 16}), 8: (1, {'@': 16}), 31: (1, {'@': 16}), 9: (1, {'@': 16}), 32: (1, {'@': 16}), 33: (1, {'@': 16}), 10: (1, {'@': 16}), 34: (1, {'@': 16}), 35: (1, {'@': 16})}, 110: {20: (1, {'@': 43}), 21: (1, {'@': 43}), 0: (1, {'@': 43}), 22: (1, {'@': 43}), 23: (1, {'@': 43}), 1: (1, {'@': 43}), 2: (1, {'@': 43}), 3: (1, {'@': 43}), 24: (1, {'@': 43}), 25: (1, {'@': 43}), 4: (1, {'@': 43}), 5: (1, {'@': 43}), 6: (1, {'@': 43}), 26: (1, {'@': 43}), 7: (1, {'@': 43}), 27: (1, {'@': 43}), 28: (1, {'@': 43}), 29: (1, {'@': 43}), 30: (1, {'@': 43}), 8: (1, {'@': 43}), 31: (1, {'@': 43}), 9: (1, {'@': 43}), 32: (1, {'@': 43}), 33: (1, {'@': 43}), 10: (1, {'@': 43}), 35: (1, {'@': 43}), 34: (1, {'@': 43})}, 111: {7: (1, {'@': 65})}, 112: {20: (1, {'@': 18}), 21: (1, {'@': 18}), 0: (1, {'@': 18}), 22: (1, {'@': 18}), 23: (1, {'@': 18}), 1: (1, {'@': 18}), 2: (1, {'@': 18}), 3: (1, {'@': 18}), 24: (1, {'@': 18}), 25: (1, {'@': 18}), 4: (1, {'@': 18}), 5: (1, {'@': 18}), 6: (1, {'@': 18}), 26: (1, {'@': 18}), 7: (1, {'@': 18}), 27: (1, {'@': 18}), 28: (1, {'@': 18}), 29: (1, {'@': 18}), 30: (1, {'@': 18}), 8: (1, {'@': 18}), 31: (1, {'@': 18}), 9: (1, {'@': 18}), 32: (1, {'@': 18}), 33: (1, {'@': 18}), 10: (1, {'@': 18}), 34: (1, {'@': 18}), 35: (1, {'@': 18})}, 113: {13: (0, 136), 7: (1, {'@': 66}), 5: (1, {'@': 66})}, 114: {20: (1, {'@': 14}), 21: (1, {'@': 14}), 0: (1, {'@': 14}), 22: (1, {'@': 14}), 23: (1, {'@': 14}), 1: (1, {'@': 14}), 2: (1, {'@': 14}), 3: (1, {'@': 14}), 24: (1, {'@': 14}), 25: (1, {'@': 14}), 4: (1, {'@': 14}), 5: (1, {'@': 14}), 6: (1, {'@': 14}), 26: (1, {'@': 14}), 7: (1, {'@': 14}), 27: (1, {'@': 14}), 28: (1, {'@': 14}), 29: (1, {'@': 14}), 30: (1, {'@': 14}), 8: (1, {'@': 14}), 31: (1, {'@': 14}), 9: (1, {'@': 14}), 32: (1, {'@': 14}), 33: (1, {'@': 14}), 10: (1, {'@': 14}), 34: (1, {'@': 14}), 35: (1, {'@': 14})}, 115: {16: (0, 174), 13: (1, {'@': 68}), 7: (1, {'@': 68}), 5: (1, {'@': 68})}, 116: {14: (1, {'@': 78}), 16: (1, {'@': 78}), 18: (1, {'@': 78}), 12: (1, {'@': 78}), 13: (1, {'@': 78}), 5: (1, {'@': 78}), 15: (1, {'@': 78}), 7: (1, {'@': 78}), 17: (1, {'@': 78}), 19: (1, {'@': 78})}, 117: {20: (1, {'@': 8}), 21: (1, {'@': 8}), 0: (1, {'@': 8}), 22: (1, {'@': 8}), 23: (1, {'@': 8}), 1: (1, {'@': 8}), 2: (1, {'@': 8}), 3: (1, {'@': 8}), 24: (1, {'@': 8}), 25: (1, {'@': 8}), 4: (1, {'@': 8}), 5: (1, {'@': 8}), 6: (1, {'@': 8}), 26: (1, {'@': 8}), 7: (1, {'@': 8}), 27: (1, {'@': 8}), 28: (1, {'@': 8}), 29: (1, {'@': 8}), 30: (1, {'@': 8}), 8: (1, {'@': 8}), 31: (1, {'@': 8}), 9: (1, {'@': 8}), 32: (1, {'@': 8}), 33: (1, {'@': 8}), 10: (1, {'@': 8}), 34: (1, {'@': 8}), 35: (1, {'@': 8})}, 118: {20: (1, {'@': 9}), 21: (1, {'@': 9}), 0: (1, {'@': 9}), 22: (1, {'@': 9}), 23: (1, {'@': 9}), 1: (1, {'@': 9}), 2: (1, {'@': 9}), 3: (1, {'@': 9}), 24: (1, {'@': 9}), 25: (1, {'@': 9}), 4: (1, {'@': 9}), 5: (1, {'@': 9}), 6: (1, {'@': 9}), 26: (1, {'@': 9}), 7: (1, {'@': 9}), 27: (1, {'@': 9}), 28: (1, {'@': 9}), 29: (1, {'@': 9}), 30: (1, {'@': 9}), 8: (1, {'@': 9}), 31: (1, {'@': 9}), 9: (1, {'@': 9}), 32: (1, {'@': 9}), 33: (1, {'@': 9}), 10: (1, {'@': 9}), 34: (1, {'@': 9}), 35: (1, {'@': 9})}, 119: {20: (1, {'@': 20}), 21: (1, {'@': 20}), 0: (1, {'@': 20}), 22: (1, {'@': 20}), 23: (1, {'@': 20}), 1: (1, {'@': 20}), 2: (1, {'@': 20}), 3: (1, {'@': 20}), 24: (1, {'@': 20}), 25: (1, {'@': 20}), 4: (1, {'@': 20}), 5: (1, {'@': 20}), 6: (1, {'@': 20}), 26: (1, {'@': 20}), 7: (1, {'@': 20}), 27: (1, {'@': 20}), 28: (1, {'@': 20}), 29: (1, {'@': 20}), 30: (1, {'@': 20}), 8: (1, {'@': 20}), 31: (1, {'@': 20}), 9: (1, {'@': 20}), 32: (1, {'@': 20}), 33: (1, {'@': 20}), 10: (1, {'@': 20}), 34: (1, {'@': 20}), 35: (1, {'@': 20})}, 120: {7: (1, {'@': 33})}, 121: {20: (1, {'@': 2}), 21: (1, {'@': 2}), 0: (1, {'@': 2}), 22: (1, {'@': 2}), 23: (1, {'@': 2}), 1: (1, {'@': 2}), 2: (1, {'@': 2}), 3: (1, {'@': 2}), 24: (1, {'@': 2}), 25: (1, {'@': 2}), 4: (1, {'@': 2}), 5: (1, {'@': 2}), 6: (1, {'@': 2}), 26: (1, {'@': 2}), 7: (1, {'@': 2}), 27: (1, {'@': 2}), 28: (1, {'@': 2}), 29: (1, {'@': 2}), 30: (1, {'@': 2}), 8: (1, {'@': 2}), 31: (1, {'@': 2}), 9: (1, {'@': 2}), 32: (1, {'@': 2}), 33: (1, {'@': 2}), 10: (1, {'@': 2}), 34: (1, {'@': 2}), 35: (1, {'@': 2})}, 122: {21: (0, 173), 36: (0, 157), 37: (0, 6), 31: (0, 170), 23: (0, 15), 38: (0, 135), 24: (0, 43), 32: (0, 38), 39: (0, 97), 40: (0, 99), 41: (0, 94), 42: (0, 109), 43: (0, 60), 44: (0, 112), 25: (0, 93), 26: (0, 96), 45: (0, 114), 27: (0, 123), 46: (0, 103), 47: (0, 119), 48: (0, 105), 49: (0, 128), 34: (0, 92), 29: (0, 106), 50: (0, 127), 33: (0, 155), 28: (0, 81), 30: (0, 12), 4: (1, {'@': 82}), 5: (1, {'@': 82}), 3: (1, {'@': 82}), 6: (1, {'@': 82}), 0: (1, {'@': 82}), 7: (1, {'@': 82}), 8: (1, {'@': 82}), 1: (1, {'@': 82}), 2: (1, {'@': 82}), 9: (1, {'@': 82}), 10: (1, {'@': 82})}, 123: {58: (0, 132)}, 124: {14: (1, {'@': 80}), 16: (1, {'@': 80}), 18: (1, {'@': 80}), 12: (1, {'@': 80}), 13: (1, {'@': 80}), 5: (1, {'@': 80}), 15: (1, {'@': 80}), 7: (1, {'@': 80}), 17: (1, {'@': 80}), 19: (1, {'@': 80})}, 125: {20: (1, {'@': 45}), 21: (1, {'@': 45}), 0: (1, {'@': 45}), 22: (1, {'@': 45}), 23: (1, {'@': 45}), 1: (1, {'@': 45}), 2: (1, {'@': 45}), 3: (1, {'@': 45}), 24: (1, {'@': 45}), 25: (1, {'@': 45}), 4: (1, {'@': 45}), 5: (1, {'@': 45}), 6: (1, {'@': 45}), 26: (1, {'@': 45}), 7: (1, {'@': 45}), 27: (1, {'@': 45}), 28: (1, {'@': 45}), 29: (1, {'@': 45}), 30: (1, {'@': 45}), 8: (1, {'@': 45}), 31: (1, {'@': 45}), 9: (1, {'@': 45}), 32: (1, {'@': 45}), 33: (1, {'@': 45}), 10: (1, {'@': 45}), 35: (1, {'@': 45}), 34: (1, {'@': 45})}, 126: {6: (0, 89), 7: (0, 91)}, 127: {22: (0, 163), 78: (0, 171)}, 128: {20: (1, {'@': 15}), 21: (1, {'@': 15}), 0: (1, {'@': 15}), 22: (1, {'@': 15}), 23: (1, {'@': 15}), 1: (1, {'@': 15}), 2: (1, {'@': 15}), 3: (1, {'@': 15}), 24: (1, {'@': 15}), 25: (1, {'@': 15}), 4: (1, {'@': 15}), 5: (1, {'@': 15}), 6: (1, {'@': 15}), 26: (1, {'@': 15}), 7: (1, {'@': 15}), 27: (1, {'@': 15}), 28: (1, {'@': 15}), 29: (1, {'@': 15}), 30: (1, {'@': 15}), 8: (1, {'@': 15}), 31: (1, {'@': 15}), 9: (1, {'@': 15}), 32: (1, {'@': 15}), 33: (1, {'@': 15}), 10: (1, {'@': 15}), 34: (1, {'@': 15}), 35: (1, {'@': 15})}, 129: {70: (0, 113), 71: (0, 83), 8: (0, 151), 73: (0, 67), 55: (0, 2), 72: (0, 115), 3: (0, 9), 53: (0, 116), 54: (0, 33), 6: (0, 124), 2: (0, 129), 56: (0, 31), 0: (0, 108), 61: (0, 130)}, 130: {12: (1, {'@': 77}), 13: (1, {'@': 77}), 14: (1, {'@': 77}), 15: (1, {'@': 77}), 7: (1, {'@': 77}), 16: (1, {'@': 77}), 17: (1, {'@': 77}), 18: (1, {'@': 77}), 19: (1, {'@': 77}), 5: (1, {'@': 77})}, 131: {20: (1, {'@': 30}), 21: (1, {'@': 30}), 0: (1, {'@': 30}), 22: (1, {'@': 30}), 23: (1, {'@': 30}), 1: (1, {'@': 30}), 2: (1, {'@': 30}), 3: (1, {'@': 30}), 24: (1, {'@': 30}), 25: (1, {'@': 30}), 4: (1, {'@': 30}), 5: (1, {'@': 30}), 6: (1, {'@': 30}), 26: (1, {'@': 30}), 7: (1, {'@': 30}), 27: (1, {'@': 30}), 28: (1, {'@': 30}), 29: (1, {'@': 30}), 30: (1, {'@': 30}), 8: (1, {'@': 30}), 31: (1, {'@': 30}), 9: (1, {'@': 30}), 32: (1, {'@': 30}), 33: (1, {'@': 30}), 10: (1, {'@': 30}), 35: (1, {'@': 30}), 34: (1, {'@': 30})}, 132: {20: (1, {'@': 44}), 21: (1, {'@': 44}), 0: (1, {'@': 44}), 22: (1, {'@': 44}), 23: (1, {'@': 44}), 1: (1, {'@': 44}), 2: (1, {'@': 44}), 3: (1, {'@': 44}), 24: (1, {'@': 44}), 25: (1, {'@': 44}), 4: (1, {'@': 44}), 5: (1, {'@': 44}), 6: (1, {'@': 44}), 26: (1, {'@': 44}), 7: (1, {'@': 44}), 27: (1, {'@': 44}), 28: (1, {'@': 44}), 29: (1, {'@': 44}), 30: (1, {'@': 44}), 8: (1, {'@': 44}), 31: (1, {'@': 44}), 9: (1, {'@': 44}), 32: (1, {'@': 44}), 33: (1, {'@': 44}), 10: (1, {'@': 44}), 35: (1, {'@': 44}), 34: (1, {'@': 44})}, 133: {68: (0, 122), 31: (0, 170), 21: (0, 173), 37: (0, 6), 40: (0, 3), 30: (0, 12), 23: (0, 15), 38: (0, 20), 24: (0, 43), 32: (0, 38), 45: (0, 55), 42: (0, 58), 43: (0, 60), 28: (0, 81), 39: (0, 80), 44: (0, 101), 25: (0, 93), 26: (0, 96), 49: (0, 84), 46: (0, 121), 27: (0, 123), 48: (0, 105), 29: (0, 106), 34: (0, 42), 36: (0, 117), 47: (0, 118), 50: (0, 127), 33: (0, 155), 41: (0, 145), 4: (1, {'@': 82}), 5: (1, {'@': 82}), 3: (1, {'@': 82}), 6: (1, {'@': 82}), 0: (1, {'@': 82}), 7: (1, {'@': 82}), 8: (1, {'@': 82}), 1: (1, {'@': 82}), 2: (1, {'@': 82}), 9: (1, {'@': 82}), 10: (1, {'@': 82})}, 134: {68: (0, 8), 31: (0, 170), 21: (0, 173), 37: (0, 6), 40: (0, 3), 30: (0, 12), 23: (0, 15), 38: (0, 20), 24: (0, 43), 32: (0, 38), 45: (0, 55), 42: (0, 58), 43: (0, 60), 28: (0, 81), 39: (0, 80), 44: (0, 101), 25: (0, 93), 26: (0, 96), 49: (0, 84), 46: (0, 121), 27: (0, 123), 48: (0, 105), 29: (0, 106), 36: (0, 117), 47: (0, 118), 50: (0, 127), 33: (0, 155), 41: (0, 145), 79: (0, 165), 4: (1, {'@': 82}), 5: (1, {'@': 82}), 3: (1, {'@': 82}), 6: (1, {'@': 82}), 0: (1, {'@': 82}), 7: (1, {'@': 82}), 8: (1, {'@': 82}), 1: (1, {'@': 82}), 2: (1, {'@': 82}), 9: (1, {'@': 82}), 10: (1, {'@': 82})}, 135: {8: (0, 151), 7: (0, 150), 4: (0, 0), 9: (0, 1), 55: (0, 2), 10: (0, 160), 1: (0, 159), 5: (0, 148), 2: (0, 4), 3: (0, 9), 54: (0, 33), 6: (0, 28), 60: (0, 35), 56: (0, 31), 57: (0, 27), 0: (0, 41), 53: (0, 47)}, 136: {71: (0, 83), 8: (0, 151), 55: (0, 2), 72: (0, 75), 3: (0, 9), 53: (0, 116), 54: (0, 33), 6: (0, 124), 2: (0, 129), 56: (0, 31), 0: (0, 108), 61: (0, 130)}, 137: {4: (1, {'@': 98}), 5: (1, {'@': 98})}, 138: {80: (0, 166)}, 139: {68: (0, 164), 31: (0, 170), 21: (0, 173), 37: (0, 6), 40: (0, 3), 30: (0, 12), 23: (0, 15), 38: (0, 20), 24: (0, 43), 32: (0, 38), 45: (0, 55), 42: (0, 58), 43: (0, 60), 28: (0, 81), 39: (0, 80), 44: (0, 101), 25: (0, 93), 26: (0, 96), 49: (0, 84), 46: (0, 121), 27: (0, 123), 48: (0, 105), 29: (0, 106), 36: (0, 117), 47: (0, 118), 50: (0, 127), 33: (0, 155), 41: (0, 145), 4: (1, {'@': 82}), 5: (1, {'@': 82}), 3: (1, {'@': 82}), 6: (1, {'@': 82}), 0: (1, {'@': 82}), 7: (1, {'@': 82}), 8: (1, {'@': 82}), 1: (1, {'@': 82}), 2: (1, {'@': 82}), 9: (1, {'@': 82}), 10: (1, {'@': 82})}, 140: {4: (0, 70), 5: (0, 72)}, 141: {74: (0, 14), 77: (0, 37), 75: (0, 137), 8: (0, 151), 53: (0, 154), 9: (0, 1), 55: (0, 2), 10: (0, 160), 2: (0, 143), 1: (0, 159), 57: (0, 95), 0: (0, 41), 54: (0, 33), 3: (0, 9), 6: (0, 28), 56: (0, 31), 4: (1, {'@': 99}), 5: (1, {'@': 99})}, 142: {20: (1, {'@': 10}), 21: (1, {'@': 10}), 0: (1, {'@': 10}), 22: (1, {'@': 10}), 23: (1, {'@': 10}), 1: (1, {'@': 10}), 2: (1, {'@': 10}), 3: (1, {'@': 10}), 24: (1, {'@': 10}), 25: (1, {'@': 10}), 4: (1, {'@': 10}), 5: (1, {'@': 10}), 6: (1, {'@': 10}), 26: (1, {'@': 10}), 7: (1, {'@': 10}), 27: (1, {'@': 10}), 28: (1, {'@': 10}), 29: (1, {'@': 10}), 30: (1, {'@': 10}), 8: (1, {'@': 10}), 31: (1, {'@': 10}), 9: (1, {'@': 10}), 32: (1, {'@': 10}), 33: (1, {'@': 10}), 10: (1, {'@': 10}), 34: (1, {'@': 10}), 35: (1, {'@': 10})}, 143: {74: (0, 14), 77: (0, 40), 75: (0, 137), 8: (0, 151), 53: (0, 154), 9: (0, 1), 55: (0, 2), 10: (0, 160), 2: (0, 143), 1: (0, 159), 57: (0, 95), 0: (0, 41), 54: (0, 33), 3: (0, 9), 6: (0, 28), 56: (0, 31), 4: (1, {'@': 99}), 5: (1, {'@': 99})}, 144: {8: (0, 151), 4: (0, 0), 9: (0, 1), 55: (0, 2), 10: (0, 160), 1: (0, 159), 5: (0, 148), 2: (0, 4), 3: (0, 9), 54: (0, 33), 6: (0, 28), 60: (0, 35), 56: (0, 31), 57: (0, 27), 0: (0, 41), 53: (0, 47), 7: (1, {'@': 46})}, 145: {20: (1, {'@': 1}), 21: (1, {'@': 1}), 0: (1, {'@': 1}), 22: (1, {'@': 1}), 23: (1, {'@': 1}), 1: (1, {'@': 1}), 2: (1, {'@': 1}), 3: (1, {'@': 1}), 24: (1, {'@': 1}), 25: (1, {'@': 1}), 4: (1, {'@': 1}), 5: (1, {'@': 1}), 6: (1, {'@': 1}), 26: (1, {'@': 1}), 7: (1, {'@': 1}), 27: (1, {'@': 1}), 28: (1, {'@': 1}), 29: (1, {'@': 1}), 30: (1, {'@': 1}), 8: (1, {'@': 1}), 31: (1, {'@': 1}), 9: (1, {'@': 1}), 32: (1, {'@': 1}), 33: (1, {'@': 1}), 10: (1, {'@': 1}), 34: (1, {'@': 1}), 35: (1, {'@': 1})}, 146: {20: (1, {'@': 22}), 21: (1, {'@': 22}), 0: (1, {'@': 22}), 22: (1, {'@': 22}), 23: (1, {'@': 22}), 1: (1, {'@': 22}), 2: (1, {'@': 22}), 3: (1, {'@': 22}), 24: (1, {'@': 22}), 25: (1, {'@': 22}), 4: (1, {'@': 22}), 5: (1, {'@': 22}), 6: (1, {'@': 22}), 26: (1, {'@': 22}), 7: (1, {'@': 22}), 27: (1, {'@': 22}), 28: (1, {'@': 22}), 29: (1, {'@': 22}), 30: (1, {'@': 22}), 8: (1, {'@': 22}), 31: (1, {'@': 22}), 9: (1, {'@': 22}), 32: (1, {'@': 22}), 33: (1, {'@': 22}), 10: (1, {'@': 22}), 34: (1, {'@': 22}), 35: (1, {'@': 22})}, 147: {69: (0, 153), 81: (0, 22), 4: (1, {'@': 47}), 5: (1, {'@': 47}), 3: (1, {'@': 47}), 6: (1, {'@': 47}), 0: (1, {'@': 47}), 7: (1, {'@': 47}), 8: (1, {'@': 47}), 1: (1, {'@': 47}), 2: (1, {'@': 47}), 9: (1, {'@': 47}), 10: (1, {'@': 47})}, 148: {0: (1, {'@': 86}), 1: (1, {'@': 86}), 2: (1, {'@': 86}), 3: (1, {'@': 86}), 4: (1, {'@': 86}), 5: (1, {'@': 86}), 6: (1, {'@': 86}), 7: (1, {'@': 86}), 8: (1, {'@': 86}), 9: (1, {'@': 86}), 10: (1, {'@': 86})}, 149: {68: (0, 61), 31: (0, 170), 21: (0, 173), 37: (0, 6), 40: (0, 3), 30: (0, 12), 23: (0, 15), 38: (0, 20), 24: (0, 43), 32: (0, 38), 45: (0, 55), 42: (0, 58), 43: (0, 60), 28: (0, 81), 39: (0, 80), 44: (0, 101), 25: (0, 93), 26: (0, 96), 49: (0, 84), 46: (0, 121), 27: (0, 123), 48: (0, 105), 29: (0, 106), 36: (0, 117), 47: (0, 118), 50: (0, 127), 33: (0, 155), 41: (0, 145), 34: (0, 131), 4: (1, {'@': 82}), 5: (1, {'@': 82}), 3: (1, {'@': 82}), 6: (1, {'@': 82}), 0: (1, {'@': 82}), 7: (1, {'@': 82}), 8: (1, {'@': 82}), 1: (1, {'@': 82}), 2: (1, {'@': 82}), 9: (1, {'@': 82}), 10: (1, {'@': 82})}, 150: {20: (1, {'@': 21}), 21: (1, {'@': 21}), 0: (1, {'@': 21}), 22: (1, {'@': 21}), 23: (1, {'@': 21}), 1: (1, {'@': 21}), 2: (1, {'@': 21}), 3: (1, {'@': 21}), 24: (1, {'@': 21}), 25: (1, {'@': 21}), 4: (1, {'@': 21}), 5: (1, {'@': 21}), 6: (1, {'@': 21}), 26: (1, {'@': 21}), 7: (1, {'@': 21}), 27: (1, {'@': 21}), 28: (1, {'@': 21}), 29: (1, {'@': 21}), 30: (1, {'@': 21}), 8: (1, {'@': 21}), 31: (1, {'@': 21}), 9: (1, {'@': 21}), 32: (1, {'@': 21}), 33: (1, {'@': 21}), 10: (1, {'@': 21}), 34: (1, {'@': 21}), 35: (1, {'@': 21})}, 151: {8: (0, 151), 3: (0, 9), 56: (0, 77)}, 152: {0: (0, 108), 54: (0, 33), 8: (0, 151), 55: (0, 2), 3: (0, 9), 53: (0, 116), 6: (0, 124), 2: (0, 129), 61: (0, 73), 56: (0, 31)}, 153: {82: (0, 169), 3: (0, 50), 59: (0, 172)}, 154: {0: (1, {'@': 102}), 6: (1, {'@': 102}), 4: (1, {'@': 102}), 5: (1, {'@': 102}), 10: (1, {'@': 102}), 8: (1, {'@': 102}), 1: (1, {'@': 102}), 2: (1, {'@': 102}), 9: (1, {'@': 102}), 3: (1, {'@': 102})}, 155: {3: (0, 175)}, 156: {20: (1, {'@': 53}), 21: (1, {'@': 53}), 0: (1, {'@': 53}), 22: (1, {'@': 53}), 23: (1, {'@': 53}), 1: (1, {'@': 53}), 2: (1, {'@': 53}), 3: (1, {'@': 53}), 24: (1, {'@': 53}), 25: (1, {'@': 53}), 4: (1, {'@': 53}), 5: (1, {'@': 53}), 6: (1, {'@': 53}), 26: (1, {'@': 53}), 7: (1, {'@': 53}), 27: (1, {'@': 53}), 28: (1, {'@': 53}), 29: (1, {'@': 53}), 30: (1, {'@': 53}), 8: (1, {'@': 53}), 31: (1, {'@': 53}), 9: (1, {'@': 53}), 32: (1, {'@': 53}), 33: (1, {'@': 53}), 10: (1, {'@': 53}), 35: (1, {'@': 53}), 34: (1, {'@': 53})}, 157: {20: (1, {'@': 19}), 21: (1, {'@': 19}), 0: (1, {'@': 19}), 22: (1, {'@': 19}), 23: (1, {'@': 19}), 1: (1, {'@': 19}), 2: (1, {'@': 19}), 3: (1, {'@': 19}), 24: (1, {'@': 19}), 25: (1, {'@': 19}), 4: (1, {'@': 19}), 5: (1, {'@': 19}), 6: (1, {'@': 19}), 26: (1, {'@': 19}), 7: (1, {'@': 19}), 27: (1, {'@': 19}), 28: (1, {'@': 19}), 29: (1, {'@': 19}), 30: (1, {'@': 19}), 8: (1, {'@': 19}), 31: (1, {'@': 19}), 9: (1, {'@': 19}), 32: (1, {'@': 19}), 33: (1, {'@': 19}), 10: (1, {'@': 19}), 34: (1, {'@': 19}), 35: (1, {'@': 19})}, 158: {0: (1, {'@': 104}), 6: (1, {'@': 104}), 4: (1, {'@': 104}), 5: (1, {'@': 104}), 10: (1, {'@': 104}), 8: (1, {'@': 104}), 1: (1, {'@': 104}), 2: (1, {'@': 104}), 9: (1, {'@': 104}), 3: (1, {'@': 104})}, 159: {0: (1, {'@': 24}), 1: (1, {'@': 24}), 2: (1, {'@': 24}), 3: (1, {'@': 24}), 4: (1, {'@': 24}), 6: (1, {'@': 24}), 5: (1, {'@': 24}), 8: (1, {'@': 24}), 9: (1, {'@': 24}), 10: (1, {'@': 24}), 7: (1, {'@': 24})}, 160: {0: (1, {'@': 26}), 1: (1, {'@': 26}), 2: (1, {'@': 26}), 3: (1, {'@': 26}), 4: (1, {'@': 26}), 6: (1, {'@': 26}), 5: (1, {'@': 26}), 8: (1, {'@': 26}), 9: (1, {'@': 26}), 10: (1, {'@': 26}), 7: (1, {'@': 26})}, 161: {0: (0, 108), 54: (0, 33), 8: (0, 151), 55: (0, 2), 3: (0, 9), 53: (0, 116), 61: (0, 23), 6: (0, 124), 2: (0, 129), 56: (0, 31)}, 162: {7: (1, {'@': 32})}, 163: {7: (0, 139)}, 164: {21: (0, 173), 36: (0, 157), 37: (0, 6), 31: (0, 170), 23: (0, 15), 38: (0, 135), 24: (0, 43), 32: (0, 38), 39: (0, 97), 40: (0, 99), 41: (0, 94), 42: (0, 109), 43: (0, 60), 44: (0, 112), 25: (0, 93), 26: (0, 96), 45: (0, 114), 27: (0, 123), 46: (0, 103), 47: (0, 119), 48: (0, 105), 49: (0, 128), 29: (0, 106), 50: (0, 127), 33: (0, 155), 28: (0, 81), 30: (0, 12), 20: (1, {'@': 62}), 4: (1, {'@': 82}), 5: (1, {'@': 82}), 3: (1, {'@': 82}), 6: (1, {'@': 82}), 0: (1, {'@': 82}), 7: (1, {'@': 82}), 8: (1, {'@': 82}), 1: (1, {'@': 82}), 2: (1, {'@': 82}), 9: (1, {'@': 82}), 10: (1, {'@': 82})}, 165: {}, 166: {0: (1, {'@': 37}), 6: (1, {'@': 37}), 65: (1, {'@': 37}), 8: (1, {'@': 37}), 2: (1, {'@': 37}), 3: (1, {'@': 37})}, 167: {20: (1, {'@': 57}), 21: (1, {'@': 57}), 0: (1, {'@': 57}), 22: (1, {'@': 57}), 23: (1, {'@': 57}), 1: (1, {'@': 57}), 2: (1, {'@': 57}), 3: (1, {'@': 57}), 24: (1, {'@': 57}), 25: (1, {'@': 57}), 4: (1, {'@': 57}), 5: (1, {'@': 57}), 6: (1, {'@': 57}), 26: (1, {'@': 57}), 7: (1, {'@': 57}), 27: (1, {'@': 57}), 28: (1, {'@': 57}), 29: (1, {'@': 57}), 30: (1, {'@': 57}), 8: (1, {'@': 57}), 31: (1, {'@': 57}), 9: (1, {'@': 57}), 32: (1, {'@': 57}), 33: (1, {'@': 57}), 10: (1, {'@': 57}), 35: (1, {'@': 57}), 34: (1, {'@': 57})}, 168: {20: (1, {'@': 31}), 21: (1, {'@': 31}), 0: (1, {'@': 31}), 22: (1, {'@': 31}), 23: (1, {'@': 31}), 1: (1, {'@': 31}), 2: (1, {'@': 31}), 3: (1, {'@': 31}), 24: (1, {'@': 31}), 25: (1, {'@': 31}), 4: (1, {'@': 31}), 5: (1, {'@': 31}), 6: (1, {'@': 31}), 26: (1, {'@': 31}), 7: (1, {'@': 31}), 27: (1, {'@': 31}), 28: (1, {'@': 31}), 29: (1, {'@': 31}), 30: (1, {'@': 31}), 8: (1, {'@': 31}), 31: (1, {'@': 31}), 9: (1, {'@': 31}), 32: (1, {'@': 31}), 33: (1, {'@': 31}), 10: (1, {'@': 31}), 35: (1, {'@': 31}), 34: (1, {'@': 31})}, 169: {4: (0, 63), 59: (0, 30)}, 170: {3: (0, 110)}, 171: {20: (0, 69)}, 172: {4: (1, {'@': 48}), 5: (1, {'@': 48}), 3: (1, {'@': 48}), 6: (1, {'@': 48}), 0: (1, {'@': 48}), 7: (1, {'@': 48}), 8: (1, {'@': 48}), 1: (1, {'@': 48}), 2: (1, {'@': 48}), 9: (1, {'@': 48}), 10: (1, {'@': 48})}, 173: {3: (0, 88)}, 174: {71: (0, 98), 8: (0, 151), 55: (0, 2), 3: (0, 9), 53: (0, 116), 54: (0, 33), 6: (0, 124), 2: (0, 129), 56: (0, 31), 0: (0, 108), 61: (0, 130)}, 175: {7: (1, {'@': 64})}}, 'start_states': {'start': 134}, 'end_states': {'start': 165}}, '__type__': 'ParsingFrontend'}, 'rules': [{'@': 0}, {'@': 1}, {'@': 2}, {'@': 3}, {'@': 4}, {'@': 5}, {'@': 6}, {'@': 7}, {'@': 8}, {'@': 9}, {'@': 10}, {'@': 11}, {'@': 12}, {'@': 13}, {'@': 14}, {'@': 15}, {'@': 16}, {'@': 17}, {'@': 18}, {'@': 19}, {'@': 20}, {'@': 21}, {'@': 22}, {'@': 23}, {'@': 24}, {'@': 25}, {'@': 26}, {'@': 27}, {'@': 28}, {'@': 29}, {'@': 30}, {'@': 31}, {'@': 32}, {'@': 33}, {'@': 34}, {'@': 35}, {'@': 36}, {'@': 37}, {'@': 38}, {'@': 39}, {'@': 40}, {'@': 41}, {'@': 42}, {'@': 43}, {'@': 44}, {'@': 45}, {'@': 46}, {'@': 47}, {'@': 48}, {'@': 49}, {'@': 50}, {'@': 51}, {'@': 52}, {'@': 53}, {'@': 54}, {'@': 55}, {'@': 56}, {'@': 57}, {'@': 58}, {'@': 59}, {'@': 60}, {'@': 61}, {'@': 62}, {'@': 63}, {'@': 64}, {'@': 65}, {'@': 66}, {'@': 67}, {'@': 68}, {'@': 69}, {'@': 70}, {'@': 71}, {'@': 72}, {'@': 73}, {'@': 74}, {'@': 75}, {'@': 76}, {'@': 77}, {'@': 78}, {'@': 79}, {'@': 80}, {'@': 81}, {'@': 82}, {'@': 83}, {'@': 84}, {'@': 85}, {'@': 86}, {'@': 87}, {'@': 88}, {'@': 89}, {'@': 90}, {'@': 91}, {'@': 92}, {'@': 93}, {'@': 94}, {'@': 95}, {'@': 96}, {'@': 97}, {'@': 98}, {'@': 99}, {'@': 100}, {'@': 101}, {'@': 102}, {'@': 103}, {'@': 104}, {'@': 105}, {'@': 106}], 'options': {'debug': False, 'strict': False, 'keep_all_tokens': False, 'tree_class': None, 'cache': False, 'cache_grammar': False, 'postlex': None, 'parser': 'lalr', 'lexer': 'contextual', 'transformer': None, 'start': ['start'], 'priority': 'normal', 'ambiguity': 'auto', 'regex': False, 'propagate_positions': False, 'lexer_callbacks': {}, 'maybe_placeholders': False, 'edit_terminals': None, 'g_regex_flags': 0, 'use_bytes': False, 'ordered_sets': True, 'import_paths': [], 'source_path': None, '_plugins': {}}, '__type__': 'Lark'} +) +MEMO = ( +{0: {'origin': {'name': 'start', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 1: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'include_file', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 2: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'line', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 3: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'init', '__type__': 'NonTerminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 4: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'undef', '__type__': 'NonTerminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 5: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ifdef', '__type__': 'NonTerminal'}], 'order': 4, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 6: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'require', '__type__': 'NonTerminal'}], 'order': 5, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 7: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'pragma', '__type__': 'NonTerminal'}], 'order': 6, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 8: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'errormsg', '__type__': 'NonTerminal'}], 'order': 7, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 9: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'warningmsg', '__type__': 'NonTerminal'}], 'order': 8, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 10: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'defs', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 9, 'alias': 'program_tokenstring', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 11: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'define', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 10, 'alias': 'program_tokenstring_2', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 12: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'include_file', '__type__': 'NonTerminal'}], 'order': 11, 'alias': 'program_char', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 13: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'line', '__type__': 'NonTerminal'}], 'order': 12, 'alias': 'program_char', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 14: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'init', '__type__': 'NonTerminal'}], 'order': 13, 'alias': 'program_char', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 15: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'undef', '__type__': 'NonTerminal'}], 'order': 14, 'alias': 'program_char', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 16: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'ifdef', '__type__': 'NonTerminal'}], 'order': 15, 'alias': 'program_char', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 17: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'require', '__type__': 'NonTerminal'}], 'order': 16, 'alias': 'program_char', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 18: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'pragma', '__type__': 'NonTerminal'}], 'order': 17, 'alias': 'program_char', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 19: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'errormsg', '__type__': 'NonTerminal'}], 'order': 18, 'alias': 'program_char', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 20: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'warningmsg', '__type__': 'NonTerminal'}], 'order': 19, 'alias': 'program_char', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 21: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'defs', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 20, 'alias': 'program_newline', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 22: {'origin': {'name': 'program', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'program', '__type__': 'NonTerminal'}, {'name': 'define', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 21, 'alias': 'program_newline_2', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 23: {'origin': {'name': 'token', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 24: {'origin': {'name': 'token', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'TOKEN', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 25: {'origin': {'name': 'token', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'CONTINUE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 26: {'origin': {'name': 'token', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'SEPARATOR', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 27: {'origin': {'name': 'token', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NUMBER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 28: {'origin': {'name': 'include_file', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'include', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'program', '__type__': 'NonTerminal'}, {'name': 'ENDFILE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 29: {'origin': {'name': 'include_file', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'include', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ENDFILE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'include_file_empty', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 30: {'origin': {'name': 'include_file', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'include_once', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ENDFILE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'include_once_empty', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 31: {'origin': {'name': 'include_file', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'include_once', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'program', '__type__': 'NonTerminal'}, {'name': 'ENDFILE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'include_once_ok', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 32: {'origin': {'name': 'include', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INCLUDE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'include_modifier', '__type__': 'NonTerminal'}, {'name': 'FILENAME', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'include_fname', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 33: {'origin': {'name': 'include', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INCLUDE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'include_modifier', '__type__': 'NonTerminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'include_macro', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 34: {'origin': {'name': 'include_once', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INCLUDE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ONCE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'include_modifier', '__type__': 'NonTerminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'include_once_str', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 35: {'origin': {'name': 'include_once', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INCLUDE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ONCE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'include_modifier', '__type__': 'NonTerminal'}, {'name': 'FILENAME', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'include_once_fname', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 36: {'origin': {'name': 'include_modifier', '__type__': 'NonTerminal'}, 'expansion': [], 'order': 0, 'alias': 'include_modifier_empty', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 37: {'origin': {'name': 'include_modifier', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LB', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'CO', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RB', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'include_modifier_arch', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 38: {'origin': {'name': 'line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'INTEGER', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 39: {'origin': {'name': 'line', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'INTEGER', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'line_file', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 40: {'origin': {'name': 'require', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'REQUIRE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 41: {'origin': {'name': 'init', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INIT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'init_id', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 42: {'origin': {'name': 'init', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'INIT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'init_str', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 43: {'origin': {'name': 'undef', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'UNDEF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 44: {'origin': {'name': 'errormsg', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ERROR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'TEXT', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 45: {'origin': {'name': 'warningmsg', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'WARNING', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'TEXT', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 46: {'origin': {'name': 'define', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'DEFINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'params', '__type__': 'NonTerminal'}, {'name': 'defs', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 47: {'origin': {'name': 'params', '__type__': 'NonTerminal'}, 'expansion': [], 'order': 0, 'alias': 'params_epsilon', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 48: {'origin': {'name': 'params', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'params_empty', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 49: {'origin': {'name': 'params', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'paramlist', '__type__': 'NonTerminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'params_paramlist', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 50: {'origin': {'name': 'paramlist', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'paramlist_single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 51: {'origin': {'name': 'paramlist', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'paramlist', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'paramlist_paramlist', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 52: {'origin': {'name': 'pragma', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PRAGMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'pragma_id', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 53: {'origin': {'name': 'pragma', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PRAGMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'pragma_id_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 54: {'origin': {'name': 'pragma', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PRAGMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'INTEGER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'pragma_id_expr', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 55: {'origin': {'name': 'pragma', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PRAGMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'pragma_id_string', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 56: {'origin': {'name': 'pragma', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PRAGMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'PUSH', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 4, 'alias': 'pragma_push', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 57: {'origin': {'name': 'pragma', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PRAGMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'POP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'LP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'RP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'pragma_push', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 58: {'origin': {'name': 'pragma', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'PRAGMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ONCE', 'filter_out': False, '__type__': 'Terminal'}], 'order': 6, 'alias': 'pragma_once', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 59: {'origin': {'name': 'ifdef', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_header', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'program', '__type__': 'NonTerminal'}, {'name': 'ENDIF', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 60: {'origin': {'name': 'ifdef', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ifdefelsea', '__type__': 'NonTerminal'}, {'name': 'ifdefelseb', '__type__': 'NonTerminal'}, {'name': 'ENDIF', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'ifdef_else', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 61: {'origin': {'name': 'ifdefelsea', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'if_header', '__type__': 'NonTerminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'program', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 62: {'origin': {'name': 'ifdefelseb', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ELSE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'NEWLINE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'program', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 63: {'origin': {'name': 'if_header', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IFDEF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': 'ifdef_header', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 64: {'origin': {'name': 'if_header', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IFNDEF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'ifndef_header', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 65: {'origin': {'name': 'if_header', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'IF', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'if_expr_header', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 66: {'origin': {'name': 'expr', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_or', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 67: {'origin': {'name': 'expr_or', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_or', '__type__': 'NonTerminal'}, {'name': 'OR', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_and', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expror', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 68: {'origin': {'name': 'expr_or', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_and', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 69: {'origin': {'name': 'expr_and', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_and', '__type__': 'NonTerminal'}, {'name': 'AND', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_cmp', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'exprand', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 70: {'origin': {'name': 'expr_and', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_cmp', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 71: {'origin': {'name': 'expr_cmp', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_cmp', '__type__': 'NonTerminal'}, {'name': 'EQ', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_atom', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expreq', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 72: {'origin': {'name': 'expr_cmp', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_cmp', '__type__': 'NonTerminal'}, {'name': 'NE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_atom', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'exprne', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 73: {'origin': {'name': 'expr_cmp', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_cmp', '__type__': 'NonTerminal'}, {'name': 'LT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_atom', '__type__': 'NonTerminal'}], 'order': 2, 'alias': 'exprlt', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 74: {'origin': {'name': 'expr_cmp', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_cmp', '__type__': 'NonTerminal'}, {'name': 'LE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_atom', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'exprle', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 75: {'origin': {'name': 'expr_cmp', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_cmp', '__type__': 'NonTerminal'}, {'name': 'GT', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_atom', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'exprgt', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 76: {'origin': {'name': 'expr_cmp', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_cmp', '__type__': 'NonTerminal'}, {'name': 'GE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr_atom', '__type__': 'NonTerminal'}], 'order': 5, 'alias': 'exprge', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 77: {'origin': {'name': 'expr_cmp', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'expr_atom', '__type__': 'NonTerminal'}], 'order': 6, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 78: {'origin': {'name': 'expr_atom', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'macrocall', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'expr_macrocall', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 79: {'origin': {'name': 'expr_atom', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'NUMBER', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'expr_val', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 80: {'origin': {'name': 'expr_atom', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'STRING', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'expr_str', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 81: {'origin': {'name': 'expr_atom', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LLP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'expr', '__type__': 'NonTerminal'}, {'name': 'RRP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'expr_par', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 82: {'origin': {'name': 'defs', '__type__': 'NonTerminal'}, 'expansion': [], 'order': 0, 'alias': 'defs_list_eps', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 83: {'origin': {'name': 'defs', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'defs', '__type__': 'NonTerminal'}, {'name': 'def_item', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'defs_list', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 84: {'origin': {'name': 'def_item', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'token', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'def_item_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 85: {'origin': {'name': 'def_item', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'def_item_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 86: {'origin': {'name': 'def_item', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'RRP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'def_item_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 87: {'origin': {'name': 'def_item', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LLP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 3, 'alias': 'def_item_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 88: {'origin': {'name': 'def_item', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'macrocall', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'def_macrocall', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 89: {'origin': {'name': 'macrocall', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'macrocall_call', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 90: {'origin': {'name': 'macrocall_call', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'macrocall_call', '__type__': 'NonTerminal'}, {'name': 'args', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'macrocall_args', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 91: {'origin': {'name': 'macrocall_call', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'macrocall_paste', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 92: {'origin': {'name': 'macrocall_paste', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'macrocall_paste', '__type__': 'NonTerminal'}, {'name': 'PASTE', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'macrocall_unary', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'macrocall_paste', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 93: {'origin': {'name': 'macrocall_paste', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'macrocall_unary', '__type__': 'NonTerminal'}], 'order': 1, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 94: {'origin': {'name': 'macrocall_unary', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'STRINGIZING', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'macrocall_unary', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'macrocall_stringizing', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 95: {'origin': {'name': 'macrocall_unary', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'ID', 'filter_out': False, '__type__': 'Terminal'}], 'order': 1, 'alias': 'macrocall_id', 'options': {'keep_all_tokens': False, 'expand1': True, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 96: {'origin': {'name': 'args', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LLP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arglist', '__type__': 'NonTerminal'}, {'name': 'RRP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 97: {'origin': {'name': 'arglist', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'arglist', '__type__': 'NonTerminal'}, {'name': 'COMMA', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arg', '__type__': 'NonTerminal'}], 'order': 0, 'alias': None, 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 98: {'origin': {'name': 'arglist', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'arg', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'arglist_single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 99: {'origin': {'name': 'arg', '__type__': 'NonTerminal'}, 'expansion': [], 'order': 0, 'alias': 'arg_eps', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 100: {'origin': {'name': 'arg', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'argstring', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'arg_val', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 101: {'origin': {'name': 'argstring', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'token', '__type__': 'NonTerminal'}], 'order': 0, 'alias': 'argstring_token_single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 102: {'origin': {'name': 'argstring', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'macrocall', '__type__': 'NonTerminal'}], 'order': 1, 'alias': 'argstring_macrocall_single', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 103: {'origin': {'name': 'argstring', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'LLP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arglist', '__type__': 'NonTerminal'}, {'name': 'RRP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 2, 'alias': 'argstring_argslist', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 104: {'origin': {'name': 'argstring', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'argstring', '__type__': 'NonTerminal'}, {'name': 'token', '__type__': 'NonTerminal'}], 'order': 3, 'alias': 'argstring_token', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 105: {'origin': {'name': 'argstring', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'argstring', '__type__': 'NonTerminal'}, {'name': 'macrocall', '__type__': 'NonTerminal'}], 'order': 4, 'alias': 'argstring_macrocall', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}, 106: {'origin': {'name': 'argstring', '__type__': 'NonTerminal'}, 'expansion': [{'name': 'argstring', '__type__': 'NonTerminal'}, {'name': 'LLP', 'filter_out': False, '__type__': 'Terminal'}, {'name': 'arglist', '__type__': 'NonTerminal'}, {'name': 'RRP', 'filter_out': False, '__type__': 'Terminal'}], 'order': 5, 'alias': 'argstring_argstring', 'options': {'keep_all_tokens': False, 'expand1': False, 'priority': None, 'template_source': None, 'empty_indices': (), '__type__': 'RuleOptions'}, '__type__': 'Rule'}} +) +Shift = 0 +Reduce = 1 +def Lark_StandAlone(**kwargs): + return Lark._load_from_dict(DATA, MEMO, **kwargs) diff --git a/src/zxbpp/zxbpplex.py b/src/zxbpp/zxbpplex.py index 2373fab03..a3e34d5c6 100755 --- a/src/zxbpp/zxbpplex.py +++ b/src/zxbpp/zxbpplex.py @@ -1,19 +1,16 @@ #!/usr/bin/env python3 -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# -# This is the Lexer for the ZXBpp (ZXBasic Preprocessor) -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import re import sys -from src.api import global_ -from src.ply import lex +from src.api import global_, lex from src.zxbpp.base_pplex import BaseLexer, ReservedDirectives from src.zxbpp.prepro.definestable import DefinesTable @@ -38,6 +35,7 @@ "OR", "STRING", "TEXT", + "CO", "TOKEN", "NEWLINE", "_ENDFILE_", @@ -47,10 +45,12 @@ "EQ", "PUSH", "POP", + "LB", "LP", "LLP", "RRP", "RP", + "RB", "COMMA", "CONTINUE", "NUMBER", @@ -153,6 +153,18 @@ def t_prepro_define_pragma_defargs_defargsopt_CONTINUE(self, t): t.lexer.lineno += 1 return t + def t_prepro_LB(self, t): + r"\[" + return t + + def t_prepro_RB(self, t): + r"\]" + return t + + def t_prepro_CO(self, t): + r"\:" + return t + def t_INITIAL_comment_beginBlock(self, t): r"/'" self.__COMMENT_LEVEL += 1 diff --git a/tests/__init__.py b/tests/__init__.py index 5ace62cdb..0b98abd16 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,5 +1,11 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import os -import os.path import sys path = os.path.realpath(os.path.join(os.path.dirname(__file__), os.pardir)) diff --git a/tests/api/__init__.py b/tests/api/__init__.py index e69de29bb..906667a8d 100644 --- a/tests/api/__init__.py +++ b/tests/api/__init__.py @@ -0,0 +1,6 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- diff --git a/tests/api/test_arg_parser.py b/tests/api/test_arg_parser.py index 1d2ff353f..fe60f3cb0 100644 --- a/tests/api/test_arg_parser.py +++ b/tests/api/test_arg_parser.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import unittest from src.arch.z80 import backend diff --git a/tests/api/test_check.py b/tests/api/test_check.py index 7e5ed55c1..72013b83b 100644 --- a/tests/api/test_check.py +++ b/tests/api/test_check.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import unittest from src.api import check diff --git a/tests/api/test_config.py b/tests/api/test_config.py index 891eb86bb..fbaae9778 100644 --- a/tests/api/test_config.py +++ b/tests/api/test_config.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import sys import unittest from unittest import mock diff --git a/tests/api/test_symbolTable.py b/tests/api/test_symbolTable.py index c0a7f69f5..5810da7a5 100644 --- a/tests/api/test_symbolTable.py +++ b/tests/api/test_symbolTable.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from io import StringIO from unittest import TestCase diff --git a/tests/api/test_utils.py b/tests/api/test_utils.py index 34bb62fb4..6cccaa6c1 100644 --- a/tests/api/test_utils.py +++ b/tests/api/test_utils.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import unittest from src.api import utils diff --git a/tests/arch/zx48k/backend/test_memcell.py b/tests/arch/zx48k/backend/test_memcell.py index 776091cf7..71331db3a 100644 --- a/tests/arch/zx48k/backend/test_memcell.py +++ b/tests/arch/zx48k/backend/test_memcell.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import unittest import src.arch diff --git a/tests/arch/zx48k/optimizer/common.py b/tests/arch/zx48k/optimizer/common.py index 3c604c471..6db51ecb7 100644 --- a/tests/arch/zx48k/optimizer/common.py +++ b/tests/arch/zx48k/optimizer/common.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from contextlib import contextmanager from src.api.config import OPTIONS diff --git a/tests/arch/zx48k/optimizer/test_basicblock.py b/tests/arch/zx48k/optimizer/test_basicblock.py index c8d7b6de1..3adc0da76 100644 --- a/tests/arch/zx48k/optimizer/test_basicblock.py +++ b/tests/arch/zx48k/optimizer/test_basicblock.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import unittest import src.arch.z80.optimizer.flow_graph diff --git a/tests/arch/zx48k/optimizer/test_cpustate.py b/tests/arch/zx48k/optimizer/test_cpustate.py index 3cf5d9ae9..a1a7ab12f 100644 --- a/tests/arch/zx48k/optimizer/test_cpustate.py +++ b/tests/arch/zx48k/optimizer/test_cpustate.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import pytest from src.arch.z80.optimizer import cpustate, helpers diff --git a/tests/arch/zx48k/optimizer/test_helpers.py b/tests/arch/zx48k/optimizer/test_helpers.py index 6e41ce99b..cb4612f92 100644 --- a/tests/arch/zx48k/optimizer/test_helpers.py +++ b/tests/arch/zx48k/optimizer/test_helpers.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.arch.z80.optimizer import helpers diff --git a/tests/arch/zx48k/optimizer/test_o1_optimizer.py b/tests/arch/zx48k/optimizer/test_o1_optimizer.py index 931081681..fc6730dc3 100644 --- a/tests/arch/zx48k/optimizer/test_o1_optimizer.py +++ b/tests/arch/zx48k/optimizer/test_o1_optimizer.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.arch.z80.backend import Backend from tests.arch.zx48k.optimizer.common import mock_options_level diff --git a/tests/arch/zx48k/optimizer/test_optimizer.py b/tests/arch/zx48k/optimizer/test_optimizer.py index 3db7f24a7..fbea1cf62 100644 --- a/tests/arch/zx48k/optimizer/test_optimizer.py +++ b/tests/arch/zx48k/optimizer/test_optimizer.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from src.arch.z80 import optimizer from src.arch.z80.peephole import engine from tests.arch.zx48k.optimizer.common import mock_options_level diff --git a/tests/arch/zx48k/optimizer/test_optimizer_asm.py b/tests/arch/zx48k/optimizer/test_optimizer_asm.py index b212aeb1c..34cdfadfb 100644 --- a/tests/arch/zx48k/optimizer/test_optimizer_asm.py +++ b/tests/arch/zx48k/optimizer/test_optimizer_asm.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import unittest from src.arch.z80.optimizer import asm, helpers diff --git a/tests/arch/zx48k/peephole/test_evaluator.py b/tests/arch/zx48k/peephole/test_evaluator.py index faac87744..e354ddd0d 100644 --- a/tests/arch/zx48k/peephole/test_evaluator.py +++ b/tests/arch/zx48k/peephole/test_evaluator.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import unittest from src.arch.z80.peephole import evaluator diff --git a/tests/arch/zx48k/peephole/test_parser.py b/tests/arch/zx48k/peephole/test_parser.py index 54762b2e2..1cf1192d8 100644 --- a/tests/arch/zx48k/peephole/test_parser.py +++ b/tests/arch/zx48k/peephole/test_parser.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import unittest from src.arch.z80.peephole import parser diff --git a/tests/arch/zx48k/peephole/test_pattern.py b/tests/arch/zx48k/peephole/test_pattern.py index 078aecb96..e58395138 100644 --- a/tests/arch/zx48k/peephole/test_pattern.py +++ b/tests/arch/zx48k/peephole/test_pattern.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import unittest from src.arch.z80.peephole import pattern diff --git a/tests/arch/zx48k/peephole/test_template.py b/tests/arch/zx48k/peephole/test_template.py index 542b91e2b..9bb98eb44 100644 --- a/tests/arch/zx48k/peephole/test_template.py +++ b/tests/arch/zx48k/peephole/test_template.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import unittest from src.arch.z80.peephole import template diff --git a/tests/cmdline/__init__.py b/tests/cmdline/__init__.py index 69b0c613c..977095bad 100644 --- a/tests/cmdline/__init__.py +++ b/tests/cmdline/__init__.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import os import os.path import sys diff --git a/tests/cmdline/test_zxb.py b/tests/cmdline/test_zxb.py index dd4fe4399..e4f129ae9 100644 --- a/tests/cmdline/test_zxb.py +++ b/tests/cmdline/test_zxb.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import os import pytest diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 000000000..bdc012d15 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,3 @@ +# Initialize imports + +from src.api import check # noqa: F401 diff --git a/tests/functional/Makefile b/tests/functional/Makefile index 55a0ed282..88b37ca88 100644 --- a/tests/functional/Makefile +++ b/tests/functional/Makefile @@ -1,41 +1,41 @@ -# vim:noet:ts=4: -.PHONY: test test_ prepro asm bas all - -all: test test_ - -diffbas: - ./test.py -d '**/*.bas' - -test: prepro bin asm bas - -test_: - pytest . -n auto --no-cov -k "cmdline" - -prepro: - ./test.py zxbpp/*.bi - -asm: - ./test.py asm/*.asm - -# This includes all .bas BASIC programs having a corresponding .asm -bas: - ./test.py '**/*.bas' - -# This only includes all .bas BASIC programs having a -# corresponding .asm which starts by a digit. -basic_tests: - ./test.py '**/[0-9]*.bas' - -bin: - ./test.py '**/tzx_*.bas' '**/tap_*.bas' '**/sna_*.bas' '**/z80_*.bas' - -# Measures coverage using only basic tests -.PHONY: basic_coverage -basic_coverage: - ./coverage.sh [0-9]*.bas - coverage html -d basic_coverage - -.PHONY: coverage -coverage: - ./coverage.sh *.bas - coverage html -d coverage +# vim:noet:ts=4: +.PHONY: test test_ prepro asm bas all + +all: test test_ + +diffbas: + ./test.py -d '**/*.bas' + +test: prepro bin asm bas + +test_: + pytest . -n auto --no-cov -k "cmdline" + +prepro: + ./test.py zxbpp/*.bi + +asm: + ./test.py asm/*.asm + +# This includes all .bas BASIC programs having a corresponding .asm +bas: + ./test.py '**/*.bas' + +# This only includes all .bas BASIC programs having a +# corresponding .asm which starts by a digit. +basic_tests: + ./test.py '**/[0-9]*.bas' + +bin: + ./test.py '**/tzx_*.bas' '**/tap_*.bas' '**/sna_*.bas' '**/z80_*.bas' + +# Measures coverage using only basic tests +.PHONY: basic_coverage +basic_coverage: + ./coverage.sh [0-9]*.bas + coverage html -d basic_coverage + +.PHONY: coverage +coverage: + ./coverage.sh *.bas + coverage html -d coverage diff --git a/tests/functional/arch/zx48k/arr_elem_by_ref01.asm b/tests/functional/arch/zx48k/arr_elem_by_ref01.asm new file mode 100644 index 000000000..c413a82be --- /dev/null +++ b/tests/functional/arch/zx48k/arr_elem_by_ref01.asm @@ -0,0 +1,236 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFW .LABEL.__LABEL0 +_a.__DATA__.__PTR__: + DEFW _a.__DATA__ + DEFW 0 + DEFW 0 +_a.__DATA__: + DEFB 01h + DEFB 02h + DEFB 03h +.LABEL.__LABEL0: + DEFW 0000h + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 1 + push hl + ld hl, _a + call .core.__ARRAY + push hl + call _Incr + ld a, (_a.__DATA__ + 1) + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +_Incr: + push ix + ld ix, 0 + add ix, sp + ld h, (ix+5) + ld l, (ix+4) + ld a, (hl) + inc a + ld h, (ix+5) + ld l, (ix+4) + ld (hl), a +_Incr__leave: + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + ;; Performs a faster multiply for little 16bit numbs +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + LOCAL __MUL16LOOP + LOCAL __MUL16NOADD + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +__MUL16LOOP: + add hl, hl ; hl << 1 + sla c + rla ; a,c << 1 + jp nc, __MUL16NOADD + add hl, de +__MUL16NOADD: + djnz __MUL16LOOP + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + push namespace core +__FMUL16: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +1: + add hl, de + djnz 1b + ret + pop namespace +#line 20 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" +#line 24 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + push namespace core +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ;; HL <-- [HL] +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL TMP_ARR_PTR ; Ptr to Array DATA region. Stored temporarily + LOCAL LBOUND_PTR, UBOUND_PTR ; LBound and UBound PTR indexes + LOCAL RET_ADDR ; Contains the return address popped from the stack + LBOUND_PTR EQU 23698 ; Uses MEMBOT as a temporary variable + UBOUND_PTR EQU LBOUND_PTR + 2 ; Next 2 bytes for UBOUND PTR + RET_ADDR EQU UBOUND_PTR + 2 ; Next 2 bytes for RET_ADDR + TMP_ARR_PTR EQU RET_ADDR + 2 ; Next 2 bytes for TMP_ARR_PTR + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE <-- PTR to Dim sizes table + ld (TMP_ARR_PTR), hl ; HL = Array __DATA__.__PTR__ + inc hl + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __LBOUND__ PTR + ld (LBOUND_PTR), bc ; Store it for later +#line 66 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + ex de, hl ; HL <-- PTR to Dim sizes table, DE <-- dummy + ex (sp), hl ; Return address in HL, PTR Dim sizes table onto Stack + ld (RET_ADDR), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer to Dim sizes table + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Element Offset "accumulator" +LOOP: + ex de, hl ; DE = Element Offset + ld hl, (LBOUND_PTR) + ld a, h + or l + ld b, h + ld c, l + jr z, 1f + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (LBOUND_PTR), hl +1: + pop hl ; Get next index (Ai) from the stack + sbc hl, bc ; Subtract LBOUND +#line 116 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + add hl, de ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + dec bc ; Decrements loop counter + ld e, (hl) ; Loads next dimension size into D'E' + inc hl + ld d, (hl) + inc hl + push de + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FMUL16 ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 146 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 156 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start + ld de, (RET_ADDR) + push de + ret + ENDP + pop namespace +#line 44 "arch/zx48k/arr_elem_by_ref01.bas" + END diff --git a/tests/functional/arch/zx48k/arr_elem_by_ref01.bas b/tests/functional/arch/zx48k/arr_elem_by_ref01.bas new file mode 100644 index 000000000..d4e8fc9af --- /dev/null +++ b/tests/functional/arch/zx48k/arr_elem_by_ref01.bas @@ -0,0 +1,10 @@ +REM Test passing an array element by ref + +DIM a(2) As UByte => {1, 2, 3} + +SUB Incr(ByRef x As UByte) + LET x = x + 1 +END SUB + +Incr(a(1)) +POKE 0, a(1) diff --git a/tests/functional/arch/zx48k/arr_elem_by_ref02.asm b/tests/functional/arch/zx48k/arr_elem_by_ref02.asm new file mode 100644 index 000000000..a128f50be --- /dev/null +++ b/tests/functional/arch/zx48k/arr_elem_by_ref02.asm @@ -0,0 +1,840 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 4768 +.core.ZXBASIC_MEM_HEAP: + DEFS 4768 + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call _test + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +_Incr: + push ix + ld ix, 0 + add ix, sp + ld h, (ix+5) + ld l, (ix+4) + ld a, (hl) + inc a + ld h, (ix+5) + ld l, (ix+4) + ld (hl), a +_Incr__leave: + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret +_test: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + push hl + push hl + ld hl, .LABEL.__LABEL1 + push hl + ld hl, -6 + ld de, .LABEL.__LABEL0 + ld bc, 3 + call .core.__ALLOC_INITIALIZED_LOCAL_ARRAY + ld hl, 1 + push hl + push ix + pop hl + ld de, -6 + add hl, de + call .core.__ARRAY + push hl + call _Incr + ld l, (ix-4) + ld h, (ix-3) + inc hl + ld a, (hl) + ld (0), a +_test__leave: + ex af, af' + exx + ld l, (ix-4) + ld h, (ix-3) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + ;; Performs a faster multiply for little 16bit numbs +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + LOCAL __MUL16LOOP + LOCAL __MUL16NOADD + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +__MUL16LOOP: + add hl, hl ; hl << 1 + sla c + rla ; a,c << 1 + jp nc, __MUL16NOADD + add hl, de +__MUL16NOADD: + djnz __MUL16LOOP + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + push namespace core +__FMUL16: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +1: + add hl, de + djnz 1b + ret + pop namespace +#line 20 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" +#line 24 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + push namespace core +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ;; HL <-- [HL] +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL TMP_ARR_PTR ; Ptr to Array DATA region. Stored temporarily + LOCAL LBOUND_PTR, UBOUND_PTR ; LBound and UBound PTR indexes + LOCAL RET_ADDR ; Contains the return address popped from the stack + LBOUND_PTR EQU 23698 ; Uses MEMBOT as a temporary variable + UBOUND_PTR EQU LBOUND_PTR + 2 ; Next 2 bytes for UBOUND PTR + RET_ADDR EQU UBOUND_PTR + 2 ; Next 2 bytes for RET_ADDR + TMP_ARR_PTR EQU RET_ADDR + 2 ; Next 2 bytes for TMP_ARR_PTR + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE <-- PTR to Dim sizes table + ld (TMP_ARR_PTR), hl ; HL = Array __DATA__.__PTR__ + inc hl + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __LBOUND__ PTR + ld (LBOUND_PTR), bc ; Store it for later +#line 66 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + ex de, hl ; HL <-- PTR to Dim sizes table, DE <-- dummy + ex (sp), hl ; Return address in HL, PTR Dim sizes table onto Stack + ld (RET_ADDR), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer to Dim sizes table + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Element Offset "accumulator" +LOOP: + ex de, hl ; DE = Element Offset + ld hl, (LBOUND_PTR) + ld a, h + or l + ld b, h + ld c, l + jr z, 1f + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (LBOUND_PTR), hl +1: + pop hl ; Get next index (Ai) from the stack + sbc hl, bc ; Subtract LBOUND +#line 116 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + add hl, de ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + dec bc ; Decrements loop counter + ld e, (hl) ; Loads next dimension size into D'E' + inc hl + ld d, (hl) + inc hl + push de + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FMUL16 ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 146 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 156 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start + ld de, (RET_ADDR) + push de + ret + ENDP + pop namespace +#line 76 "arch/zx48k/arr_elem_by_ref02.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be freed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; __MEM_INIT must be called to initalize this library with the + ; standard parameters + ; --------------------------------------------------------------------- + push namespace core +__MEM_INIT: ; Initializes the library using (RAMTOP) as start, and + ld hl, ZXBASIC_MEM_HEAP ; Change this with other address of heap start + ld de, ZXBASIC_HEAP_SIZE ; Change this with your size + ; --------------------------------------------------------------------- + ; __MEM_INIT2 initalizes this library +; Parameters: +; HL : Memory address of 1st byte of the memory heap +; DE : Length in bytes of the Memory Heap + ; --------------------------------------------------------------------- +__MEM_INIT2: + ; HL as TOP + PROC + dec de + dec de + dec de + dec de ; DE = length - 4; HL = start + ; This is done, because we require 4 bytes for the empty dummy-header block + xor a + ld (hl), a + inc hl + ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 + inc hl + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block + ld (hl), c + inc hl + ld (hl), b + inc hl ; Pointer to next block + ld (hl), e + inc hl + ld (hl), d + inc hl ; Block size (should be length - 4 at start); This block contains all the available memory + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret + ENDP + pop namespace +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ; --------------------------------------------------------------------- + ; MEM_ALLOC + ; Allocates a block of memory in the heap. + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 + TEMP EQU TEMP0 + 1 + ld hl, 0 + ld (TEMP), hl +__MEM_START: + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start + inc bc + inc bc ; BC = BC + 2 ; block size needs 2 extra bytes for hidden pointer +__MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE + ld a, h ; HL = NULL (No memory available?) + or l +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ; HL = Pointer to Free block + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = Block Length + push hl ; HL = *pointer to -> next block + ex de, hl + or a ; CF = 0 + sbc hl, bc ; FREE >= BC (Length) (HL = BlockLength - Length) + jp nc, __MEM_DONE + pop hl + ld (TEMP), hl + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + jp __MEM_LOOP +__MEM_DONE: ; A free block has been found. + ; Check if at least 4 bytes remains free (HL >= 4) + push hl + exx ; exx to preserve bc + pop hl + ld bc, 4 + or a + sbc hl, bc + exx + jp nc, __MEM_SUBTRACT + ; At this point... + ; less than 4 bytes remains free. So we return this block entirely + ; We must link the previous block with the next to this one + ; (DE) => Pointer to next block + ; (TEMP) => &(previous->next) + pop hl ; Discard current block pointer + push de + ex de, hl ; DE = Previous block pointer; (HL) = Next block pointer + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex de, hl ; HL = Previous block pointer; DE = Next block pointer +TEMP0: + ld hl, 0 ; Pre-previous block pointer + ld (hl), e + inc hl + ld (hl), d ; LINKED + pop hl ; Returning block. + ret +__MEM_SUBTRACT: + ; At this point we have to store HL value (Length - BC) into (DE - 2) + ex de, hl + dec hl + ld (hl), d + dec hl + ld (hl), e ; Store new block length + add hl, de ; New length + DE => free-block start + pop de ; Remove previous HL off the stack + ld (hl), c ; Store length on its 1st word + inc hl + ld (hl), b + inc hl ; Return hl + ret + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" + ; --------------------------------------------------------------------- + ; MEM_CALLOC + ; Allocates a block of memory in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +__MEM_CALLOC: + push bc + call __MEM_ALLOC + pop bc + ld a, h + or l + ret z ; No memory + ld (hl), 0 + dec bc + ld a, b + or c + ret z ; Already filled (1 byte-length block) + ld d, h + ld e, l + inc de + push hl + ldir + pop hl + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- + push namespace core +__ALLOC_LOCAL_ARRAY: + push de + push ix + pop de + add hl, de ; hl = ix + hl + pop de + ld (hl), e + inc hl + ld (hl), d + inc hl + push hl + call __MEM_CALLOC + pop de + ex de, hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with data whose pointer (PTR) is in the stack + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] = PTR to the element area + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY: + push bc + call __ALLOC_LOCAL_ARRAY + pop bc + ;; Swaps [SP], [SP + 2] + exx + pop hl ; HL <- RET address + ex (sp), hl ; HL <- Data table, [SP] <- RET address + push hl ; [SP] <- Data table + exx + ex (sp), hl ; HL = Data table, (SP) = (IX + HL + 4) - start of array address lbound + ; HL = data table + ; BC = length + ; DE = new data area + ldir + pop hl ; HL = addr of LBound area if used + ret +#line 142 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + pop namespace +#line 77 "arch/zx48k/arr_elem_by_ref02.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- + push namespace core +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN + ld a, h + or l + ret z ; Return if NULL pointer + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous + or a ; Clear carry flag + sbc hl, bc ; Carry if BC > HL => This block if before + add hl, bc ; Restores HL, preserving Carry flag + jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) + inc hl + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 + call __MEM_JOIN_TEST + pop hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; + push hl ; Saves it for later + add hl, bc ; Adds its length. If HL == DE now, it must be joined + or a + sbc hl, de ; If Z, then HL == DE => We must join + pop hl + ret nz +__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC + push hl ; Saves it for later + ex de, hl + ld e, (hl) ; DE -> block->next->length + inc hl + ld d, (hl) + inc hl + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next + pop hl ; Recovers Pointer to block + ld (hl), c + inc hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved + ret + ENDP + pop namespace +#line 78 "arch/zx48k/arr_elem_by_ref02.bas" +.LABEL.__LABEL0: + DEFB 00h + DEFB 00h + DEFB 01h +.LABEL.__LABEL1: + DEFB 01h + DEFB 02h + DEFB 03h + END diff --git a/tests/functional/arch/zx48k/arr_elem_by_ref02.bas b/tests/functional/arch/zx48k/arr_elem_by_ref02.bas new file mode 100644 index 000000000..be588fc5f --- /dev/null +++ b/tests/functional/arch/zx48k/arr_elem_by_ref02.bas @@ -0,0 +1,14 @@ +REM Test passing an local array element by ref + + +SUB Incr(ByRef x As UByte) + LET x = x + 1 +END SUB + +SUB test + DIM a(2) As UByte => {1, 2, 3} + Incr(a(1)) + POKE 0, a(1) +END SUB + +test diff --git a/tests/functional/arch/zx48k/arr_elem_by_ref03.asm b/tests/functional/arch/zx48k/arr_elem_by_ref03.asm new file mode 100644 index 000000000..3ddbde69a --- /dev/null +++ b/tests/functional/arch/zx48k/arr_elem_by_ref03.asm @@ -0,0 +1,856 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 4768 +.core.ZXBASIC_MEM_HEAP: + DEFS 4768 + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call _test + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +_Incr: + push ix + ld ix, 0 + add ix, sp + ld h, (ix+5) + ld l, (ix+4) + ld a, (hl) + inc a + ld h, (ix+5) + ld l, (ix+4) + ld (hl), a +_Incr__leave: + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret +_ProxyFunc: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+4) + ld h, (ix+5) + push hl + call _Incr +_ProxyFunc__leave: + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret +_test: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + push hl + push hl + ld hl, .LABEL.__LABEL1 + push hl + ld hl, -6 + ld de, .LABEL.__LABEL0 + ld bc, 3 + call .core.__ALLOC_INITIALIZED_LOCAL_ARRAY + ld hl, 1 + push hl + push ix + pop hl + ld de, -6 + add hl, de + call .core.__ARRAY + push hl + call _ProxyFunc + ld l, (ix-4) + ld h, (ix-3) + inc hl + ld a, (hl) + ld (0), a +_test__leave: + ex af, af' + exx + ld l, (ix-4) + ld h, (ix-3) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + ;; Performs a faster multiply for little 16bit numbs +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + LOCAL __MUL16LOOP + LOCAL __MUL16NOADD + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +__MUL16LOOP: + add hl, hl ; hl << 1 + sla c + rla ; a,c << 1 + jp nc, __MUL16NOADD + add hl, de +__MUL16NOADD: + djnz __MUL16LOOP + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + push namespace core +__FMUL16: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +1: + add hl, de + djnz 1b + ret + pop namespace +#line 20 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" +#line 24 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + push namespace core +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ;; HL <-- [HL] +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL TMP_ARR_PTR ; Ptr to Array DATA region. Stored temporarily + LOCAL LBOUND_PTR, UBOUND_PTR ; LBound and UBound PTR indexes + LOCAL RET_ADDR ; Contains the return address popped from the stack + LBOUND_PTR EQU 23698 ; Uses MEMBOT as a temporary variable + UBOUND_PTR EQU LBOUND_PTR + 2 ; Next 2 bytes for UBOUND PTR + RET_ADDR EQU UBOUND_PTR + 2 ; Next 2 bytes for RET_ADDR + TMP_ARR_PTR EQU RET_ADDR + 2 ; Next 2 bytes for TMP_ARR_PTR + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE <-- PTR to Dim sizes table + ld (TMP_ARR_PTR), hl ; HL = Array __DATA__.__PTR__ + inc hl + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __LBOUND__ PTR + ld (LBOUND_PTR), bc ; Store it for later +#line 66 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + ex de, hl ; HL <-- PTR to Dim sizes table, DE <-- dummy + ex (sp), hl ; Return address in HL, PTR Dim sizes table onto Stack + ld (RET_ADDR), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer to Dim sizes table + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Element Offset "accumulator" +LOOP: + ex de, hl ; DE = Element Offset + ld hl, (LBOUND_PTR) + ld a, h + or l + ld b, h + ld c, l + jr z, 1f + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (LBOUND_PTR), hl +1: + pop hl ; Get next index (Ai) from the stack + sbc hl, bc ; Subtract LBOUND +#line 116 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + add hl, de ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + dec bc ; Decrements loop counter + ld e, (hl) ; Loads next dimension size into D'E' + inc hl + ld d, (hl) + inc hl + push de + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FMUL16 ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 146 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 156 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start + ld de, (RET_ADDR) + push de + ret + ENDP + pop namespace +#line 92 "arch/zx48k/arr_elem_by_ref03.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be freed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; __MEM_INIT must be called to initalize this library with the + ; standard parameters + ; --------------------------------------------------------------------- + push namespace core +__MEM_INIT: ; Initializes the library using (RAMTOP) as start, and + ld hl, ZXBASIC_MEM_HEAP ; Change this with other address of heap start + ld de, ZXBASIC_HEAP_SIZE ; Change this with your size + ; --------------------------------------------------------------------- + ; __MEM_INIT2 initalizes this library +; Parameters: +; HL : Memory address of 1st byte of the memory heap +; DE : Length in bytes of the Memory Heap + ; --------------------------------------------------------------------- +__MEM_INIT2: + ; HL as TOP + PROC + dec de + dec de + dec de + dec de ; DE = length - 4; HL = start + ; This is done, because we require 4 bytes for the empty dummy-header block + xor a + ld (hl), a + inc hl + ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 + inc hl + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block + ld (hl), c + inc hl + ld (hl), b + inc hl ; Pointer to next block + ld (hl), e + inc hl + ld (hl), d + inc hl ; Block size (should be length - 4 at start); This block contains all the available memory + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret + ENDP + pop namespace +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ; --------------------------------------------------------------------- + ; MEM_ALLOC + ; Allocates a block of memory in the heap. + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 + TEMP EQU TEMP0 + 1 + ld hl, 0 + ld (TEMP), hl +__MEM_START: + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start + inc bc + inc bc ; BC = BC + 2 ; block size needs 2 extra bytes for hidden pointer +__MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE + ld a, h ; HL = NULL (No memory available?) + or l +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ; HL = Pointer to Free block + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = Block Length + push hl ; HL = *pointer to -> next block + ex de, hl + or a ; CF = 0 + sbc hl, bc ; FREE >= BC (Length) (HL = BlockLength - Length) + jp nc, __MEM_DONE + pop hl + ld (TEMP), hl + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + jp __MEM_LOOP +__MEM_DONE: ; A free block has been found. + ; Check if at least 4 bytes remains free (HL >= 4) + push hl + exx ; exx to preserve bc + pop hl + ld bc, 4 + or a + sbc hl, bc + exx + jp nc, __MEM_SUBTRACT + ; At this point... + ; less than 4 bytes remains free. So we return this block entirely + ; We must link the previous block with the next to this one + ; (DE) => Pointer to next block + ; (TEMP) => &(previous->next) + pop hl ; Discard current block pointer + push de + ex de, hl ; DE = Previous block pointer; (HL) = Next block pointer + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex de, hl ; HL = Previous block pointer; DE = Next block pointer +TEMP0: + ld hl, 0 ; Pre-previous block pointer + ld (hl), e + inc hl + ld (hl), d ; LINKED + pop hl ; Returning block. + ret +__MEM_SUBTRACT: + ; At this point we have to store HL value (Length - BC) into (DE - 2) + ex de, hl + dec hl + ld (hl), d + dec hl + ld (hl), e ; Store new block length + add hl, de ; New length + DE => free-block start + pop de ; Remove previous HL off the stack + ld (hl), c ; Store length on its 1st word + inc hl + ld (hl), b + inc hl ; Return hl + ret + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" + ; --------------------------------------------------------------------- + ; MEM_CALLOC + ; Allocates a block of memory in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +__MEM_CALLOC: + push bc + call __MEM_ALLOC + pop bc + ld a, h + or l + ret z ; No memory + ld (hl), 0 + dec bc + ld a, b + or c + ret z ; Already filled (1 byte-length block) + ld d, h + ld e, l + inc de + push hl + ldir + pop hl + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- + push namespace core +__ALLOC_LOCAL_ARRAY: + push de + push ix + pop de + add hl, de ; hl = ix + hl + pop de + ld (hl), e + inc hl + ld (hl), d + inc hl + push hl + call __MEM_CALLOC + pop de + ex de, hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with data whose pointer (PTR) is in the stack + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] = PTR to the element area + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY: + push bc + call __ALLOC_LOCAL_ARRAY + pop bc + ;; Swaps [SP], [SP + 2] + exx + pop hl ; HL <- RET address + ex (sp), hl ; HL <- Data table, [SP] <- RET address + push hl ; [SP] <- Data table + exx + ex (sp), hl ; HL = Data table, (SP) = (IX + HL + 4) - start of array address lbound + ; HL = data table + ; BC = length + ; DE = new data area + ldir + pop hl ; HL = addr of LBound area if used + ret +#line 142 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + pop namespace +#line 93 "arch/zx48k/arr_elem_by_ref03.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- + push namespace core +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN + ld a, h + or l + ret z ; Return if NULL pointer + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous + or a ; Clear carry flag + sbc hl, bc ; Carry if BC > HL => This block if before + add hl, bc ; Restores HL, preserving Carry flag + jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) + inc hl + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 + call __MEM_JOIN_TEST + pop hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; + push hl ; Saves it for later + add hl, bc ; Adds its length. If HL == DE now, it must be joined + or a + sbc hl, de ; If Z, then HL == DE => We must join + pop hl + ret nz +__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC + push hl ; Saves it for later + ex de, hl + ld e, (hl) ; DE -> block->next->length + inc hl + ld d, (hl) + inc hl + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next + pop hl ; Recovers Pointer to block + ld (hl), c + inc hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved + ret + ENDP + pop namespace +#line 94 "arch/zx48k/arr_elem_by_ref03.bas" +.LABEL.__LABEL0: + DEFB 00h + DEFB 00h + DEFB 01h +.LABEL.__LABEL1: + DEFB 01h + DEFB 02h + DEFB 03h + END diff --git a/tests/functional/arch/zx48k/arr_elem_by_ref03.bas b/tests/functional/arch/zx48k/arr_elem_by_ref03.bas new file mode 100644 index 000000000..6ee818e06 --- /dev/null +++ b/tests/functional/arch/zx48k/arr_elem_by_ref03.bas @@ -0,0 +1,18 @@ +REM Test passing an local array element by param + + +SUB Incr(ByRef x As UByte) + LET x = x + 1 +END SUB + +SUB ProxyFunc(ByRef x as UByte) + Incr(x) +END SUB + +SUB test + DIM a(2) As UByte => {1, 2, 3} + ProxyFunc(a(1)) + POKE 0, a(1) +END SUB + +test diff --git a/tests/functional/arch/zx48k/array13.asm b/tests/functional/arch/zx48k/array13.asm new file mode 100644 index 000000000..31b7279f8 --- /dev/null +++ b/tests/functional/arch/zx48k/array13.asm @@ -0,0 +1,237 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_array: + DEFW .LABEL.__LABEL0 +_array.__DATA__.__PTR__: + DEFW _array.__DATA__ + DEFW _array.__LBOUND__ + DEFW 0 +_array.__DATA__: + DEFB 00h + DEFB 00h +.LABEL.__LABEL0: + DEFW 0000h + DEFB 01h +_array.__LBOUND__: + DEFW 0001h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, _array + push hl + call _test + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +_test: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + ld l, (ix-2) + ld h, (ix-1) + push hl + push ix + pop hl + ld de, 4 + add hl, de + call .core.__ARRAY_PTR + ld a, (hl) + ld (0), a +_test__leave: + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + ;; Performs a faster multiply for little 16bit numbs +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + LOCAL __MUL16LOOP + LOCAL __MUL16NOADD + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +__MUL16LOOP: + add hl, hl ; hl << 1 + sla c + rla ; a,c << 1 + jp nc, __MUL16NOADD + add hl, de +__MUL16NOADD: + djnz __MUL16LOOP + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + push namespace core +__FMUL16: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +1: + add hl, de + djnz 1b + ret + pop namespace +#line 20 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" +#line 24 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + push namespace core +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ;; HL <-- [HL] +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL TMP_ARR_PTR ; Ptr to Array DATA region. Stored temporarily + LOCAL LBOUND_PTR, UBOUND_PTR ; LBound and UBound PTR indexes + LOCAL RET_ADDR ; Contains the return address popped from the stack + LBOUND_PTR EQU 23698 ; Uses MEMBOT as a temporary variable + UBOUND_PTR EQU LBOUND_PTR + 2 ; Next 2 bytes for UBOUND PTR + RET_ADDR EQU UBOUND_PTR + 2 ; Next 2 bytes for RET_ADDR + TMP_ARR_PTR EQU RET_ADDR + 2 ; Next 2 bytes for TMP_ARR_PTR + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE <-- PTR to Dim sizes table + ld (TMP_ARR_PTR), hl ; HL = Array __DATA__.__PTR__ + inc hl + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __LBOUND__ PTR + ld (LBOUND_PTR), bc ; Store it for later +#line 66 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + ex de, hl ; HL <-- PTR to Dim sizes table, DE <-- dummy + ex (sp), hl ; Return address in HL, PTR Dim sizes table onto Stack + ld (RET_ADDR), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer to Dim sizes table + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Element Offset "accumulator" +LOOP: + ex de, hl ; DE = Element Offset + ld hl, (LBOUND_PTR) + ld a, h + or l + ld b, h + ld c, l + jr z, 1f + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (LBOUND_PTR), hl +1: + pop hl ; Get next index (Ai) from the stack + sbc hl, bc ; Subtract LBOUND +#line 116 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + add hl, de ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + dec bc ; Decrements loop counter + ld e, (hl) ; Loads next dimension size into D'E' + inc hl + ld d, (hl) + inc hl + push de + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FMUL16 ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 146 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 156 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start + ld de, (RET_ADDR) + push de + ret + ENDP + pop namespace +#line 44 "arch/zx48k/array13.bas" + END diff --git a/tests/functional/arch/zx48k/array13.bas b/tests/functional/arch/zx48k/array13.bas new file mode 100644 index 000000000..4669b5c44 --- /dev/null +++ b/tests/functional/arch/zx48k/array13.bas @@ -0,0 +1,9 @@ +SUB test(arr() As UByte) + DIM i As UInteger + POKE 0, arr(i) +END SUB + +DIM array(1 TO 2) As UByte + +test array + diff --git a/tests/functional/arch/zx48k/bool_crash.asm b/tests/functional/arch/zx48k/bool_crash.asm index 342deb639..0c3f73144 100644 --- a/tests/functional/arch/zx48k/bool_crash.asm +++ b/tests/functional/arch/zx48k/bool_crash.asm @@ -38,7 +38,7 @@ _ReadKeys: push ix ld ix, 0 add ix, sp - pop af + ld a, (_pldx) ld hl, (_pldx - 1) or h jp z, .LABEL.__LABEL1 diff --git a/tests/functional/arch/zx48k/border00_IC.ic b/tests/functional/arch/zx48k/border00_IC.ic index 14b733047..7031602f2 100644 --- a/tests/functional/arch/zx48k/border00_IC.ic +++ b/tests/functional/arch/zx48k/border00_IC.ic @@ -1,4 +1,4 @@ -('fparamu8', '7') -('call', '.core.BORDER', '0') -('end', '0') -('inline', ';; --- end of user code ---') +('fparamu8', '7') +('call', '.core.BORDER', '0') +('end', '0') +('inline', ';; --- end of user code ---') diff --git a/tests/functional/arch/zx48k/bound02.asm b/tests/functional/arch/zx48k/bound02.asm index 9ea2a3a0c..57b502714 100644 --- a/tests/functional/arch/zx48k/bound02.asm +++ b/tests/functional/arch/zx48k/bound02.asm @@ -24,7 +24,7 @@ _a: DEFW .LABEL.__LABEL0 _a.__DATA__.__PTR__: DEFW _a.__DATA__ - DEFW 0 + DEFW _a.__LBOUND__ DEFW 0 _a.__DATA__: DEFB 00h @@ -87,6 +87,9 @@ _a.__DATA__: DEFW 0001h DEFW 0004h DEFB 02h +_a.__LBOUND__: + DEFW 0002h + DEFW 0003h .core.ZXBASIC_USER_DATA_END: .core.__MAIN_PROGRAM__: ld a, 1 diff --git a/tests/functional/arch/zx48k/bxor16.asm b/tests/functional/arch/zx48k/bxor16.asm index b92400d85..e3a12b068 100644 --- a/tests/functional/arch/zx48k/bxor16.asm +++ b/tests/functional/arch/zx48k/bxor16.asm @@ -30,8 +30,9 @@ _b: call .core.__BXOR16 ld a, l ld (_b), a + ld de, 65535 ld hl, (_a) - call .core.__NEGHL + call .core.__BXOR16 ld a, l ld (_b), a ld hl, (_a) @@ -42,8 +43,9 @@ _b: call .core.__BXOR16 ld a, l ld (_b), a + ld de, 65535 ld hl, (_a) - call .core.__NEGHL + call .core.__BXOR16 ld a, l ld (_b), a ld de, (_a) @@ -84,22 +86,5 @@ __BXOR16: ld l, a ret pop namespace -#line 46 "arch/zx48k/bxor16.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/neg16.asm" - ; Negates HL value (16 bit) - push namespace core -__ABS16: - bit 7, h - ret z -__NEGHL: - ld a, l ; HL = -HL - cpl - ld l, a - ld a, h - cpl - ld h, a - inc hl - ret - pop namespace -#line 47 "arch/zx48k/bxor16.bas" +#line 48 "arch/zx48k/bxor16.bas" END diff --git a/tests/functional/arch/zx48k/cast_const_to_int.asm b/tests/functional/arch/zx48k/cast_const_to_int.asm new file mode 100644 index 000000000..2ccdf1e0a --- /dev/null +++ b/tests/functional/arch/zx48k/cast_const_to_int.asm @@ -0,0 +1,232 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_var_neg: + DEFB 00h + DEFB 80h + DEFB 0FEh + DEFB 0FFh +_var: + DEFB 00h + DEFB 80h + DEFB 01h + DEFB 00h +_var_neg_fl: + DEFB 81h + DEFB 0C0h + DEFB 00h + DEFB 00h + DEFB 00h +_r_int: + DEFB 00, 00 +_r_long: + DEFB 00, 00, 00, 00 +_r_ulong: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_var_neg) + ld de, (_var_neg + 2) + ex de, hl + ld (_r_int), hl + ld hl, 65534 + ld (_r_int), hl + ld hl, (_var_neg) + ld de, (_var_neg + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_r_long), hl + ld (_r_long + 2), de + ld hl, (_var_neg) + ld de, (_var_neg + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_r_ulong), hl + ld (_r_ulong + 2), de + ld hl, (_var) + ld de, (_var + 2) + ex de, hl + ld (_r_int), hl + ld hl, 1 + ld (_r_int), hl + ld hl, (_var) + ld de, (_var + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_r_long), hl + ld (_r_long + 2), de + ld hl, (_var) + ld de, (_var + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_r_ulong), hl + ld (_r_ulong + 2), de + ld a, (_var_neg_fl) + ld de, (_var_neg_fl + 1) + ld bc, (_var_neg_fl + 3) + call .core.__FTOU32REG + ld (_r_int), hl + ld hl, 65534 + ld (_r_int), hl + ld a, (_var_neg_fl) + ld de, (_var_neg_fl + 1) + ld bc, (_var_neg_fl + 3) + call .core.__FTOU32REG + ld (_r_long), hl + ld (_r_long + 2), de + ld a, (_var_neg_fl) + ld de, (_var_neg_fl + 1) + ld bc, (_var_neg_fl + 3) + call .core.__FTOU32REG + ld (_r_ulong), hl + ld (_r_ulong + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/ftou32reg.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/ftou32reg.asm" + push namespace core +__FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS 32 bit signed) + ; Input FP number in A EDCB (A exponent, EDCB mantissa) + ; Output: DEHL 32 bit number (signed) + PROC + LOCAL __IS_FLOAT + LOCAL __NEGATE + or a + jr nz, __IS_FLOAT + ; Here if it is a ZX ROM Integer + ld h, c + ld l, d + ld d, e + ret +__IS_FLOAT: ; Jumps here if it is a true floating point number + ld h, e + push hl ; Stores it for later (Contains Sign in H) + push de + push bc + exx + pop de ; Loads mantissa into C'B' E'D' + pop bc ; + set 7, c ; Highest mantissa bit is always 1 + exx + ld hl, 0 ; DEHL = 0 + ld d, h + ld e, l + ;ld a, c ; Get exponent + sub 128 ; Exponent -= 128 + jr z, __FTOU32REG_END ; If it was <= 128, we are done (Integers must be > 128) + jr c, __FTOU32REG_END ; It was decimal (0.xxx). We are done (return 0) + ld b, a ; Loop counter = exponent - 128 +__FTOU32REG_LOOP: + exx ; Shift C'B' E'D' << 1, output bit stays in Carry + sla d + rl e + rl b + rl c + exx ; Shift DEHL << 1, inserting the carry on the right + rl l + rl h + rl e + rl d + djnz __FTOU32REG_LOOP +__FTOU32REG_END: + pop af ; Take the sign bit + or a ; Sets SGN bit to 1 if negative + jp m, __NEGATE ; Negates DEHL + ret +__NEGATE: + exx + ld a, d + or e + or b + or c + exx + jr z, __END + inc l + jr nz, __END + inc h + jr nz, __END + inc de + LOCAL __END +__END: + jp __NEG32 + ENDP +__FTOU8: ; Converts float in C ED LH to Unsigned byte in A + call __FTOU32REG + ld a, l + ret + pop namespace +#line 92 "arch/zx48k/cast_const_to_int.bas" + END diff --git a/tests/functional/arch/zx48k/cast_const_to_int.bas b/tests/functional/arch/zx48k/cast_const_to_int.bas new file mode 100644 index 000000000..25d7b2b04 --- /dev/null +++ b/tests/functional/arch/zx48k/cast_const_to_int.bas @@ -0,0 +1,25 @@ +dim var_neg as fixed = -1.5 +const const_neg as fixed = -1.5 +dim var as Fixed = 1.5 +const const_ as fixed = 1.5 +dim var_neg_fl as Float = -1.5 +const const_neg_fl as Float = -1.5 + +DIM r_int as Integer +DIM r_long as Long +DIM r_ulong as ULong + +LET r_int = cast(integer, var_neg) +LET r_int = cast(integer, const_neg) +LET r_long = cast(long, var_neg) +LET r_ulong = cast(ulong, var_neg) + +LET r_int = cast(integer, var) +LET r_int = cast(integer, const_) +LET r_long = cast(long, var) +LET r_ulong = cast(ulong, var) + +LET r_int = cast(integer, var_neg_fl) +LET r_int = cast(integer, const_neg_fl) +LET r_long = cast(long, var_neg_fl) +LET r_ulong = cast(ulong, var_neg_fl) diff --git a/tests/functional/arch/zx48k/cast_f16_to_long.asm b/tests/functional/arch/zx48k/cast_f16_to_long.asm new file mode 100644 index 000000000..c83500765 --- /dev/null +++ b/tests/functional/arch/zx48k/cast_f16_to_long.asm @@ -0,0 +1,54 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_v1: + DEFB 00h + DEFB 80h + DEFB 01h + DEFB 00h +_v2: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_v1) + ld de, (_v1 + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_v2), hl + ld (_v2 + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx48k/cast_f16_to_long.bas b/tests/functional/arch/zx48k/cast_f16_to_long.bas new file mode 100644 index 000000000..b8f1315d7 --- /dev/null +++ b/tests/functional/arch/zx48k/cast_f16_to_long.bas @@ -0,0 +1,5 @@ +DIM v1 As Fixed = 1.5 +DIM v2 As Long + +v2 = v1 + diff --git a/tests/functional/arch/zx48k/cast_f16_to_ulong.asm b/tests/functional/arch/zx48k/cast_f16_to_ulong.asm new file mode 100644 index 000000000..c83500765 --- /dev/null +++ b/tests/functional/arch/zx48k/cast_f16_to_ulong.asm @@ -0,0 +1,54 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_v1: + DEFB 00h + DEFB 80h + DEFB 01h + DEFB 00h +_v2: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_v1) + ld de, (_v1 + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_v2), hl + ld (_v2 + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx48k/cast_f16_to_ulong.bas b/tests/functional/arch/zx48k/cast_f16_to_ulong.bas new file mode 100644 index 000000000..e805c2240 --- /dev/null +++ b/tests/functional/arch/zx48k/cast_f16_to_ulong.bas @@ -0,0 +1,5 @@ +DIM v1 As Fixed = 1.5 +DIM v2 As ULong + +v2 = v1 + diff --git a/tests/functional/arch/zx48k/dim_at0.bas b/tests/functional/arch/zx48k/dim_at0.bas index 774c6b6fd..551aa5165 100644 --- a/tests/functional/arch/zx48k/dim_at0.bas +++ b/tests/functional/arch/zx48k/dim_at0.bas @@ -1,3 +1,5 @@ +declare sub test2 + sub test1() DIM dummy as Uinteger = @test2 end sub diff --git a/tests/functional/arch/zx48k/dim_at1.asm b/tests/functional/arch/zx48k/dim_at1.asm new file mode 100644 index 000000000..37ba88869 --- /dev/null +++ b/tests/functional/arch/zx48k/dim_at1.asm @@ -0,0 +1,57 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +_test1: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + ld hl, _test2 + ld (ix-2), l + ld (ix-1), h +_test1__leave: + ld sp, ix + pop ix + ret +_test2: + push ix + ld ix, 0 + add ix, sp +_test2__leave: + ld sp, ix + pop ix + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx48k/dim_at1.bas b/tests/functional/arch/zx48k/dim_at1.bas new file mode 100644 index 000000000..774c6b6fd --- /dev/null +++ b/tests/functional/arch/zx48k/dim_at1.bas @@ -0,0 +1,6 @@ +sub test1() + DIM dummy as Uinteger = @test2 +end sub + +sub test2 +end sub diff --git a/tests/functional/arch/zx48k/dimconst2.bas b/tests/functional/arch/zx48k/dimconst2.bas index 40bf15053..4a2bd3400 100644 --- a/tests/functional/arch/zx48k/dimconst2.bas +++ b/tests/functional/arch/zx48k/dimconst2.bas @@ -1,6 +1,5 @@ +Map: Function q DIM q as Uinteger = @Map End Function - -Map: diff --git a/tests/functional/arch/zx48k/dimconst2b.asm b/tests/functional/arch/zx48k/dimconst2b.asm new file mode 100644 index 000000000..cc5304501 --- /dev/null +++ b/tests/functional/arch/zx48k/dimconst2b.asm @@ -0,0 +1,62 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_Map: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, 2 + ld (_Map), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +_q: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + push ix + pop hl + ld bc, -2 + add hl, bc + ex de, hl + ld hl, .LABEL.__LABEL0 + ld bc, 2 + ldir + ld a, 1 + ld (_Map), a +_q__leave: + ld sp, ix + pop ix + ret + ;; --- end of user code --- +.LABEL.__LABEL0: + DEFW _Map + END diff --git a/tests/functional/arch/zx48k/dimconst2b.bas b/tests/functional/arch/zx48k/dimconst2b.bas index ac72a71da..74f1e4f02 100644 --- a/tests/functional/arch/zx48k/dimconst2b.bas +++ b/tests/functional/arch/zx48k/dimconst2b.bas @@ -1,6 +1,6 @@ +Map = 2 + Function q DIM q as Uinteger = @Map Map = 1 End Function - -Map = 2 diff --git a/tests/functional/arch/zx48k/dimconst2c.bas b/tests/functional/arch/zx48k/dimconst2c.bas index 8729561d4..c4f87b10f 100644 --- a/tests/functional/arch/zx48k/dimconst2c.bas +++ b/tests/functional/arch/zx48k/dimconst2c.bas @@ -1,5 +1,5 @@ +Let Map = 1 + Function q DIM q as Uinteger = @Map End Function - -Let Map = 1 diff --git a/tests/functional/arch/zx48k/dimconst2d.bas b/tests/functional/arch/zx48k/dimconst2d.bas index 2554fa064..98c175ccf 100644 --- a/tests/functional/arch/zx48k/dimconst2d.bas +++ b/tests/functional/arch/zx48k/dimconst2d.bas @@ -1,5 +1,5 @@ +Dim Map = 1 + Function q DIM q as Uinteger = @Map End Function - -Dim Map = 1 diff --git a/tests/functional/arch/zx48k/dimconst2f.asm b/tests/functional/arch/zx48k/dimconst2f.asm new file mode 100644 index 000000000..6c23bcac0 --- /dev/null +++ b/tests/functional/arch/zx48k/dimconst2f.asm @@ -0,0 +1,50 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: +.LABEL._Map: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +_q: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + ld hl, .LABEL._Map + ld (ix-2), l + ld (ix-1), h +_q__leave: + ld sp, ix + pop ix + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx48k/dimconst2f.bas b/tests/functional/arch/zx48k/dimconst2f.bas new file mode 100644 index 000000000..40bf15053 --- /dev/null +++ b/tests/functional/arch/zx48k/dimconst2f.bas @@ -0,0 +1,6 @@ + +Function q + DIM q as Uinteger = @Map +End Function + +Map: diff --git a/tests/functional/arch/zx48k/dimconst2g.asm b/tests/functional/arch/zx48k/dimconst2g.asm new file mode 100644 index 000000000..568a7700c --- /dev/null +++ b/tests/functional/arch/zx48k/dimconst2g.asm @@ -0,0 +1,59 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_Map: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, 2 + ld (_Map), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +_q: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + push hl + inc sp + push ix + pop hl + ld de, -1 + add hl, de + ld (ix-3), l + ld (ix-2), h + ld (ix-1), 1 +_q__leave: + ld sp, ix + pop ix + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx48k/dimconst2g.bas b/tests/functional/arch/zx48k/dimconst2g.bas new file mode 100644 index 000000000..0e92770c4 --- /dev/null +++ b/tests/functional/arch/zx48k/dimconst2g.bas @@ -0,0 +1,7 @@ + +Function q + DIM q as Uinteger = @Map + Map = 1 +End Function + +Map = 2 diff --git a/tests/functional/arch/zx48k/dimconst2h.asm b/tests/functional/arch/zx48k/dimconst2h.asm new file mode 100644 index 000000000..e059d96a8 --- /dev/null +++ b/tests/functional/arch/zx48k/dimconst2h.asm @@ -0,0 +1,51 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_Map: + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +_q: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + ld hl, _Map + ld (ix-2), l + ld (ix-1), h +_q__leave: + ld sp, ix + pop ix + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx48k/dimconst2h.bas b/tests/functional/arch/zx48k/dimconst2h.bas new file mode 100644 index 000000000..1a4d33db9 --- /dev/null +++ b/tests/functional/arch/zx48k/dimconst2h.bas @@ -0,0 +1,6 @@ + +Function q + DIM q as Uinteger = @Map +End Function + +Dim Map = 1 diff --git a/tests/functional/arch/zx48k/dimconst2i.asm b/tests/functional/arch/zx48k/dimconst2i.asm new file mode 100644 index 000000000..d81572224 --- /dev/null +++ b/tests/functional/arch/zx48k/dimconst2i.asm @@ -0,0 +1,53 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_Map: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, 1 + ld (_Map), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +_q: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + ld hl, _Map + ld (ix-2), l + ld (ix-1), h +_q__leave: + ld sp, ix + pop ix + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx48k/dimconst2i.bas b/tests/functional/arch/zx48k/dimconst2i.bas new file mode 100644 index 000000000..2829da970 --- /dev/null +++ b/tests/functional/arch/zx48k/dimconst2i.bas @@ -0,0 +1,6 @@ + +Function q + DIM q as Uinteger = @Map +End Function + +Let Map = 1 diff --git a/tests/functional/arch/zx48k/fact.asm b/tests/functional/arch/zx48k/fact.asm index 2367e493a..e666c4249 100644 --- a/tests/functional/arch/zx48k/fact.asm +++ b/tests/functional/arch/zx48k/fact.asm @@ -185,7 +185,6 @@ __TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) ld l, c ret pop namespace - f #line 83 "arch/zx48k/fact.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" ; SUB32 diff --git a/tests/functional/arch/zx48k/func_call_IC.ic b/tests/functional/arch/zx48k/func_call_IC.ic index dbee01323..b061fa1b3 100644 --- a/tests/functional/arch/zx48k/func_call_IC.ic +++ b/tests/functional/arch/zx48k/func_call_IC.ic @@ -1,17 +1,17 @@ -('paramf', '0.0') -('call', '_test', '0') -('call', '.core.__MEM_FREE', '0') -('end', '0') -('label', '_test') -('enter', '0') -('ploadf', 't3', '0') -('jzerof', 't3', '.LABEL.__LABEL1') -('retstr', '#.LABEL.__LABEL2', '_test__leave') -('label', '.LABEL.__LABEL1') -('inline', '#line 3 "func_call_IC.bas"') -('inline', '\nld hl, 0\n') -('inline', '#line 6 "func_call_IC.bas"') -('label', '_test__leave') -('leave', '6') -('vard', '.LABEL.__LABEL2', "['0008', '6E', '6F', '74', '20', '7A', '65', '72', '6F']") -('inline', ';; --- end of user code ---') +('paramf', '0.0') +('call', '_test', '0') +('call', '.core.__MEM_FREE', '0') +('end', '0') +('label', '_test') +('enter', '0') +('ploadf', 't3', '0') +('jzerof', 't3', '.LABEL.__LABEL1') +('retstr', '#.LABEL.__LABEL2', '_test__leave') +('label', '.LABEL.__LABEL1') +('inline', '#line 3 "func_call_IC.bas"') +('inline', '\nld hl, 0\n') +('inline', '#line 6 "func_call_IC.bas"') +('label', '_test__leave') +('leave', '6') +('vard', '.LABEL.__LABEL2', "['0008', '6E', '6F', '74', '20', '7A', '65', '72', '6F']") +('inline', ';; --- end of user code ---') diff --git a/tests/functional/arch/zx48k/lbound10.asm b/tests/functional/arch/zx48k/lbound10.asm index f4e5954a6..1ef72f5a2 100644 --- a/tests/functional/arch/zx48k/lbound10.asm +++ b/tests/functional/arch/zx48k/lbound10.asm @@ -83,10 +83,14 @@ _test3: push hl push hl push hl + ld hl, 0 + push hl + ld hl, _test3.a.__LBOUND__ + push hl ld hl, -6 ld de, .LABEL.__LABEL5 ld bc, 9 - call .core.__ALLOC_LOCAL_ARRAY + call .core.__ALLOC_LOCAL_ARRAY_WITH_BOUNDS push ix pop hl ld de, -6 @@ -104,9 +108,12 @@ _test3__leave: ld sp, ix pop ix ret +_test3.a.__LBOUND__: + DEFW 0003h + DEFW 0007h ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -117,7 +124,7 @@ _test3__leave: ; closed source programs). ; ; Please read the MIT license on the internet -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -211,8 +218,8 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/heapinit.asm" +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -319,7 +326,7 @@ __MEM_INIT2: ret ENDP pop namespace -#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -350,9 +357,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ret z ; NULL -#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -417,7 +424,7 @@ __MEM_SUBTRACT: ret ENDP pop namespace -#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; --------------------------------------------------------------------- ; MEM_CALLOC ; Allocates a block of memory in the heap, and clears it filling it @@ -515,9 +522,69 @@ __ALLOC_INITIALIZED_LOCAL_ARRAY: ldir pop hl ; HL = addr of LBound area if used ret + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes. Then sets LBOUND and UBOUND ptrs + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] PTR to the lbound element area + ; [SP + 4] PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS: + call __ALLOC_LOCAL_ARRAY +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS2: + pop bc ;; ret address + pop de ;; lbound + inc hl + ld (hl), e + inc hl + ld (hl), d + pop de ;; PTR to ubound table + push bc ;; puts ret address back + ld a, d + or e + ret z ;; if PTR for UBound is 0, it's not used + inc hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; TOP of the stack = PTR to the element area + ; [SP + 2] = PTR to the element area + ; [SP + 4] = PTR to the lbound element area + ; [SP + 6] = PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS: + ;; Swaps [SP] and [SP + 2] + exx + pop hl ;; Ret address + ex (sp), hl ;; HL <- PTR to Element area, (sp) = Ret address + push hl ;; [SP] = PTR to element area, [SP + 2] = Ret address + exx + call __ALLOC_INITIALIZED_LOCAL_ARRAY + jp __ALLOC_LOCAL_ARRAY_WITH_BOUNDS2 #line 142 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" pop namespace -#line 80 "arch/zx48k/lbound10.bas" +#line 87 "arch/zx48k/lbound10.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arraybound.asm" ; --------------------------------------------------------- ; Copyleft (k)2011 by Jose Rodriguez (a.k.a. Boriel) @@ -595,8 +662,8 @@ __DIM_NOT_EXIST: ret ENDP pop namespace -#line 81 "arch/zx48k/lbound10.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/free.asm" +#line 88 "arch/zx48k/lbound10.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -754,7 +821,7 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ret ENDP pop namespace -#line 82 "arch/zx48k/lbound10.bas" +#line 89 "arch/zx48k/lbound10.bas" .LABEL.__LABEL5: DEFB 01h DEFB 00h diff --git a/tests/functional/arch/zx48k/lbound11.asm b/tests/functional/arch/zx48k/lbound11.asm index 33792028f..a76677b29 100644 --- a/tests/functional/arch/zx48k/lbound11.asm +++ b/tests/functional/arch/zx48k/lbound11.asm @@ -99,10 +99,14 @@ _test3: push hl push hl push hl + ld hl, 0 + push hl + ld hl, _test3.a.__LBOUND__ + push hl ld hl, -6 ld de, .LABEL.__LABEL5 ld bc, 9 - call .core.__ALLOC_LOCAL_ARRAY + call .core.__ALLOC_LOCAL_ARRAY_WITH_BOUNDS push ix pop hl ld de, -6 @@ -120,9 +124,12 @@ _test3__leave: ld sp, ix pop ix ret +_test3.a.__LBOUND__: + DEFW 0003h + DEFW 0007h ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -133,7 +140,7 @@ _test3__leave: ; closed source programs). ; ; Please read the MIT license on the internet -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -227,8 +234,8 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/heapinit.asm" +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -335,7 +342,7 @@ __MEM_INIT2: ret ENDP pop namespace -#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -366,9 +373,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ret z ; NULL -#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -433,7 +440,7 @@ __MEM_SUBTRACT: ret ENDP pop namespace -#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; --------------------------------------------------------------------- ; MEM_CALLOC ; Allocates a block of memory in the heap, and clears it filling it @@ -531,9 +538,69 @@ __ALLOC_INITIALIZED_LOCAL_ARRAY: ldir pop hl ; HL = addr of LBound area if used ret + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes. Then sets LBOUND and UBOUND ptrs + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] PTR to the lbound element area + ; [SP + 4] PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS: + call __ALLOC_LOCAL_ARRAY +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS2: + pop bc ;; ret address + pop de ;; lbound + inc hl + ld (hl), e + inc hl + ld (hl), d + pop de ;; PTR to ubound table + push bc ;; puts ret address back + ld a, d + or e + ret z ;; if PTR for UBound is 0, it's not used + inc hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; TOP of the stack = PTR to the element area + ; [SP + 2] = PTR to the element area + ; [SP + 4] = PTR to the lbound element area + ; [SP + 6] = PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS: + ;; Swaps [SP] and [SP + 2] + exx + pop hl ;; Ret address + ex (sp), hl ;; HL <- PTR to Element area, (sp) = Ret address + push hl ;; [SP] = PTR to element area, [SP + 2] = Ret address + exx + call __ALLOC_INITIALIZED_LOCAL_ARRAY + jp __ALLOC_LOCAL_ARRAY_WITH_BOUNDS2 #line 142 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" pop namespace -#line 96 "arch/zx48k/lbound11.bas" +#line 103 "arch/zx48k/lbound11.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arraybound.asm" ; --------------------------------------------------------- ; Copyleft (k)2011 by Jose Rodriguez (a.k.a. Boriel) @@ -611,8 +678,8 @@ __DIM_NOT_EXIST: ret ENDP pop namespace -#line 97 "arch/zx48k/lbound11.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/free.asm" +#line 104 "arch/zx48k/lbound11.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -770,7 +837,7 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ret ENDP pop namespace -#line 98 "arch/zx48k/lbound11.bas" +#line 105 "arch/zx48k/lbound11.bas" .LABEL.__LABEL5: DEFB 01h DEFB 00h diff --git a/tests/functional/arch/zx48k/lbound12.asm b/tests/functional/arch/zx48k/lbound12.asm index 04098b8bf..4102e8e2a 100644 --- a/tests/functional/arch/zx48k/lbound12.asm +++ b/tests/functional/arch/zx48k/lbound12.asm @@ -83,10 +83,14 @@ _test1: push hl push hl push hl + ld hl, 0 + push hl + ld hl, _test1.a1.__LBOUND__ + push hl ld hl, -6 ld de, .LABEL.__LABEL5 ld bc, 18 - call .core.__ALLOC_LOCAL_ARRAY + call .core.__ALLOC_LOCAL_ARRAY_WITH_BOUNDS push ix pop hl ld de, -6 @@ -106,6 +110,9 @@ _test1__leave: ld sp, ix pop ix ret +_test1.a1.__LBOUND__: + DEFW 0003h + DEFW 0007h _test2: push ix ld ix, 0 @@ -124,7 +131,7 @@ _test2__leave: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -135,7 +142,7 @@ _test2__leave: ; closed source programs). ; ; Please read the MIT license on the internet -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -229,8 +236,8 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/heapinit.asm" +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -337,7 +344,7 @@ __MEM_INIT2: ret ENDP pop namespace -#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -368,9 +375,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ret z ; NULL -#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -435,7 +442,7 @@ __MEM_SUBTRACT: ret ENDP pop namespace -#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; --------------------------------------------------------------------- ; MEM_CALLOC ; Allocates a block of memory in the heap, and clears it filling it @@ -533,9 +540,69 @@ __ALLOC_INITIALIZED_LOCAL_ARRAY: ldir pop hl ; HL = addr of LBound area if used ret + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes. Then sets LBOUND and UBOUND ptrs + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] PTR to the lbound element area + ; [SP + 4] PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS: + call __ALLOC_LOCAL_ARRAY +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS2: + pop bc ;; ret address + pop de ;; lbound + inc hl + ld (hl), e + inc hl + ld (hl), d + pop de ;; PTR to ubound table + push bc ;; puts ret address back + ld a, d + or e + ret z ;; if PTR for UBound is 0, it's not used + inc hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; TOP of the stack = PTR to the element area + ; [SP + 2] = PTR to the element area + ; [SP + 4] = PTR to the lbound element area + ; [SP + 6] = PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS: + ;; Swaps [SP] and [SP + 2] + exx + pop hl ;; Ret address + ex (sp), hl ;; HL <- PTR to Element area, (sp) = Ret address + push hl ;; [SP] = PTR to element area, [SP + 2] = Ret address + exx + call __ALLOC_INITIALIZED_LOCAL_ARRAY + jp __ALLOC_LOCAL_ARRAY_WITH_BOUNDS2 #line 142 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" pop namespace -#line 98 "arch/zx48k/lbound12.bas" +#line 105 "arch/zx48k/lbound12.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arraybound.asm" ; --------------------------------------------------------- ; Copyleft (k)2011 by Jose Rodriguez (a.k.a. Boriel) @@ -613,12 +680,12 @@ __DIM_NOT_EXIST: ret ENDP pop namespace -#line 99 "arch/zx48k/lbound12.bas" +#line 106 "arch/zx48k/lbound12.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arraystrfree.asm" ; This routine is in charge of freeing an array of strings from memory ; HL = Pointer to start of array in memory ; Top of the stack = Number of elements of the array -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/free.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -818,7 +885,7 @@ __ARRAYSTR_FREE_MEM: ; like the above, buf also frees the array itself pop hl ; recovers array block pointer jp __MEM_FREE ; Frees it and returns from __MEM_FREE pop namespace -#line 100 "arch/zx48k/lbound12.bas" +#line 107 "arch/zx48k/lbound12.bas" .LABEL.__LABEL5: DEFB 01h DEFB 00h diff --git a/tests/functional/arch/zx48k/lbound2.asm b/tests/functional/arch/zx48k/lbound2.asm index 97d33b619..9c4b2636e 100644 --- a/tests/functional/arch/zx48k/lbound2.asm +++ b/tests/functional/arch/zx48k/lbound2.asm @@ -25,7 +25,7 @@ _a: DEFW .LABEL.__LABEL0 _a.__DATA__.__PTR__: DEFW _a.__DATA__ - DEFW 0 + DEFW _a.__LBOUND__ DEFW 0 _a.__DATA__: DEFB 00h @@ -41,6 +41,9 @@ _a.__DATA__: DEFW 0001h DEFW 0003h DEFB 01h +_a.__LBOUND__: + DEFW 0003h + DEFW 0007h .core.ZXBASIC_USER_DATA_END: .core.__MAIN_PROGRAM__: ld hl, (_b) diff --git a/tests/functional/arch/zx48k/lbound4.asm b/tests/functional/arch/zx48k/lbound4.asm index 5d8feea85..dd45427e1 100644 --- a/tests/functional/arch/zx48k/lbound4.asm +++ b/tests/functional/arch/zx48k/lbound4.asm @@ -25,7 +25,7 @@ _a: DEFW .LABEL.__LABEL0 _a.__DATA__.__PTR__: DEFW _a.__DATA__ - DEFW 0 + DEFW _a.__LBOUND__ DEFW 0 _a.__DATA__: DEFB 00h @@ -41,6 +41,9 @@ _a.__DATA__: DEFW 0001h DEFW 0003h DEFB 01h +_a.__LBOUND__: + DEFW 0003h + DEFW 0007h .core.ZXBASIC_USER_DATA_END: .core.__MAIN_PROGRAM__: ld hl, (_b) diff --git a/tests/functional/arch/zx48k/lbound5.asm b/tests/functional/arch/zx48k/lbound5.asm index 87a357f50..93b6da4b6 100644 --- a/tests/functional/arch/zx48k/lbound5.asm +++ b/tests/functional/arch/zx48k/lbound5.asm @@ -25,7 +25,7 @@ _a: DEFW .LABEL.__LABEL0 _a.__DATA__.__PTR__: DEFW _a.__DATA__ - DEFW 0 + DEFW _a.__LBOUND__ DEFW 0 _a.__DATA__: DEFB 00h @@ -41,6 +41,9 @@ _a.__DATA__: DEFW 0001h DEFW 0003h DEFB 01h +_a.__LBOUND__: + DEFW 0003h + DEFW 0007h .core.ZXBASIC_USER_DATA_END: .core.__MAIN_PROGRAM__: ld hl, (_b) diff --git a/tests/functional/arch/zx48k/lbound6.asm b/tests/functional/arch/zx48k/lbound6.asm index 3f16f9a09..b44acdb7c 100644 --- a/tests/functional/arch/zx48k/lbound6.asm +++ b/tests/functional/arch/zx48k/lbound6.asm @@ -50,10 +50,14 @@ _test: push hl push hl push hl + ld hl, 0 + push hl + ld hl, _test.a.__LBOUND__ + push hl ld hl, -6 ld de, .LABEL.__LABEL5 ld bc, 9 - call .core.__ALLOC_LOCAL_ARRAY + call .core.__ALLOC_LOCAL_ARRAY_WITH_BOUNDS ld hl, 0 ld (_b), hl jp .LABEL.__LABEL0 @@ -88,9 +92,12 @@ _test__leave: ld sp, ix pop ix ret +_test.a.__LBOUND__: + DEFW 0003h + DEFW 0007h ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -101,7 +108,7 @@ _test__leave: ; closed source programs). ; ; Please read the MIT license on the internet -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -195,8 +202,8 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/heapinit.asm" +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -303,7 +310,7 @@ __MEM_INIT2: ret ENDP pop namespace -#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -334,9 +341,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ret z ; NULL -#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -401,7 +408,7 @@ __MEM_SUBTRACT: ret ENDP pop namespace -#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; --------------------------------------------------------------------- ; MEM_CALLOC ; Allocates a block of memory in the heap, and clears it filling it @@ -499,9 +506,69 @@ __ALLOC_INITIALIZED_LOCAL_ARRAY: ldir pop hl ; HL = addr of LBound area if used ret + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes. Then sets LBOUND and UBOUND ptrs + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] PTR to the lbound element area + ; [SP + 4] PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS: + call __ALLOC_LOCAL_ARRAY +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS2: + pop bc ;; ret address + pop de ;; lbound + inc hl + ld (hl), e + inc hl + ld (hl), d + pop de ;; PTR to ubound table + push bc ;; puts ret address back + ld a, d + or e + ret z ;; if PTR for UBound is 0, it's not used + inc hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; TOP of the stack = PTR to the element area + ; [SP + 2] = PTR to the element area + ; [SP + 4] = PTR to the lbound element area + ; [SP + 6] = PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS: + ;; Swaps [SP] and [SP + 2] + exx + pop hl ;; Ret address + ex (sp), hl ;; HL <- PTR to Element area, (sp) = Ret address + push hl ;; [SP] = PTR to element area, [SP + 2] = Ret address + exx + call __ALLOC_INITIALIZED_LOCAL_ARRAY + jp __ALLOC_LOCAL_ARRAY_WITH_BOUNDS2 #line 142 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" pop namespace -#line 64 "arch/zx48k/lbound6.bas" +#line 71 "arch/zx48k/lbound6.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arraybound.asm" ; --------------------------------------------------------- ; Copyleft (k)2011 by Jose Rodriguez (a.k.a. Boriel) @@ -579,8 +646,8 @@ __DIM_NOT_EXIST: ret ENDP pop namespace -#line 65 "arch/zx48k/lbound6.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/free.asm" +#line 72 "arch/zx48k/lbound6.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -738,7 +805,7 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ret ENDP pop namespace -#line 66 "arch/zx48k/lbound6.bas" +#line 73 "arch/zx48k/lbound6.bas" .LABEL.__LABEL5: DEFB 01h DEFB 00h diff --git a/tests/functional/arch/zx48k/lbound7.asm b/tests/functional/arch/zx48k/lbound7.asm index bf4090e66..23a7dc826 100644 --- a/tests/functional/arch/zx48k/lbound7.asm +++ b/tests/functional/arch/zx48k/lbound7.asm @@ -24,7 +24,7 @@ _a: DEFW .LABEL.__LABEL5 _a.__DATA__.__PTR__: DEFW _a.__DATA__ - DEFW 0 + DEFW _a.__LBOUND__ DEFW 0 _a.__DATA__: DEFB 00h @@ -40,6 +40,9 @@ _a.__DATA__: DEFW 0001h DEFW 0003h DEFB 01h +_a.__LBOUND__: + DEFW 0003h + DEFW 0007h .core.ZXBASIC_USER_DATA_END: .core.__MAIN_PROGRAM__: ld hl, _a diff --git a/tests/functional/arch/zx48k/lbound8.asm b/tests/functional/arch/zx48k/lbound8.asm index ab0673bbc..18b9ac662 100644 --- a/tests/functional/arch/zx48k/lbound8.asm +++ b/tests/functional/arch/zx48k/lbound8.asm @@ -24,7 +24,7 @@ _a: DEFW .LABEL.__LABEL5 _a.__DATA__.__PTR__: DEFW _a.__DATA__ - DEFW 0 + DEFW _a.__LBOUND__ DEFW 0 _a.__DATA__: DEFB 00h @@ -40,6 +40,9 @@ _a.__DATA__: DEFW 0001h DEFW 0003h DEFB 01h +_a.__LBOUND__: + DEFW 0003h + DEFW 0007h .core.ZXBASIC_USER_DATA_END: .core.__MAIN_PROGRAM__: ld hl, _a diff --git a/tests/functional/arch/zx48k/lbound9.asm b/tests/functional/arch/zx48k/lbound9.asm index ccc0d9543..58f3bfa75 100644 --- a/tests/functional/arch/zx48k/lbound9.asm +++ b/tests/functional/arch/zx48k/lbound9.asm @@ -24,7 +24,7 @@ _a: DEFW .LABEL.__LABEL5 _a.__DATA__.__PTR__: DEFW _a.__DATA__ - DEFW 0 + DEFW _a.__LBOUND__ DEFW 0 _a.__DATA__: DEFB 00h @@ -40,6 +40,9 @@ _a.__DATA__: DEFW 0001h DEFW 0003h DEFB 01h +_a.__LBOUND__: + DEFW 0003h + DEFW 0007h .core.ZXBASIC_USER_DATA_END: .core.__MAIN_PROGRAM__: ld hl, _a diff --git a/tests/functional/arch/zx48k/letsubstr_undecl.asm b/tests/functional/arch/zx48k/letsubstr_undecl.asm new file mode 100644 index 000000000..594f1e43c --- /dev/null +++ b/tests/functional/arch/zx48k/letsubstr_undecl.asm @@ -0,0 +1,446 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 4768 +.core.ZXBASIC_MEM_HEAP: + DEFS 4768 + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_S: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, .LABEL.__LABEL0 + push hl + xor a + push af + ld hl, 0 + push hl + ld hl, 0 + push hl + ld hl, (_S) + call .core.__LETSUBSTR + ld a, (_S) & 0xFF + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +.LABEL.__LABEL0: + DEFW 0001h + DEFB 61h + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/letsubstr.asm" + ; Substring assigment eg. LET a$(p0 TO p1) = "xxxx" + ; HL = Start of string + ; TOP of the stack -> p1 (16 bit, unsigned) + ; TOP -1 of the stack -> p0 register + ; TOP -2 Flag (popped out in A register) + ; A Register => 0 if HL is not freed from memory + ; => Not 0 if HL must be freed from memory on exit + ; TOP -3 B$ address +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; __MEM_INIT must be called to initalize this library with the + ; standard parameters + ; --------------------------------------------------------------------- + push namespace core +__MEM_INIT: ; Initializes the library using (RAMTOP) as start, and + ld hl, ZXBASIC_MEM_HEAP ; Change this with other address of heap start + ld de, ZXBASIC_HEAP_SIZE ; Change this with your size + ; --------------------------------------------------------------------- + ; __MEM_INIT2 initalizes this library +; Parameters: +; HL : Memory address of 1st byte of the memory heap +; DE : Length in bytes of the Memory Heap + ; --------------------------------------------------------------------- +__MEM_INIT2: + ; HL as TOP + PROC + dec de + dec de + dec de + dec de ; DE = length - 4; HL = start + ; This is done, because we require 4 bytes for the empty dummy-header block + xor a + ld (hl), a + inc hl + ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 + inc hl + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block + ld (hl), c + inc hl + ld (hl), b + inc hl ; Pointer to next block + ld (hl), e + inc hl + ld (hl), d + inc hl ; Block size (should be length - 4 at start); This block contains all the available memory + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret + ENDP + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- + push namespace core +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN + ld a, h + or l + ret z ; Return if NULL pointer + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous + or a ; Clear carry flag + sbc hl, bc ; Carry if BC > HL => This block if before + add hl, bc ; Restores HL, preserving Carry flag + jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) + inc hl + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 + call __MEM_JOIN_TEST + pop hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; + push hl ; Saves it for later + add hl, bc ; Adds its length. If HL == DE now, it must be joined + or a + sbc hl, de ; If Z, then HL == DE => We must join + pop hl + ret nz +__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC + push hl ; Saves it for later + ex de, hl + ld e, (hl) ; DE -> block->next->length + inc hl + ld d, (hl) + inc hl + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next + pop hl ; Recovers Pointer to block + ld (hl), c + inc hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved + ret + ENDP + pop namespace +#line 11 "/zxbasic/src/lib/arch/zx48k/runtime/letsubstr.asm" + push namespace core +__LETSUBSTR: + PROC + LOCAL __CONT0 + LOCAL __CONT1 + LOCAL __CONT2 + LOCAL __FREE_STR + exx + pop hl ; Return address + pop de ; p1 + pop bc ; p0 + exx + pop af ; Flag + ex af, af' ; Save it for later + pop de ; B$ + exx + push hl ; push ret addr back + exx + push de ; B$ addr to be freed upon return (if A != 0) + ld a, h + or l + jp z, __FREE_STR ; Return if null + ld c, (hl) + inc hl + ld b, (hl) ; BC = Str length + inc hl ; HL = String start + push bc + exx + ex de, hl + or a + sbc hl, bc ; HL = Length of string requested by user + inc hl ; len (a$(p0 TO p1)) = p1 - p0 + 1 + ex de, hl ; Saves it in DE + pop hl ; HL = String length + exx + jp c, __FREE_STR ; Return if p0 > p1 + exx + or a + sbc hl, bc ; P0 >= String length? + exx + jp z, __FREE_STR ; Return if equal + jp c, __FREE_STR ; Return if greater + exx + add hl, bc ; Add it back + sbc hl, de ; Length of substring > string => Truncate it + add hl, de ; add it back + jr nc, __CONT0 ; Length of substring within a$ + ld d, h + ld e, l ; Truncate length of substring to fit within the strlen +__CONT0: ; At this point DE = Length of substring to copy + ; BC = start of char to copy + push de + push bc + exx + pop bc + add hl, bc ; Start address (within a$) so copy from b$ (in DE) + push hl + exx + pop hl ; Start address (within a$) so copy from b$ (in DE) + ld b, d ; Length of string + ld c, e + ld (hl), ' ' + ld d, h + ld e, l + inc de + dec bc + ld a, b + or c + jr z, __CONT2 + ; At this point HL = DE = Start of Write zone in a$ + ; BC = Number of chars to write + ldir +__CONT2: + pop bc ; Recovers Length of string to copy + exx + ex de, hl ; HL = Source, DE = Target + ld a, h + or l + jp z, __FREE_STR ; Return if B$ is NULL + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld a, b + or c + jp z, __FREE_STR ; Return if len(b$) = 0 + ; Now if len(b$) < len(char to copy), copy only len(b$) chars + push de + push hl + push bc + exx + pop hl ; LEN (b$) + or a + sbc hl, bc + add hl, bc + jr nc, __CONT1 + ; If len(b$) < len(to copy) + ld b, h ; BC = len(to copy) + ld c, l +__CONT1: + pop hl + pop de + ldir ; Copy b$ into a$(x to y) +__FREE_STR: + pop hl + ex af, af' + or a ; If not 0, free + jp nz, __MEM_FREE + ret + ENDP + pop namespace +#line 32 "arch/zx48k/letsubstr_undecl.bas" + END diff --git a/tests/functional/arch/zx48k/letsubstr_undecl.bas b/tests/functional/arch/zx48k/letsubstr_undecl.bas new file mode 100644 index 000000000..1f5b1adf0 --- /dev/null +++ b/tests/functional/arch/zx48k/letsubstr_undecl.bas @@ -0,0 +1,2 @@ +S$(0) = "a" +POKE 0, @S diff --git a/tests/functional/arch/zx48k/local_array_with_bounds2.asm b/tests/functional/arch/zx48k/local_array_with_bounds2.asm new file mode 100644 index 000000000..522f32f9e --- /dev/null +++ b/tests/functional/arch/zx48k/local_array_with_bounds2.asm @@ -0,0 +1,917 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 4768 +.core.ZXBASIC_MEM_HEAP: + DEFS 4768 + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call _Sub1 + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +_Sub1: + push ix + ld ix, 0 + add ix, sp + ld hl, -7 + add hl, sp + ld sp, hl + ld (hl), 0 + ld bc, 6 + ld d, h + ld e, l + inc de + ldir + ld hl, _Sub1.a.__UBOUND__ + push hl + ld hl, 0 + push hl + ld hl, -7 + ld de, .LABEL.__LABEL0 + ld bc, 6 + call .core.__ALLOC_LOCAL_ARRAY_WITH_BOUNDS + ld (ix-1), 2 + ld a, (ix-1) + dec a + ld l, a + ld h, 0 + push hl + push ix + pop hl + ld de, -7 + add hl, de + call .core.__ARRAY + ld de, 7 + ld (hl), e + inc hl + ld (hl), d + ld l, (ix-5) + ld h, (ix-4) + inc hl + inc hl + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + ld (0), a +_Sub1__leave: + ex af, af' + exx + ld l, (ix-5) + ld h, (ix-4) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + ret +_Sub1.a.__UBOUND__: + DEFW 0002h + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + ;; Performs a faster multiply for little 16bit numbs +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + LOCAL __MUL16LOOP + LOCAL __MUL16NOADD + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +__MUL16LOOP: + add hl, hl ; hl << 1 + sla c + rla ; a,c << 1 + jp nc, __MUL16NOADD + add hl, de +__MUL16NOADD: + djnz __MUL16LOOP + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + push namespace core +__FMUL16: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +1: + add hl, de + djnz 1b + ret + pop namespace +#line 20 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 23 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" +#line 24 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + push namespace core +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ;; HL <-- [HL] +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL TMP_ARR_PTR ; Ptr to Array DATA region. Stored temporarily + LOCAL LBOUND_PTR, UBOUND_PTR ; LBound and UBound PTR indexes + LOCAL RET_ADDR ; Contains the return address popped from the stack + LBOUND_PTR EQU 23698 ; Uses MEMBOT as a temporary variable + UBOUND_PTR EQU LBOUND_PTR + 2 ; Next 2 bytes for UBOUND PTR + RET_ADDR EQU UBOUND_PTR + 2 ; Next 2 bytes for RET_ADDR + TMP_ARR_PTR EQU RET_ADDR + 2 ; Next 2 bytes for TMP_ARR_PTR + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE <-- PTR to Dim sizes table + ld (TMP_ARR_PTR), hl ; HL = Array __DATA__.__PTR__ + inc hl + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __LBOUND__ PTR + ld (LBOUND_PTR), bc ; Store it for later + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __UBOUND__ PTR + ld (UBOUND_PTR), bc +#line 66 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + ex de, hl ; HL <-- PTR to Dim sizes table, DE <-- dummy + ex (sp), hl ; Return address in HL, PTR Dim sizes table onto Stack + ld (RET_ADDR), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer to Dim sizes table + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Element Offset "accumulator" +LOOP: + ex de, hl ; DE = Element Offset + ld hl, (LBOUND_PTR) + ld a, h + or l + ld b, h + ld c, l + jr z, 1f + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (LBOUND_PTR), hl +1: + pop hl ; Get next index (Ai) from the stack + sbc hl, bc ; Subtract LBOUND + ld a, ERROR_SubscriptWrong + jp c, __ERROR + push hl ; Saves (Ai) - Lbound(i) + add hl, bc ; Recover original (Ai) value + push hl + ld hl, (UBOUND_PTR) + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (UBOUND_PTR), hl + pop hl ; original (Ai) value + scf + sbc hl, bc ; HL <- HL - BC - 1 = Ai - UBound(i) - 1 => No Carry if Ai > UBound(i) + jp nc, __ERROR + pop hl ; Recovers (Ai) - Lbound(Ai) +#line 116 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + add hl, de ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + dec bc ; Decrements loop counter + ld e, (hl) ; Loads next dimension size into D'E' + inc hl + ld d, (hl) + inc hl + push de + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FMUL16 ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 146 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 156 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start + ld de, (RET_ADDR) + push de + ret + ENDP + pop namespace +#line 76 "arch/zx48k/local_array_with_bounds2.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be freed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; __MEM_INIT must be called to initalize this library with the + ; standard parameters + ; --------------------------------------------------------------------- + push namespace core +__MEM_INIT: ; Initializes the library using (RAMTOP) as start, and + ld hl, ZXBASIC_MEM_HEAP ; Change this with other address of heap start + ld de, ZXBASIC_HEAP_SIZE ; Change this with your size + ; --------------------------------------------------------------------- + ; __MEM_INIT2 initalizes this library +; Parameters: +; HL : Memory address of 1st byte of the memory heap +; DE : Length in bytes of the Memory Heap + ; --------------------------------------------------------------------- +__MEM_INIT2: + ; HL as TOP + PROC + dec de + dec de + dec de + dec de ; DE = length - 4; HL = start + ; This is done, because we require 4 bytes for the empty dummy-header block + xor a + ld (hl), a + inc hl + ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 + inc hl + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block + ld (hl), c + inc hl + ld (hl), b + inc hl ; Pointer to next block + ld (hl), e + inc hl + ld (hl), d + inc hl ; Block size (should be length - 4 at start); This block contains all the available memory + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret + ENDP + pop namespace +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ; --------------------------------------------------------------------- + ; MEM_ALLOC + ; Allocates a block of memory in the heap. + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 + TEMP EQU TEMP0 + 1 + ld hl, 0 + ld (TEMP), hl +__MEM_START: + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start + inc bc + inc bc ; BC = BC + 2 ; block size needs 2 extra bytes for hidden pointer +__MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE + ld a, h ; HL = NULL (No memory available?) + or l +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ; HL = Pointer to Free block + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = Block Length + push hl ; HL = *pointer to -> next block + ex de, hl + or a ; CF = 0 + sbc hl, bc ; FREE >= BC (Length) (HL = BlockLength - Length) + jp nc, __MEM_DONE + pop hl + ld (TEMP), hl + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + jp __MEM_LOOP +__MEM_DONE: ; A free block has been found. + ; Check if at least 4 bytes remains free (HL >= 4) + push hl + exx ; exx to preserve bc + pop hl + ld bc, 4 + or a + sbc hl, bc + exx + jp nc, __MEM_SUBTRACT + ; At this point... + ; less than 4 bytes remains free. So we return this block entirely + ; We must link the previous block with the next to this one + ; (DE) => Pointer to next block + ; (TEMP) => &(previous->next) + pop hl ; Discard current block pointer + push de + ex de, hl ; DE = Previous block pointer; (HL) = Next block pointer + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex de, hl ; HL = Previous block pointer; DE = Next block pointer +TEMP0: + ld hl, 0 ; Pre-previous block pointer + ld (hl), e + inc hl + ld (hl), d ; LINKED + pop hl ; Returning block. + ret +__MEM_SUBTRACT: + ; At this point we have to store HL value (Length - BC) into (DE - 2) + ex de, hl + dec hl + ld (hl), d + dec hl + ld (hl), e ; Store new block length + add hl, de ; New length + DE => free-block start + pop de ; Remove previous HL off the stack + ld (hl), c ; Store length on its 1st word + inc hl + ld (hl), b + inc hl ; Return hl + ret + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" + ; --------------------------------------------------------------------- + ; MEM_CALLOC + ; Allocates a block of memory in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +__MEM_CALLOC: + push bc + call __MEM_ALLOC + pop bc + ld a, h + or l + ret z ; No memory + ld (hl), 0 + dec bc + ld a, b + or c + ret z ; Already filled (1 byte-length block) + ld d, h + ld e, l + inc de + push hl + ldir + pop hl + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- + push namespace core +__ALLOC_LOCAL_ARRAY: + push de + push ix + pop de + add hl, de ; hl = ix + hl + pop de + ld (hl), e + inc hl + ld (hl), d + inc hl + push hl + call __MEM_CALLOC + pop de + ex de, hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with data whose pointer (PTR) is in the stack + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] = PTR to the element area + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY: + push bc + call __ALLOC_LOCAL_ARRAY + pop bc + ;; Swaps [SP], [SP + 2] + exx + pop hl ; HL <- RET address + ex (sp), hl ; HL <- Data table, [SP] <- RET address + push hl ; [SP] <- Data table + exx + ex (sp), hl ; HL = Data table, (SP) = (IX + HL + 4) - start of array address lbound + ; HL = data table + ; BC = length + ; DE = new data area + ldir + pop hl ; HL = addr of LBound area if used + ret + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes. Then sets LBOUND and UBOUND ptrs + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] PTR to the lbound element area + ; [SP + 4] PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS: + call __ALLOC_LOCAL_ARRAY +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS2: + pop bc ;; ret address + pop de ;; lbound + inc hl + ld (hl), e + inc hl + ld (hl), d + pop de ;; PTR to ubound table + push bc ;; puts ret address back + ld a, d + or e + ret z ;; if PTR for UBound is 0, it's not used + inc hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; TOP of the stack = PTR to the element area + ; [SP + 2] = PTR to the element area + ; [SP + 4] = PTR to the lbound element area + ; [SP + 6] = PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS: + ;; Swaps [SP] and [SP + 2] + exx + pop hl ;; Ret address + ex (sp), hl ;; HL <- PTR to Element area, (sp) = Ret address + push hl ;; [SP] = PTR to element area, [SP + 2] = Ret address + exx + call __ALLOC_INITIALIZED_LOCAL_ARRAY + jp __ALLOC_LOCAL_ARRAY_WITH_BOUNDS2 +#line 142 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + pop namespace +#line 77 "arch/zx48k/local_array_with_bounds2.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- + push namespace core +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN + ld a, h + or l + ret z ; Return if NULL pointer + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous + or a ; Clear carry flag + sbc hl, bc ; Carry if BC > HL => This block if before + add hl, bc ; Restores HL, preserving Carry flag + jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) + inc hl + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 + call __MEM_JOIN_TEST + pop hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; + push hl ; Saves it for later + add hl, bc ; Adds its length. If HL == DE now, it must be joined + or a + sbc hl, de ; If Z, then HL == DE => We must join + pop hl + ret nz +__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC + push hl ; Saves it for later + ex de, hl + ld e, (hl) ; DE -> block->next->length + inc hl + ld d, (hl) + inc hl + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next + pop hl ; Recovers Pointer to block + ld (hl), c + inc hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved + ret + ENDP + pop namespace +#line 78 "arch/zx48k/local_array_with_bounds2.bas" +.LABEL.__LABEL0: + DEFB 00h + DEFB 00h + DEFB 02h + END diff --git a/tests/functional/arch/zx48k/local_array_with_bounds2.bas b/tests/functional/arch/zx48k/local_array_with_bounds2.bas new file mode 100644 index 000000000..d47c7053f --- /dev/null +++ b/tests/functional/arch/zx48k/local_array_with_bounds2.bas @@ -0,0 +1,15 @@ +#pragma array_check=true + +sub Sub1() + dim i as uByte + dim a(2) as uInteger + + i=2 + a(i-1)=7 + + POKE 0,a(1) +end sub + + + +Sub1 diff --git a/tests/functional/arch/zx48k/local_str_array1.asm b/tests/functional/arch/zx48k/local_str_array1.asm index 774b6e4d1..18ac19255 100644 --- a/tests/functional/arch/zx48k/local_str_array1.asm +++ b/tests/functional/arch/zx48k/local_str_array1.asm @@ -61,8 +61,8 @@ _test: ld de, -6 add hl, de call .core.__ARRAY - pop de - call .core.__STORE_STR2 + ld de, .LABEL.__LABEL0 + call .core.__STORE_STR ld hl, (_i) push hl push ix @@ -259,7 +259,7 @@ ARRAY_SIZE_LOOP: pop namespace #line 74 "arch/zx48k/local_str_array1.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -270,7 +270,7 @@ ARRAY_SIZE_LOOP: ; closed source programs). ; ; Please read the MIT license on the internet -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -364,8 +364,8 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/heapinit.asm" +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -472,7 +472,7 @@ __MEM_INIT2: ret ENDP pop namespace -#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -503,9 +503,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ret z ; NULL -#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -570,7 +570,7 @@ __MEM_SUBTRACT: ret ENDP pop namespace -#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; --------------------------------------------------------------------- ; MEM_CALLOC ; Allocates a block of memory in the heap, and clears it filling it @@ -675,7 +675,7 @@ __ALLOC_INITIALIZED_LOCAL_ARRAY: ; This routine is in charge of freeing an array of strings from memory ; HL = Pointer to start of array in memory ; Top of the stack = Number of elements of the array -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/free.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -913,38 +913,253 @@ __LOADSTR: ; __FASTCALL__ entry pop hl ; Recovers destiny in hl as result ret pop namespace -#line 78 "arch/zx48k/local_str_array1.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/storestr2.asm" - ; Similar to __STORE_STR, but this one is called when - ; the value of B$ if already duplicated onto the stack. - ; So we needn't call STRASSING to create a duplication - ; HL = address of string memory variable - ; DE = address of 2n string. It just copies DE into (HL) - ; freeing (HL) previously. +#line 77 "arch/zx48k/local_str_array1.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/storestr.asm" +; vim:ts=4:et:sw=4 + ; Stores value of current string pointed by DE register into address pointed by HL + ; Returns DE = Address pointer (&a$) + ; Returns HL = HL (b$ => might be needed later to free it from the heap) + ; + ; e.g. => HL = _variableName (DIM _variableName$) + ; DE = Address into the HEAP + ; + ; This function will resize (REALLOC) the space pointed by HL + ; before copying the content of b$ into a$ +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/strcpy.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/realloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_REALLOC + ; Reallocates a block of memory in the heap. + ; + ; Parameters + ; HL = Pointer to the original block + ; BC = New Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; +; Notes: + ; If BC = 0, the block is freed, otherwise + ; the content of the original block is copied to the new one, and + ; the new size is adjusted. If BC < original length, the content + ; will be truncated. Otherwise, extra block content might contain + ; memory garbage. + ; + ; --------------------------------------------------------------------- push namespace core -__PISTORE_STR2: ; Indirect store temporary string at (IX + BC) +__REALLOC: ; Reallocates block pointed by HL, with new length BC + PROC + LOCAL __REALLOC_END + ld a, h + or l + jp z, __MEM_ALLOC ; If HL == NULL, just do a malloc + ld e, (hl) + inc hl + ld d, (hl) ; DE = First 2 bytes of HL block + push hl + exx + pop de + inc de ; DE' <- HL + 2 + exx ; DE' <- HL (Saves current pointer into DE') + dec hl ; HL = Block start + push de + push bc + call __MEM_FREE ; Frees current block + pop bc + push bc + call __MEM_ALLOC ; Gets a new block of length BC + pop bc + pop de + ld a, h + or l + ret z ; Return if HL == NULL (No memory) + ld (hl), e + inc hl + ld (hl), d + inc hl ; Recovers first 2 bytes in HL + dec bc + dec bc ; BC = BC - 2 (Two bytes copied) + ld a, b + or c + jp z, __REALLOC_END ; Ret if nothing to copy (BC == 0) + exx + push de + exx + pop de ; DE <- DE' ; Start of remaining block + push hl ; Saves current Block + 2 start + ex de, hl ; Exchanges them: DE is destiny block + ldir ; Copies BC Bytes + pop hl ; Recovers Block + 2 start +__REALLOC_END: + dec hl ; Set HL + dec hl ; To begin of block + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/strcpy.asm" + ; String library + push namespace core +__STRASSIGN: ; Performs a$ = b$ (HL = address of a$; DE = Address of b$) + PROC + LOCAL __STRREALLOC + LOCAL __STRCONTINUE + LOCAL __B_IS_NULL + LOCAL __NOTHING_TO_COPY + ld b, d + ld c, e + ld a, b + or c + jr z, __B_IS_NULL + ex de, hl + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(b$) + ex de, hl ; DE = &b$ +__B_IS_NULL: ; Jumps here if B$ pointer is NULL + inc bc + inc bc ; BC = BC + 2 ; (LEN(b$) + 2 bytes for storing length) + push de + push hl + ld a, h + or l + jr z, __STRREALLOC + dec hl + ld d, (hl) + dec hl + ld e, (hl) ; DE = MEMBLOCKSIZE(a$) + dec de + dec de ; DE = DE - 2 ; (Membloksize takes 2 bytes for memblock length) + ld h, b + ld l, c ; HL = LEN(b$) + 2 => Minimum block size required + ex de, hl ; Now HL = BLOCKSIZE(a$), DE = LEN(b$) + 2 + or a ; Prepare to subtract BLOCKSIZE(a$) - LEN(b$) + sbc hl, de ; Carry if len(b$) > Blocklen(a$) + jr c, __STRREALLOC ; No need to realloc + ; Need to reallocate at least to len(b$) + 2 + ex de, hl ; DE = Remaining bytes in a$ mem block. + ld hl, 4 + sbc hl, de ; if remaining bytes < 4 we can continue + jr nc,__STRCONTINUE ; Otherwise, we realloc, to free some bytes +__STRREALLOC: + pop hl + call __REALLOC ; Returns in HL a new pointer with BC bytes allocated + push hl +__STRCONTINUE: ; Pops hl and de SWAPPED + pop de ; DE = &a$ + pop hl ; HL = &b$ + ld a, d ; Return if not enough memory for new length + or e + ret z ; Return if DE == NULL (0) +__STRCPY: ; Copies string pointed by HL into string pointed by DE + ; Returns DE as HL (new pointer) + ld a, h + or l + jr z, __NOTHING_TO_COPY + ld c, (hl) + inc hl + ld b, (hl) + dec hl + inc bc + inc bc + push de + ldir + pop hl + ret +__NOTHING_TO_COPY: + ex de, hl + ld (hl), e + inc hl + ld (hl), d + dec hl + ret + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx48k/runtime/storestr.asm" + push namespace core +__PISTORE_STR: ; Indirect assignment at (IX + BC) push ix pop hl add hl, bc -__ISTORE_STR2: - ld c, (hl) ; Dereferences HL +__ISTORE_STR: ; Indirect assignment, hl point to a pointer to a pointer to the heap! + ld c, (hl) inc hl ld h, (hl) - ld l, c ; HL = *HL (real string variable address) -__STORE_STR2: - push hl + ld l, c ; HL = (HL) +__STORE_STR: + push de ; Pointer to b$ + push hl ; Pointer to a$ ld c, (hl) inc hl ld h, (hl) - ld l, c ; HL = *HL (real string address) - push de - call __MEM_FREE - pop de - pop hl + ld l, c ; HL = (HL) + call __STRASSIGN ; HL (a$) = DE (b$); HL changed to a new dynamic memory allocation + ex de, hl ; DE = new address of a$ + pop hl ; Recover variable memory address pointer ld (hl), e inc hl - ld (hl), d - dec hl ; HL points to mem address variable. This might be useful in the future. + ld (hl), d ; Stores a$ ptr into element ptr + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret pop namespace #line 79 "arch/zx48k/local_str_array1.bas" diff --git a/tests/functional/arch/zx48k/local_str_array2.asm b/tests/functional/arch/zx48k/local_str_array2.asm index c3fdaed7f..9e4d7b1f9 100644 --- a/tests/functional/arch/zx48k/local_str_array2.asm +++ b/tests/functional/arch/zx48k/local_str_array2.asm @@ -72,8 +72,8 @@ _test: ld de, -8 add hl, de call .core.__ARRAY - pop de - call .core.__STORE_STR2 + ld de, .LABEL.__LABEL0 + call .core.__STORE_STR ld l, (ix-2) ld h, (ix-1) push hl @@ -271,7 +271,7 @@ ARRAY_SIZE_LOOP: pop namespace #line 89 "arch/zx48k/local_str_array2.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -282,7 +282,7 @@ ARRAY_SIZE_LOOP: ; closed source programs). ; ; Please read the MIT license on the internet -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -376,8 +376,8 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/heapinit.asm" +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -484,7 +484,7 @@ __MEM_INIT2: ret ENDP pop namespace -#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -515,9 +515,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ret z ; NULL -#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -582,7 +582,7 @@ __MEM_SUBTRACT: ret ENDP pop namespace -#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; --------------------------------------------------------------------- ; MEM_CALLOC ; Allocates a block of memory in the heap, and clears it filling it @@ -687,7 +687,7 @@ __ALLOC_INITIALIZED_LOCAL_ARRAY: ; This routine is in charge of freeing an array of strings from memory ; HL = Pointer to start of array in memory ; Top of the stack = Number of elements of the array -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/free.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -925,38 +925,253 @@ __LOADSTR: ; __FASTCALL__ entry pop hl ; Recovers destiny in hl as result ret pop namespace -#line 93 "arch/zx48k/local_str_array2.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/storestr2.asm" - ; Similar to __STORE_STR, but this one is called when - ; the value of B$ if already duplicated onto the stack. - ; So we needn't call STRASSING to create a duplication - ; HL = address of string memory variable - ; DE = address of 2n string. It just copies DE into (HL) - ; freeing (HL) previously. +#line 92 "arch/zx48k/local_str_array2.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/storestr.asm" +; vim:ts=4:et:sw=4 + ; Stores value of current string pointed by DE register into address pointed by HL + ; Returns DE = Address pointer (&a$) + ; Returns HL = HL (b$ => might be needed later to free it from the heap) + ; + ; e.g. => HL = _variableName (DIM _variableName$) + ; DE = Address into the HEAP + ; + ; This function will resize (REALLOC) the space pointed by HL + ; before copying the content of b$ into a$ +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/strcpy.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/realloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_REALLOC + ; Reallocates a block of memory in the heap. + ; + ; Parameters + ; HL = Pointer to the original block + ; BC = New Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; +; Notes: + ; If BC = 0, the block is freed, otherwise + ; the content of the original block is copied to the new one, and + ; the new size is adjusted. If BC < original length, the content + ; will be truncated. Otherwise, extra block content might contain + ; memory garbage. + ; + ; --------------------------------------------------------------------- push namespace core -__PISTORE_STR2: ; Indirect store temporary string at (IX + BC) +__REALLOC: ; Reallocates block pointed by HL, with new length BC + PROC + LOCAL __REALLOC_END + ld a, h + or l + jp z, __MEM_ALLOC ; If HL == NULL, just do a malloc + ld e, (hl) + inc hl + ld d, (hl) ; DE = First 2 bytes of HL block + push hl + exx + pop de + inc de ; DE' <- HL + 2 + exx ; DE' <- HL (Saves current pointer into DE') + dec hl ; HL = Block start + push de + push bc + call __MEM_FREE ; Frees current block + pop bc + push bc + call __MEM_ALLOC ; Gets a new block of length BC + pop bc + pop de + ld a, h + or l + ret z ; Return if HL == NULL (No memory) + ld (hl), e + inc hl + ld (hl), d + inc hl ; Recovers first 2 bytes in HL + dec bc + dec bc ; BC = BC - 2 (Two bytes copied) + ld a, b + or c + jp z, __REALLOC_END ; Ret if nothing to copy (BC == 0) + exx + push de + exx + pop de ; DE <- DE' ; Start of remaining block + push hl ; Saves current Block + 2 start + ex de, hl ; Exchanges them: DE is destiny block + ldir ; Copies BC Bytes + pop hl ; Recovers Block + 2 start +__REALLOC_END: + dec hl ; Set HL + dec hl ; To begin of block + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/strcpy.asm" + ; String library + push namespace core +__STRASSIGN: ; Performs a$ = b$ (HL = address of a$; DE = Address of b$) + PROC + LOCAL __STRREALLOC + LOCAL __STRCONTINUE + LOCAL __B_IS_NULL + LOCAL __NOTHING_TO_COPY + ld b, d + ld c, e + ld a, b + or c + jr z, __B_IS_NULL + ex de, hl + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(b$) + ex de, hl ; DE = &b$ +__B_IS_NULL: ; Jumps here if B$ pointer is NULL + inc bc + inc bc ; BC = BC + 2 ; (LEN(b$) + 2 bytes for storing length) + push de + push hl + ld a, h + or l + jr z, __STRREALLOC + dec hl + ld d, (hl) + dec hl + ld e, (hl) ; DE = MEMBLOCKSIZE(a$) + dec de + dec de ; DE = DE - 2 ; (Membloksize takes 2 bytes for memblock length) + ld h, b + ld l, c ; HL = LEN(b$) + 2 => Minimum block size required + ex de, hl ; Now HL = BLOCKSIZE(a$), DE = LEN(b$) + 2 + or a ; Prepare to subtract BLOCKSIZE(a$) - LEN(b$) + sbc hl, de ; Carry if len(b$) > Blocklen(a$) + jr c, __STRREALLOC ; No need to realloc + ; Need to reallocate at least to len(b$) + 2 + ex de, hl ; DE = Remaining bytes in a$ mem block. + ld hl, 4 + sbc hl, de ; if remaining bytes < 4 we can continue + jr nc,__STRCONTINUE ; Otherwise, we realloc, to free some bytes +__STRREALLOC: + pop hl + call __REALLOC ; Returns in HL a new pointer with BC bytes allocated + push hl +__STRCONTINUE: ; Pops hl and de SWAPPED + pop de ; DE = &a$ + pop hl ; HL = &b$ + ld a, d ; Return if not enough memory for new length + or e + ret z ; Return if DE == NULL (0) +__STRCPY: ; Copies string pointed by HL into string pointed by DE + ; Returns DE as HL (new pointer) + ld a, h + or l + jr z, __NOTHING_TO_COPY + ld c, (hl) + inc hl + ld b, (hl) + dec hl + inc bc + inc bc + push de + ldir + pop hl + ret +__NOTHING_TO_COPY: + ex de, hl + ld (hl), e + inc hl + ld (hl), d + dec hl + ret + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx48k/runtime/storestr.asm" + push namespace core +__PISTORE_STR: ; Indirect assignment at (IX + BC) push ix pop hl add hl, bc -__ISTORE_STR2: - ld c, (hl) ; Dereferences HL +__ISTORE_STR: ; Indirect assignment, hl point to a pointer to a pointer to the heap! + ld c, (hl) inc hl ld h, (hl) - ld l, c ; HL = *HL (real string variable address) -__STORE_STR2: - push hl + ld l, c ; HL = (HL) +__STORE_STR: + push de ; Pointer to b$ + push hl ; Pointer to a$ ld c, (hl) inc hl ld h, (hl) - ld l, c ; HL = *HL (real string address) - push de - call __MEM_FREE - pop de - pop hl + ld l, c ; HL = (HL) + call __STRASSIGN ; HL (a$) = DE (b$); HL changed to a new dynamic memory allocation + ex de, hl ; DE = new address of a$ + pop hl ; Recover variable memory address pointer ld (hl), e inc hl - ld (hl), d - dec hl ; HL points to mem address variable. This might be useful in the future. + ld (hl), d ; Stores a$ ptr into element ptr + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret pop namespace #line 94 "arch/zx48k/local_str_array2.bas" diff --git a/tests/functional/arch/zx48k/local_str_array3.asm b/tests/functional/arch/zx48k/local_str_array3.asm index 3dd129d4d..e783bd8e8 100644 --- a/tests/functional/arch/zx48k/local_str_array3.asm +++ b/tests/functional/arch/zx48k/local_str_array3.asm @@ -70,8 +70,8 @@ _test: ld de, -8 add hl, de call .core.__ARRAY - pop de - call .core.__STORE_STR2 + ld de, .LABEL.__LABEL5 + call .core.__STORE_STR .LABEL.__LABEL4: ld l, (ix-2) ld h, (ix-1) @@ -290,7 +290,7 @@ ARRAY_SIZE_LOOP: pop namespace #line 108 "arch/zx48k/local_str_array3.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -301,7 +301,7 @@ ARRAY_SIZE_LOOP: ; closed source programs). ; ; Please read the MIT license on the internet -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -395,8 +395,8 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/heapinit.asm" +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -503,7 +503,7 @@ __MEM_INIT2: ret ENDP pop namespace -#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -534,9 +534,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ret z ; NULL -#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -601,7 +601,7 @@ __MEM_SUBTRACT: ret ENDP pop namespace -#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; --------------------------------------------------------------------- ; MEM_CALLOC ; Allocates a block of memory in the heap, and clears it filling it @@ -706,7 +706,7 @@ __ALLOC_INITIALIZED_LOCAL_ARRAY: ; This routine is in charge of freeing an array of strings from memory ; HL = Pointer to start of array in memory ; Top of the stack = Number of elements of the array -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/free.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -944,38 +944,253 @@ __LOADSTR: ; __FASTCALL__ entry pop hl ; Recovers destiny in hl as result ret pop namespace -#line 112 "arch/zx48k/local_str_array3.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/storestr2.asm" - ; Similar to __STORE_STR, but this one is called when - ; the value of B$ if already duplicated onto the stack. - ; So we needn't call STRASSING to create a duplication - ; HL = address of string memory variable - ; DE = address of 2n string. It just copies DE into (HL) - ; freeing (HL) previously. +#line 111 "arch/zx48k/local_str_array3.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/storestr.asm" +; vim:ts=4:et:sw=4 + ; Stores value of current string pointed by DE register into address pointed by HL + ; Returns DE = Address pointer (&a$) + ; Returns HL = HL (b$ => might be needed later to free it from the heap) + ; + ; e.g. => HL = _variableName (DIM _variableName$) + ; DE = Address into the HEAP + ; + ; This function will resize (REALLOC) the space pointed by HL + ; before copying the content of b$ into a$ +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/strcpy.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/realloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_REALLOC + ; Reallocates a block of memory in the heap. + ; + ; Parameters + ; HL = Pointer to the original block + ; BC = New Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; +; Notes: + ; If BC = 0, the block is freed, otherwise + ; the content of the original block is copied to the new one, and + ; the new size is adjusted. If BC < original length, the content + ; will be truncated. Otherwise, extra block content might contain + ; memory garbage. + ; + ; --------------------------------------------------------------------- + push namespace core +__REALLOC: ; Reallocates block pointed by HL, with new length BC + PROC + LOCAL __REALLOC_END + ld a, h + or l + jp z, __MEM_ALLOC ; If HL == NULL, just do a malloc + ld e, (hl) + inc hl + ld d, (hl) ; DE = First 2 bytes of HL block + push hl + exx + pop de + inc de ; DE' <- HL + 2 + exx ; DE' <- HL (Saves current pointer into DE') + dec hl ; HL = Block start + push de + push bc + call __MEM_FREE ; Frees current block + pop bc + push bc + call __MEM_ALLOC ; Gets a new block of length BC + pop bc + pop de + ld a, h + or l + ret z ; Return if HL == NULL (No memory) + ld (hl), e + inc hl + ld (hl), d + inc hl ; Recovers first 2 bytes in HL + dec bc + dec bc ; BC = BC - 2 (Two bytes copied) + ld a, b + or c + jp z, __REALLOC_END ; Ret if nothing to copy (BC == 0) + exx + push de + exx + pop de ; DE <- DE' ; Start of remaining block + push hl ; Saves current Block + 2 start + ex de, hl ; Exchanges them: DE is destiny block + ldir ; Copies BC Bytes + pop hl ; Recovers Block + 2 start +__REALLOC_END: + dec hl ; Set HL + dec hl ; To begin of block + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/strcpy.asm" + ; String library + push namespace core +__STRASSIGN: ; Performs a$ = b$ (HL = address of a$; DE = Address of b$) + PROC + LOCAL __STRREALLOC + LOCAL __STRCONTINUE + LOCAL __B_IS_NULL + LOCAL __NOTHING_TO_COPY + ld b, d + ld c, e + ld a, b + or c + jr z, __B_IS_NULL + ex de, hl + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(b$) + ex de, hl ; DE = &b$ +__B_IS_NULL: ; Jumps here if B$ pointer is NULL + inc bc + inc bc ; BC = BC + 2 ; (LEN(b$) + 2 bytes for storing length) + push de + push hl + ld a, h + or l + jr z, __STRREALLOC + dec hl + ld d, (hl) + dec hl + ld e, (hl) ; DE = MEMBLOCKSIZE(a$) + dec de + dec de ; DE = DE - 2 ; (Membloksize takes 2 bytes for memblock length) + ld h, b + ld l, c ; HL = LEN(b$) + 2 => Minimum block size required + ex de, hl ; Now HL = BLOCKSIZE(a$), DE = LEN(b$) + 2 + or a ; Prepare to subtract BLOCKSIZE(a$) - LEN(b$) + sbc hl, de ; Carry if len(b$) > Blocklen(a$) + jr c, __STRREALLOC ; No need to realloc + ; Need to reallocate at least to len(b$) + 2 + ex de, hl ; DE = Remaining bytes in a$ mem block. + ld hl, 4 + sbc hl, de ; if remaining bytes < 4 we can continue + jr nc,__STRCONTINUE ; Otherwise, we realloc, to free some bytes +__STRREALLOC: + pop hl + call __REALLOC ; Returns in HL a new pointer with BC bytes allocated + push hl +__STRCONTINUE: ; Pops hl and de SWAPPED + pop de ; DE = &a$ + pop hl ; HL = &b$ + ld a, d ; Return if not enough memory for new length + or e + ret z ; Return if DE == NULL (0) +__STRCPY: ; Copies string pointed by HL into string pointed by DE + ; Returns DE as HL (new pointer) + ld a, h + or l + jr z, __NOTHING_TO_COPY + ld c, (hl) + inc hl + ld b, (hl) + dec hl + inc bc + inc bc + push de + ldir + pop hl + ret +__NOTHING_TO_COPY: + ex de, hl + ld (hl), e + inc hl + ld (hl), d + dec hl + ret + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx48k/runtime/storestr.asm" push namespace core -__PISTORE_STR2: ; Indirect store temporary string at (IX + BC) +__PISTORE_STR: ; Indirect assignment at (IX + BC) push ix pop hl add hl, bc -__ISTORE_STR2: - ld c, (hl) ; Dereferences HL +__ISTORE_STR: ; Indirect assignment, hl point to a pointer to a pointer to the heap! + ld c, (hl) inc hl ld h, (hl) - ld l, c ; HL = *HL (real string variable address) -__STORE_STR2: - push hl + ld l, c ; HL = (HL) +__STORE_STR: + push de ; Pointer to b$ + push hl ; Pointer to a$ ld c, (hl) inc hl ld h, (hl) - ld l, c ; HL = *HL (real string address) - push de - call __MEM_FREE - pop de - pop hl + ld l, c ; HL = (HL) + call __STRASSIGN ; HL (a$) = DE (b$); HL changed to a new dynamic memory allocation + ex de, hl ; DE = new address of a$ + pop hl ; Recover variable memory address pointer ld (hl), e inc hl - ld (hl), d - dec hl ; HL points to mem address variable. This might be useful in the future. + ld (hl), d ; Stores a$ ptr into element ptr + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret pop namespace #line 113 "arch/zx48k/local_str_array3.bas" diff --git a/tests/functional/arch/zx48k/local_str_array4.asm b/tests/functional/arch/zx48k/local_str_array4.asm new file mode 100644 index 000000000..0bac348ed --- /dev/null +++ b/tests/functional/arch/zx48k/local_str_array4.asm @@ -0,0 +1,1025 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 4768 +.core.ZXBASIC_MEM_HEAP: + DEFS 4768 + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_index: + DEFB 00, 00 +_TABLENAMES: + DEFW .LABEL.__LABEL1 +_TABLENAMES.__DATA__.__PTR__: + DEFW _TABLENAMES.__DATA__ + DEFW 0 + DEFW 0 +_TABLENAMES.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL1: + DEFW 0000h + DEFB 02h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, _TABLENAMES + push hl + ld hl, .LABEL.__LABEL0 + call .core.__LOADSTR + push hl + call _Test + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +_Test: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+4) + ld h, (ix+5) + push hl + ld hl, (_index) + push hl + push ix + pop hl + ld de, 6 + add hl, de + call .core.__ARRAY_PTR + pop de + call .core.__STORE_STR +_Test__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +.LABEL.__LABEL0: + DEFW 0003h + DEFB 4Fh + DEFB 55h + DEFB 50h + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + ;; Performs a faster multiply for little 16bit numbs +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + LOCAL __MUL16LOOP + LOCAL __MUL16NOADD + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +__MUL16LOOP: + add hl, hl ; hl << 1 + sla c + rla ; a,c << 1 + jp nc, __MUL16NOADD + add hl, de +__MUL16NOADD: + djnz __MUL16LOOP + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + push namespace core +__FMUL16: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +1: + add hl, de + djnz 1b + ret + pop namespace +#line 20 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" +#line 24 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + push namespace core +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ;; HL <-- [HL] +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL TMP_ARR_PTR ; Ptr to Array DATA region. Stored temporarily + LOCAL LBOUND_PTR, UBOUND_PTR ; LBound and UBound PTR indexes + LOCAL RET_ADDR ; Contains the return address popped from the stack + LBOUND_PTR EQU 23698 ; Uses MEMBOT as a temporary variable + UBOUND_PTR EQU LBOUND_PTR + 2 ; Next 2 bytes for UBOUND PTR + RET_ADDR EQU UBOUND_PTR + 2 ; Next 2 bytes for RET_ADDR + TMP_ARR_PTR EQU RET_ADDR + 2 ; Next 2 bytes for TMP_ARR_PTR + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE <-- PTR to Dim sizes table + ld (TMP_ARR_PTR), hl ; HL = Array __DATA__.__PTR__ + inc hl + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __LBOUND__ PTR + ld (LBOUND_PTR), bc ; Store it for later +#line 66 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + ex de, hl ; HL <-- PTR to Dim sizes table, DE <-- dummy + ex (sp), hl ; Return address in HL, PTR Dim sizes table onto Stack + ld (RET_ADDR), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer to Dim sizes table + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Element Offset "accumulator" +LOOP: + ex de, hl ; DE = Element Offset + ld hl, (LBOUND_PTR) + ld a, h + or l + ld b, h + ld c, l + jr z, 1f + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (LBOUND_PTR), hl +1: + pop hl ; Get next index (Ai) from the stack + sbc hl, bc ; Subtract LBOUND +#line 116 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + add hl, de ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + dec bc ; Decrements loop counter + ld e, (hl) ; Loads next dimension size into D'E' + inc hl + ld d, (hl) + inc hl + push de + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FMUL16 ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 146 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 156 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start + ld de, (RET_ADDR) + push de + ret + ENDP + pop namespace +#line 60 "arch/zx48k/local_str_array4.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/loadstr.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be freed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; __MEM_INIT must be called to initalize this library with the + ; standard parameters + ; --------------------------------------------------------------------- + push namespace core +__MEM_INIT: ; Initializes the library using (RAMTOP) as start, and + ld hl, ZXBASIC_MEM_HEAP ; Change this with other address of heap start + ld de, ZXBASIC_HEAP_SIZE ; Change this with your size + ; --------------------------------------------------------------------- + ; __MEM_INIT2 initalizes this library +; Parameters: +; HL : Memory address of 1st byte of the memory heap +; DE : Length in bytes of the Memory Heap + ; --------------------------------------------------------------------- +__MEM_INIT2: + ; HL as TOP + PROC + dec de + dec de + dec de + dec de ; DE = length - 4; HL = start + ; This is done, because we require 4 bytes for the empty dummy-header block + xor a + ld (hl), a + inc hl + ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 + inc hl + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block + ld (hl), c + inc hl + ld (hl), b + inc hl ; Pointer to next block + ld (hl), e + inc hl + ld (hl), d + inc hl ; Block size (should be length - 4 at start); This block contains all the available memory + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret + ENDP + pop namespace +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ; --------------------------------------------------------------------- + ; MEM_ALLOC + ; Allocates a block of memory in the heap. + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 + TEMP EQU TEMP0 + 1 + ld hl, 0 + ld (TEMP), hl +__MEM_START: + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start + inc bc + inc bc ; BC = BC + 2 ; block size needs 2 extra bytes for hidden pointer +__MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE + ld a, h ; HL = NULL (No memory available?) + or l +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ; HL = Pointer to Free block + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = Block Length + push hl ; HL = *pointer to -> next block + ex de, hl + or a ; CF = 0 + sbc hl, bc ; FREE >= BC (Length) (HL = BlockLength - Length) + jp nc, __MEM_DONE + pop hl + ld (TEMP), hl + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + jp __MEM_LOOP +__MEM_DONE: ; A free block has been found. + ; Check if at least 4 bytes remains free (HL >= 4) + push hl + exx ; exx to preserve bc + pop hl + ld bc, 4 + or a + sbc hl, bc + exx + jp nc, __MEM_SUBTRACT + ; At this point... + ; less than 4 bytes remains free. So we return this block entirely + ; We must link the previous block with the next to this one + ; (DE) => Pointer to next block + ; (TEMP) => &(previous->next) + pop hl ; Discard current block pointer + push de + ex de, hl ; DE = Previous block pointer; (HL) = Next block pointer + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex de, hl ; HL = Previous block pointer; DE = Next block pointer +TEMP0: + ld hl, 0 ; Pre-previous block pointer + ld (hl), e + inc hl + ld (hl), d ; LINKED + pop hl ; Returning block. + ret +__MEM_SUBTRACT: + ; At this point we have to store HL value (Length - BC) into (DE - 2) + ex de, hl + dec hl + ld (hl), d + dec hl + ld (hl), e ; Store new block length + add hl, de ; New length + DE => free-block start + pop de ; Remove previous HL off the stack + ld (hl), c ; Store length on its 1st word + inc hl + ld (hl), b + inc hl ; Return hl + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/loadstr.asm" + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + push namespace core +__ILOADSTR: ; This is the indirect pointer entry HL = (HL) + ld a, h + or l + ret z + ld a, (hl) + inc hl + ld h, (hl) + ld l, a +__LOADSTR: ; __FASTCALL__ entry + ld a, h + or l + ret z ; Return if NULL + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(a$) + inc bc + inc bc ; BC = LEN(a$) + 2 (two bytes for length) + push hl + push bc + call __MEM_ALLOC + pop bc ; Recover length + pop de ; Recover origin + ld a, h + or l + ret z ; Return if NULL (No memory) + ex de, hl ; ldir takes HL as source, DE as destiny, so SWAP HL,DE + push de ; Saves destiny start + ldir ; Copies string (length number included) + pop hl ; Recovers destiny in hl as result + ret + pop namespace +#line 61 "arch/zx48k/local_str_array4.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- + push namespace core +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN + ld a, h + or l + ret z ; Return if NULL pointer + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous + or a ; Clear carry flag + sbc hl, bc ; Carry if BC > HL => This block if before + add hl, bc ; Restores HL, preserving Carry flag + jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) + inc hl + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 + call __MEM_JOIN_TEST + pop hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; + push hl ; Saves it for later + add hl, bc ; Adds its length. If HL == DE now, it must be joined + or a + sbc hl, de ; If Z, then HL == DE => We must join + pop hl + ret nz +__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC + push hl ; Saves it for later + ex de, hl + ld e, (hl) ; DE -> block->next->length + inc hl + ld d, (hl) + inc hl + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next + pop hl ; Recovers Pointer to block + ld (hl), c + inc hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved + ret + ENDP + pop namespace +#line 62 "arch/zx48k/local_str_array4.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/storestr.asm" +; vim:ts=4:et:sw=4 + ; Stores value of current string pointed by DE register into address pointed by HL + ; Returns DE = Address pointer (&a$) + ; Returns HL = HL (b$ => might be needed later to free it from the heap) + ; + ; e.g. => HL = _variableName (DIM _variableName$) + ; DE = Address into the HEAP + ; + ; This function will resize (REALLOC) the space pointed by HL + ; before copying the content of b$ into a$ +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/strcpy.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/realloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_REALLOC + ; Reallocates a block of memory in the heap. + ; + ; Parameters + ; HL = Pointer to the original block + ; BC = New Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; +; Notes: + ; If BC = 0, the block is freed, otherwise + ; the content of the original block is copied to the new one, and + ; the new size is adjusted. If BC < original length, the content + ; will be truncated. Otherwise, extra block content might contain + ; memory garbage. + ; + ; --------------------------------------------------------------------- + push namespace core +__REALLOC: ; Reallocates block pointed by HL, with new length BC + PROC + LOCAL __REALLOC_END + ld a, h + or l + jp z, __MEM_ALLOC ; If HL == NULL, just do a malloc + ld e, (hl) + inc hl + ld d, (hl) ; DE = First 2 bytes of HL block + push hl + exx + pop de + inc de ; DE' <- HL + 2 + exx ; DE' <- HL (Saves current pointer into DE') + dec hl ; HL = Block start + push de + push bc + call __MEM_FREE ; Frees current block + pop bc + push bc + call __MEM_ALLOC ; Gets a new block of length BC + pop bc + pop de + ld a, h + or l + ret z ; Return if HL == NULL (No memory) + ld (hl), e + inc hl + ld (hl), d + inc hl ; Recovers first 2 bytes in HL + dec bc + dec bc ; BC = BC - 2 (Two bytes copied) + ld a, b + or c + jp z, __REALLOC_END ; Ret if nothing to copy (BC == 0) + exx + push de + exx + pop de ; DE <- DE' ; Start of remaining block + push hl ; Saves current Block + 2 start + ex de, hl ; Exchanges them: DE is destiny block + ldir ; Copies BC Bytes + pop hl ; Recovers Block + 2 start +__REALLOC_END: + dec hl ; Set HL + dec hl ; To begin of block + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/strcpy.asm" + ; String library + push namespace core +__STRASSIGN: ; Performs a$ = b$ (HL = address of a$; DE = Address of b$) + PROC + LOCAL __STRREALLOC + LOCAL __STRCONTINUE + LOCAL __B_IS_NULL + LOCAL __NOTHING_TO_COPY + ld b, d + ld c, e + ld a, b + or c + jr z, __B_IS_NULL + ex de, hl + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(b$) + ex de, hl ; DE = &b$ +__B_IS_NULL: ; Jumps here if B$ pointer is NULL + inc bc + inc bc ; BC = BC + 2 ; (LEN(b$) + 2 bytes for storing length) + push de + push hl + ld a, h + or l + jr z, __STRREALLOC + dec hl + ld d, (hl) + dec hl + ld e, (hl) ; DE = MEMBLOCKSIZE(a$) + dec de + dec de ; DE = DE - 2 ; (Membloksize takes 2 bytes for memblock length) + ld h, b + ld l, c ; HL = LEN(b$) + 2 => Minimum block size required + ex de, hl ; Now HL = BLOCKSIZE(a$), DE = LEN(b$) + 2 + or a ; Prepare to subtract BLOCKSIZE(a$) - LEN(b$) + sbc hl, de ; Carry if len(b$) > Blocklen(a$) + jr c, __STRREALLOC ; No need to realloc + ; Need to reallocate at least to len(b$) + 2 + ex de, hl ; DE = Remaining bytes in a$ mem block. + ld hl, 4 + sbc hl, de ; if remaining bytes < 4 we can continue + jr nc,__STRCONTINUE ; Otherwise, we realloc, to free some bytes +__STRREALLOC: + pop hl + call __REALLOC ; Returns in HL a new pointer with BC bytes allocated + push hl +__STRCONTINUE: ; Pops hl and de SWAPPED + pop de ; DE = &a$ + pop hl ; HL = &b$ + ld a, d ; Return if not enough memory for new length + or e + ret z ; Return if DE == NULL (0) +__STRCPY: ; Copies string pointed by HL into string pointed by DE + ; Returns DE as HL (new pointer) + ld a, h + or l + jr z, __NOTHING_TO_COPY + ld c, (hl) + inc hl + ld b, (hl) + dec hl + inc bc + inc bc + push de + ldir + pop hl + ret +__NOTHING_TO_COPY: + ex de, hl + ld (hl), e + inc hl + ld (hl), d + dec hl + ret + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx48k/runtime/storestr.asm" + push namespace core +__PISTORE_STR: ; Indirect assignment at (IX + BC) + push ix + pop hl + add hl, bc +__ISTORE_STR: ; Indirect assignment, hl point to a pointer to a pointer to the heap! + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = (HL) +__STORE_STR: + push de ; Pointer to b$ + push hl ; Pointer to a$ + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = (HL) + call __STRASSIGN ; HL (a$) = DE (b$); HL changed to a new dynamic memory allocation + ex de, hl ; DE = new address of a$ + pop hl ; Recover variable memory address pointer + ld (hl), e + inc hl + ld (hl), d ; Stores a$ ptr into element ptr + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + pop namespace +#line 63 "arch/zx48k/local_str_array4.bas" + END diff --git a/tests/functional/arch/zx48k/local_str_array4.bas b/tests/functional/arch/zx48k/local_str_array4.bas new file mode 100644 index 000000000..19095ffcc --- /dev/null +++ b/tests/functional/arch/zx48k/local_str_array4.bas @@ -0,0 +1,8 @@ +DIM index As UInteger +DIM TABLENAMES(10) AS STRING + +SUB Test (value As String, BYREF HighScoreNames() AS STRING) + LET HighScoreNames(index) = value +END SUB + +Test("OUP", TABLENAMES) diff --git a/tests/functional/arch/zx48k/ltee7.asm b/tests/functional/arch/zx48k/ltee7.asm index 0fefc0ad0..5f068dba5 100644 --- a/tests/functional/arch/zx48k/ltee7.asm +++ b/tests/functional/arch/zx48k/ltee7.asm @@ -60,8 +60,8 @@ _setlocal: ld de, -6 add hl, de call .core.__ARRAY - pop de - call .core.__STORE_STR2 + ld de, .LABEL.__LABEL0 + call .core.__STORE_STR _setlocal__leave: ex af, af' exx @@ -244,7 +244,7 @@ ARRAY_SIZE_LOOP: pop namespace #line 60 "arch/zx48k/ltee7.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -255,7 +255,7 @@ ARRAY_SIZE_LOOP: ; closed source programs). ; ; Please read the MIT license on the internet -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -349,8 +349,8 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/heapinit.asm" +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -457,7 +457,7 @@ __MEM_INIT2: ret ENDP pop namespace -#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -488,9 +488,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ret z ; NULL -#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -555,7 +555,7 @@ __MEM_SUBTRACT: ret ENDP pop namespace -#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/calloc.asm" +#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" ; --------------------------------------------------------------------- ; MEM_CALLOC ; Allocates a block of memory in the heap, and clears it filling it @@ -660,7 +660,7 @@ __ALLOC_INITIALIZED_LOCAL_ARRAY: ; This routine is in charge of freeing an array of strings from memory ; HL = Pointer to start of array in memory ; Top of the stack = Number of elements of the array -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/free.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -861,37 +861,252 @@ __ARRAYSTR_FREE_MEM: ; like the above, buf also frees the array itself jp __MEM_FREE ; Frees it and returns from __MEM_FREE pop namespace #line 62 "arch/zx48k/ltee7.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/storestr2.asm" - ; Similar to __STORE_STR, but this one is called when - ; the value of B$ if already duplicated onto the stack. - ; So we needn't call STRASSING to create a duplication - ; HL = address of string memory variable - ; DE = address of 2n string. It just copies DE into (HL) - ; freeing (HL) previously. +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/storestr.asm" +; vim:ts=4:et:sw=4 + ; Stores value of current string pointed by DE register into address pointed by HL + ; Returns DE = Address pointer (&a$) + ; Returns HL = HL (b$ => might be needed later to free it from the heap) + ; + ; e.g. => HL = _variableName (DIM _variableName$) + ; DE = Address into the HEAP + ; + ; This function will resize (REALLOC) the space pointed by HL + ; before copying the content of b$ into a$ +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/strcpy.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/realloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_REALLOC + ; Reallocates a block of memory in the heap. + ; + ; Parameters + ; HL = Pointer to the original block + ; BC = New Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; +; Notes: + ; If BC = 0, the block is freed, otherwise + ; the content of the original block is copied to the new one, and + ; the new size is adjusted. If BC < original length, the content + ; will be truncated. Otherwise, extra block content might contain + ; memory garbage. + ; + ; --------------------------------------------------------------------- push namespace core -__PISTORE_STR2: ; Indirect store temporary string at (IX + BC) +__REALLOC: ; Reallocates block pointed by HL, with new length BC + PROC + LOCAL __REALLOC_END + ld a, h + or l + jp z, __MEM_ALLOC ; If HL == NULL, just do a malloc + ld e, (hl) + inc hl + ld d, (hl) ; DE = First 2 bytes of HL block + push hl + exx + pop de + inc de ; DE' <- HL + 2 + exx ; DE' <- HL (Saves current pointer into DE') + dec hl ; HL = Block start + push de + push bc + call __MEM_FREE ; Frees current block + pop bc + push bc + call __MEM_ALLOC ; Gets a new block of length BC + pop bc + pop de + ld a, h + or l + ret z ; Return if HL == NULL (No memory) + ld (hl), e + inc hl + ld (hl), d + inc hl ; Recovers first 2 bytes in HL + dec bc + dec bc ; BC = BC - 2 (Two bytes copied) + ld a, b + or c + jp z, __REALLOC_END ; Ret if nothing to copy (BC == 0) + exx + push de + exx + pop de ; DE <- DE' ; Start of remaining block + push hl ; Saves current Block + 2 start + ex de, hl ; Exchanges them: DE is destiny block + ldir ; Copies BC Bytes + pop hl ; Recovers Block + 2 start +__REALLOC_END: + dec hl ; Set HL + dec hl ; To begin of block + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/strcpy.asm" + ; String library + push namespace core +__STRASSIGN: ; Performs a$ = b$ (HL = address of a$; DE = Address of b$) + PROC + LOCAL __STRREALLOC + LOCAL __STRCONTINUE + LOCAL __B_IS_NULL + LOCAL __NOTHING_TO_COPY + ld b, d + ld c, e + ld a, b + or c + jr z, __B_IS_NULL + ex de, hl + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(b$) + ex de, hl ; DE = &b$ +__B_IS_NULL: ; Jumps here if B$ pointer is NULL + inc bc + inc bc ; BC = BC + 2 ; (LEN(b$) + 2 bytes for storing length) + push de + push hl + ld a, h + or l + jr z, __STRREALLOC + dec hl + ld d, (hl) + dec hl + ld e, (hl) ; DE = MEMBLOCKSIZE(a$) + dec de + dec de ; DE = DE - 2 ; (Membloksize takes 2 bytes for memblock length) + ld h, b + ld l, c ; HL = LEN(b$) + 2 => Minimum block size required + ex de, hl ; Now HL = BLOCKSIZE(a$), DE = LEN(b$) + 2 + or a ; Prepare to subtract BLOCKSIZE(a$) - LEN(b$) + sbc hl, de ; Carry if len(b$) > Blocklen(a$) + jr c, __STRREALLOC ; No need to realloc + ; Need to reallocate at least to len(b$) + 2 + ex de, hl ; DE = Remaining bytes in a$ mem block. + ld hl, 4 + sbc hl, de ; if remaining bytes < 4 we can continue + jr nc,__STRCONTINUE ; Otherwise, we realloc, to free some bytes +__STRREALLOC: + pop hl + call __REALLOC ; Returns in HL a new pointer with BC bytes allocated + push hl +__STRCONTINUE: ; Pops hl and de SWAPPED + pop de ; DE = &a$ + pop hl ; HL = &b$ + ld a, d ; Return if not enough memory for new length + or e + ret z ; Return if DE == NULL (0) +__STRCPY: ; Copies string pointed by HL into string pointed by DE + ; Returns DE as HL (new pointer) + ld a, h + or l + jr z, __NOTHING_TO_COPY + ld c, (hl) + inc hl + ld b, (hl) + dec hl + inc bc + inc bc + push de + ldir + pop hl + ret +__NOTHING_TO_COPY: + ex de, hl + ld (hl), e + inc hl + ld (hl), d + dec hl + ret + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx48k/runtime/storestr.asm" + push namespace core +__PISTORE_STR: ; Indirect assignment at (IX + BC) push ix pop hl add hl, bc -__ISTORE_STR2: - ld c, (hl) ; Dereferences HL +__ISTORE_STR: ; Indirect assignment, hl point to a pointer to a pointer to the heap! + ld c, (hl) inc hl ld h, (hl) - ld l, c ; HL = *HL (real string variable address) -__STORE_STR2: - push hl + ld l, c ; HL = (HL) +__STORE_STR: + push de ; Pointer to b$ + push hl ; Pointer to a$ ld c, (hl) inc hl ld h, (hl) - ld l, c ; HL = *HL (real string address) - push de - call __MEM_FREE - pop de - pop hl + ld l, c ; HL = (HL) + call __STRASSIGN ; HL (a$) = DE (b$); HL changed to a new dynamic memory allocation + ex de, hl ; DE = new address of a$ + pop hl ; Recover variable memory address pointer ld (hl), e inc hl - ld (hl), d - dec hl ; HL points to mem address variable. This might be useful in the future. + ld (hl), d ; Stores a$ ptr into element ptr + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) ret pop namespace #line 63 "arch/zx48k/ltee7.bas" diff --git a/tests/functional/arch/zx48k/ltee9.asm b/tests/functional/arch/zx48k/ltee9.asm index f08516af1..f32f00449 100644 --- a/tests/functional/arch/zx48k/ltee9.asm +++ b/tests/functional/arch/zx48k/ltee9.asm @@ -277,7 +277,6 @@ __TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) ld l, c ret pop namespace - f #line 85 "arch/zx48k/ltee9.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" ; SUB32 diff --git a/tests/functional/arch/zx48k/mul32.asm b/tests/functional/arch/zx48k/mul32.asm new file mode 100644 index 000000000..a2eb9a4fb --- /dev/null +++ b/tests/functional/arch/zx48k/mul32.asm @@ -0,0 +1,154 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +_b: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a + 2) + ld hl, 0 + ld d, h + ld e, l + ld (_b), hl + ld (_b + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld (_b), hl + ld (_b + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld hl, 0 + ld d, h + ld e, l + ld (_b), hl + ld (_b + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld (_b), hl + ld (_b + 2), de + ld hl, (_a + 2) + push hl + ld hl, (_a) + push hl + ld hl, (_a) + ld de, (_a + 2) + call .core.__MUL32 + ld (_b), hl + ld (_b + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/_mul32.asm" +; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 + ; Used with permission. + ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') + ; 64bit result is returned in H'L'H L B'C'A C + push namespace core +__MUL32_64START: + push hl + exx + ld b, h + ld c, l ; BC = Low Part (A) + pop hl ; HL = Load Part (B) + ex de, hl ; DE = Low Part (B), HL = HightPart(A) (must be in B'C') + push hl + exx + pop bc ; B'C' = HightPart(A) + exx ; A = B'C'BC , B = D'E'DE + ; multiply routine 32 * 32bit = 64bit + ; h'l'hlb'c'ac = b'c'bc * d'e'de + ; needs register a, changes flags + ; + ; this routine was with tiny differences in the + ; sinclair zx81 rom for the mantissa multiply +__LMUL: + xor a ; reset carry flag + ld h, a ; result bits 32..47 = 0 + ld l, a + exx + ld h, a ; result bits 48..63 = 0 + ld l, a + exx + ld a,b ; mpr is b'c'ac + ld b,33 ; initialize loop counter + jp __LMULSTART +__LMULLOOP: + jr nc,__LMULNOADD ; JP is 2 cycles faster than JR. Since it's inside a LOOP + ; it can save up to 33 * 2 = 66 cycles + ; But JR if 3 cycles faster if JUMP not taken! + add hl,de ; result += mpd + exx + adc hl,de + exx +__LMULNOADD: + exx + rr h ; right shift upper + rr l ; 32bit of result + exx + rr h + rr l +__LMULSTART: + exx + rr b ; right shift mpr/ + rr c ; lower 32bit of result + exx + rra ; equivalent to rr a + rr c + djnz __LMULLOOP + ret ; result in h'l'hlb'c'ac + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" + push namespace core +__MUL32: + ; multiplies 32 bit un/signed integer. + ; First operand stored in DEHL, and 2nd onto stack + ; Lowest part of 2nd operand on top of the stack + ; returns the result in DE.HL + exx + pop hl ; Return ADDRESS + pop de ; Low part + ex (sp), hl ; CALLEE -> HL = High part + ex de, hl + call __MUL32_64START +__TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) + exx + push bc + exx + pop de + ld h, a + ld l, c + ret + pop namespace +#line 48 "arch/zx48k/mul32.bas" + END diff --git a/tests/functional/arch/zx48k/mul32.bas b/tests/functional/arch/zx48k/mul32.bas new file mode 100644 index 000000000..e72b22271 --- /dev/null +++ b/tests/functional/arch/zx48k/mul32.bas @@ -0,0 +1,10 @@ +' TEST for ADD16 + +DIM a as ULong +DIM b as ULong + +b = a * 0 +b = a * 1 +b = 0 * a +b = 1 * a +b = a * a diff --git a/tests/functional/arch/zx48k/mul32a.asm b/tests/functional/arch/zx48k/mul32a.asm new file mode 100644 index 000000000..5777ebff5 --- /dev/null +++ b/tests/functional/arch/zx48k/mul32a.asm @@ -0,0 +1,135 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a + 2) + push hl + ld hl, (_a) + push hl + ld hl, (_a) + ld de, (_a + 2) + call .core.__MUL32 + push de + push hl + ld hl, (_a) + ld de, (_a + 2) + call .core.__MUL32 + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/_mul32.asm" +; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 + ; Used with permission. + ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') + ; 64bit result is returned in H'L'H L B'C'A C + push namespace core +__MUL32_64START: + push hl + exx + ld b, h + ld c, l ; BC = Low Part (A) + pop hl ; HL = Load Part (B) + ex de, hl ; DE = Low Part (B), HL = HightPart(A) (must be in B'C') + push hl + exx + pop bc ; B'C' = HightPart(A) + exx ; A = B'C'BC , B = D'E'DE + ; multiply routine 32 * 32bit = 64bit + ; h'l'hlb'c'ac = b'c'bc * d'e'de + ; needs register a, changes flags + ; + ; this routine was with tiny differences in the + ; sinclair zx81 rom for the mantissa multiply +__LMUL: + xor a ; reset carry flag + ld h, a ; result bits 32..47 = 0 + ld l, a + exx + ld h, a ; result bits 48..63 = 0 + ld l, a + exx + ld a,b ; mpr is b'c'ac + ld b,33 ; initialize loop counter + jp __LMULSTART +__LMULLOOP: + jr nc,__LMULNOADD ; JP is 2 cycles faster than JR. Since it's inside a LOOP + ; it can save up to 33 * 2 = 66 cycles + ; But JR if 3 cycles faster if JUMP not taken! + add hl,de ; result += mpd + exx + adc hl,de + exx +__LMULNOADD: + exx + rr h ; right shift upper + rr l ; 32bit of result + exx + rr h + rr l +__LMULSTART: + exx + rr b ; right shift mpr/ + rr c ; lower 32bit of result + exx + rra ; equivalent to rr a + rr c + djnz __LMULLOOP + ret ; result in h'l'hlb'c'ac + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" + push namespace core +__MUL32: + ; multiplies 32 bit un/signed integer. + ; First operand stored in DEHL, and 2nd onto stack + ; Lowest part of 2nd operand on top of the stack + ; returns the result in DE.HL + exx + pop hl ; Return ADDRESS + pop de ; Low part + ex (sp), hl ; CALLEE -> HL = High part + ex de, hl + call __MUL32_64START +__TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) + exx + push bc + exx + pop de + ld h, a + ld l, c + ret + pop namespace +#line 31 "arch/zx48k/mul32a.bas" + END diff --git a/tests/functional/arch/zx48k/mul32a.bas b/tests/functional/arch/zx48k/mul32a.bas new file mode 100644 index 000000000..6e5a4b154 --- /dev/null +++ b/tests/functional/arch/zx48k/mul32a.bas @@ -0,0 +1,4 @@ + +REM another MUL16 test +DIM a As Long +a = a * a * a diff --git a/tests/functional/arch/zx48k/mul32b.asm b/tests/functional/arch/zx48k/mul32b.asm new file mode 100644 index 000000000..ac6b32790 --- /dev/null +++ b/tests/functional/arch/zx48k/mul32b.asm @@ -0,0 +1,145 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a + 2) + push hl + ld hl, (_a) + push hl + ld hl, (_a) + ld de, (_a + 2) + call .core.__MUL32 + push de + push hl + ld hl, (_a + 2) + push hl + ld hl, (_a) + push hl + ld de, 0 + ld hl, 2 + call .core.__MUL32 + push de + push hl + ld hl, (_a) + ld de, (_a + 2) + call .core.__MUL32 + call .core.__MUL32 + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/_mul32.asm" +; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 + ; Used with permission. + ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') + ; 64bit result is returned in H'L'H L B'C'A C + push namespace core +__MUL32_64START: + push hl + exx + ld b, h + ld c, l ; BC = Low Part (A) + pop hl ; HL = Load Part (B) + ex de, hl ; DE = Low Part (B), HL = HightPart(A) (must be in B'C') + push hl + exx + pop bc ; B'C' = HightPart(A) + exx ; A = B'C'BC , B = D'E'DE + ; multiply routine 32 * 32bit = 64bit + ; h'l'hlb'c'ac = b'c'bc * d'e'de + ; needs register a, changes flags + ; + ; this routine was with tiny differences in the + ; sinclair zx81 rom for the mantissa multiply +__LMUL: + xor a ; reset carry flag + ld h, a ; result bits 32..47 = 0 + ld l, a + exx + ld h, a ; result bits 48..63 = 0 + ld l, a + exx + ld a,b ; mpr is b'c'ac + ld b,33 ; initialize loop counter + jp __LMULSTART +__LMULLOOP: + jr nc,__LMULNOADD ; JP is 2 cycles faster than JR. Since it's inside a LOOP + ; it can save up to 33 * 2 = 66 cycles + ; But JR if 3 cycles faster if JUMP not taken! + add hl,de ; result += mpd + exx + adc hl,de + exx +__LMULNOADD: + exx + rr h ; right shift upper + rr l ; 32bit of result + exx + rr h + rr l +__LMULSTART: + exx + rr b ; right shift mpr/ + rr c ; lower 32bit of result + exx + rra ; equivalent to rr a + rr c + djnz __LMULLOOP + ret ; result in h'l'hlb'c'ac + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" + push namespace core +__MUL32: + ; multiplies 32 bit un/signed integer. + ; First operand stored in DEHL, and 2nd onto stack + ; Lowest part of 2nd operand on top of the stack + ; returns the result in DE.HL + exx + pop hl ; Return ADDRESS + pop de ; Low part + ex (sp), hl ; CALLEE -> HL = High part + ex de, hl + call __MUL32_64START +__TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) + exx + push bc + exx + pop de + ld h, a + ld l, c + ret + pop namespace +#line 41 "arch/zx48k/mul32b.bas" + END diff --git a/tests/functional/arch/zx48k/mul32b.bas b/tests/functional/arch/zx48k/mul32b.bas new file mode 100644 index 000000000..be0b14e49 --- /dev/null +++ b/tests/functional/arch/zx48k/mul32b.bas @@ -0,0 +1,4 @@ + +REM another ADD16 tests +DIM a As Long +a = a * a * (a * 2 * a) diff --git a/tests/functional/arch/zx48k/mul32c.asm b/tests/functional/arch/zx48k/mul32c.asm new file mode 100644 index 000000000..434009422 --- /dev/null +++ b/tests/functional/arch/zx48k/mul32c.asm @@ -0,0 +1,130 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a + 2) + push hl + ld hl, (_a) + push hl + ld de, 65535 + ld hl, 65535 + call .core.__MUL32 + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/_mul32.asm" +; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 + ; Used with permission. + ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') + ; 64bit result is returned in H'L'H L B'C'A C + push namespace core +__MUL32_64START: + push hl + exx + ld b, h + ld c, l ; BC = Low Part (A) + pop hl ; HL = Load Part (B) + ex de, hl ; DE = Low Part (B), HL = HightPart(A) (must be in B'C') + push hl + exx + pop bc ; B'C' = HightPart(A) + exx ; A = B'C'BC , B = D'E'DE + ; multiply routine 32 * 32bit = 64bit + ; h'l'hlb'c'ac = b'c'bc * d'e'de + ; needs register a, changes flags + ; + ; this routine was with tiny differences in the + ; sinclair zx81 rom for the mantissa multiply +__LMUL: + xor a ; reset carry flag + ld h, a ; result bits 32..47 = 0 + ld l, a + exx + ld h, a ; result bits 48..63 = 0 + ld l, a + exx + ld a,b ; mpr is b'c'ac + ld b,33 ; initialize loop counter + jp __LMULSTART +__LMULLOOP: + jr nc,__LMULNOADD ; JP is 2 cycles faster than JR. Since it's inside a LOOP + ; it can save up to 33 * 2 = 66 cycles + ; But JR if 3 cycles faster if JUMP not taken! + add hl,de ; result += mpd + exx + adc hl,de + exx +__LMULNOADD: + exx + rr h ; right shift upper + rr l ; 32bit of result + exx + rr h + rr l +__LMULSTART: + exx + rr b ; right shift mpr/ + rr c ; lower 32bit of result + exx + rra ; equivalent to rr a + rr c + djnz __LMULLOOP + ret ; result in h'l'hlb'c'ac + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" + push namespace core +__MUL32: + ; multiplies 32 bit un/signed integer. + ; First operand stored in DEHL, and 2nd onto stack + ; Lowest part of 2nd operand on top of the stack + ; returns the result in DE.HL + exx + pop hl ; Return ADDRESS + pop de ; Low part + ex (sp), hl ; CALLEE -> HL = High part + ex de, hl + call __MUL32_64START +__TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) + exx + push bc + exx + pop de + ld h, a + ld l, c + ret + pop namespace +#line 26 "arch/zx48k/mul32c.bas" + END diff --git a/tests/functional/arch/zx48k/mul32c.bas b/tests/functional/arch/zx48k/mul32c.bas new file mode 100644 index 000000000..43d6f9123 --- /dev/null +++ b/tests/functional/arch/zx48k/mul32c.bas @@ -0,0 +1,3 @@ +DIM a as Long + +a = -1 * a diff --git a/tests/functional/arch/zx48k/opt2_arr_elem_by_ref01.asm b/tests/functional/arch/zx48k/opt2_arr_elem_by_ref01.asm new file mode 100644 index 000000000..a2d747ed2 --- /dev/null +++ b/tests/functional/arch/zx48k/opt2_arr_elem_by_ref01.asm @@ -0,0 +1,74 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFW .LABEL.__LABEL0 +_a.__DATA__.__PTR__: + DEFW _a.__DATA__ + DEFW 0 + DEFW 0 +_a.__DATA__: + DEFB 01h + DEFB 02h + DEFB 03h +.LABEL.__LABEL0: + DEFW 0000h + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, _a.__DATA__ + inc hl + push hl + call _Incr + ld a, (_a.__DATA__ + 1) + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +_Incr: + push ix + ld ix, 0 + add ix, sp + ld h, (ix+5) + ld l, (ix+4) + ld a, (hl) + inc a + ld h, (ix+5) + ld l, (ix+4) + ld (hl), a +_Incr__leave: + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx48k/opt2_arr_elem_by_ref01.bas b/tests/functional/arch/zx48k/opt2_arr_elem_by_ref01.bas new file mode 100644 index 000000000..d4e8fc9af --- /dev/null +++ b/tests/functional/arch/zx48k/opt2_arr_elem_by_ref01.bas @@ -0,0 +1,10 @@ +REM Test passing an array element by ref + +DIM a(2) As UByte => {1, 2, 3} + +SUB Incr(ByRef x As UByte) + LET x = x + 1 +END SUB + +Incr(a(1)) +POKE 0, a(1) diff --git a/tests/functional/arch/zx48k/opt2_arr_elem_by_ref04.asm b/tests/functional/arch/zx48k/opt2_arr_elem_by_ref04.asm new file mode 100644 index 000000000..ad200e3bb --- /dev/null +++ b/tests/functional/arch/zx48k/opt2_arr_elem_by_ref04.asm @@ -0,0 +1,246 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_i: + DEFB 03h + DEFB 00h +_myArray: + DEFW .LABEL.__LABEL0 +_myArray.__DATA__.__PTR__: + DEFW _myArray.__DATA__ + DEFW 0 + DEFW 0 +_myArray.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL0: + DEFW 0000h + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_i) + push hl + ld hl, _myArray + call .core.__ARRAY + push hl + call _plusOne + ld hl, (_i) + push hl + ld hl, _myArray + call .core.__ARRAY + ld a, (hl) + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret +_plusOne: + push ix + ld ix, 0 + add ix, sp + ld h, (ix+5) + ld l, (ix+4) + ld a, (hl) + inc a + ld h, (ix+5) + ld l, (ix+4) + ld (hl), a +_plusOne__leave: + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + ;; Performs a faster multiply for little 16bit numbs +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + LOCAL __MUL16LOOP + LOCAL __MUL16NOADD + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +__MUL16LOOP: + add hl, hl ; hl << 1 + sla c + rla ; a,c << 1 + jp nc, __MUL16NOADD + add hl, de +__MUL16NOADD: + djnz __MUL16LOOP + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + push namespace core +__FMUL16: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +1: + add hl, de + djnz 1b + ret + pop namespace +#line 20 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" +#line 24 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + push namespace core +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ;; HL <-- [HL] +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL TMP_ARR_PTR ; Ptr to Array DATA region. Stored temporarily + LOCAL LBOUND_PTR, UBOUND_PTR ; LBound and UBound PTR indexes + LOCAL RET_ADDR ; Contains the return address popped from the stack + LBOUND_PTR EQU 23698 ; Uses MEMBOT as a temporary variable + UBOUND_PTR EQU LBOUND_PTR + 2 ; Next 2 bytes for UBOUND PTR + RET_ADDR EQU UBOUND_PTR + 2 ; Next 2 bytes for RET_ADDR + TMP_ARR_PTR EQU RET_ADDR + 2 ; Next 2 bytes for TMP_ARR_PTR + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE <-- PTR to Dim sizes table + ld (TMP_ARR_PTR), hl ; HL = Array __DATA__.__PTR__ + inc hl + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __LBOUND__ PTR + ld (LBOUND_PTR), bc ; Store it for later +#line 66 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + ex de, hl ; HL <-- PTR to Dim sizes table, DE <-- dummy + ex (sp), hl ; Return address in HL, PTR Dim sizes table onto Stack + ld (RET_ADDR), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer to Dim sizes table + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Element Offset "accumulator" +LOOP: + ex de, hl ; DE = Element Offset + ld hl, (LBOUND_PTR) + ld a, h + or l + ld b, h + ld c, l + jr z, 1f + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (LBOUND_PTR), hl +1: + pop hl ; Get next index (Ai) from the stack + sbc hl, bc ; Subtract LBOUND +#line 116 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + add hl, de ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + dec bc ; Decrements loop counter + ld e, (hl) ; Loads next dimension size into D'E' + inc hl + ld d, (hl) + inc hl + push de + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FMUL16 ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 146 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 156 "/zxbasic/src/lib/arch/zx48k/runtime/array/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start + ld de, (RET_ADDR) + push de + ret + ENDP + pop namespace +#line 48 "arch/zx48k/opt2_arr_elem_by_ref04.bas" + END diff --git a/tests/functional/arch/zx48k/opt2_arr_elem_by_ref04.bas b/tests/functional/arch/zx48k/opt2_arr_elem_by_ref04.bas new file mode 100644 index 000000000..8c8767d3e --- /dev/null +++ b/tests/functional/arch/zx48k/opt2_arr_elem_by_ref04.bas @@ -0,0 +1,11 @@ +SUB plusOne(ByRef a As Ubyte) + LET a = a + 1 +END SUB + +DIM i as UInteger = 3 +DIM myArray(5) as Ubyte + +plusOne(myArray(i)) +POKE 0, myArray(i) + + diff --git a/tests/functional/arch/zx48k/opt2_in_opt.asm b/tests/functional/arch/zx48k/opt2_in_opt.asm new file mode 100644 index 000000000..a6e665656 --- /dev/null +++ b/tests/functional/arch/zx48k/opt2_in_opt.asm @@ -0,0 +1,38 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld bc, 0 + in a, (c) + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx48k/opt2_in_opt.bas b/tests/functional/arch/zx48k/opt2_in_opt.bas new file mode 100644 index 000000000..6d8e01008 --- /dev/null +++ b/tests/functional/arch/zx48k/opt2_in_opt.bas @@ -0,0 +1,3 @@ +REM USR 0 must be compiled despite c being optimized +LET c = IN 0 + diff --git a/tests/functional/arch/zx48k/opt2_rnd_opt.asm b/tests/functional/arch/zx48k/opt2_rnd_opt.asm new file mode 100644 index 000000000..de07cdd67 --- /dev/null +++ b/tests/functional/arch/zx48k/opt2_rnd_opt.asm @@ -0,0 +1,127 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call .core.RND + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/random.asm" + ; RANDOM functions + push namespace core +RANDOMIZE: + ; Randomize with 32 bit seed in DE HL + ; if SEED = 0, calls ROM to take frames as seed + PROC + LOCAL TAKE_FRAMES + LOCAL FRAMES + ld a, h + or l + or d + or e + jr z, TAKE_FRAMES + ld (RANDOM_SEED_LOW), hl + ld (RANDOM_SEED_HIGH), de + ret +TAKE_FRAMES: + ; Takes the seed from frames + ld hl, (FRAMES) + ld (RANDOM_SEED_LOW), hl + ld hl, (FRAMES + 2) + ld (RANDOM_SEED_HIGH), hl + ret + FRAMES EQU 23672 + ENDP + RANDOM_SEED_HIGH EQU RAND+1 ; RANDOM seed, 16 higher bits + RANDOM_SEED_LOW EQU 23670 ; RANDOM seed, 16 lower bits +RAND: + PROC + ld de,0C0DEh ; yw -> zt + ld hl,(RANDOM_SEED_LOW) ; xz -> yw + ld (RANDOM_SEED_LOW),de ; x = y, z = w + ld a,e ; w = w ^ ( w << 3 ) + add a,a + add a,a + add a,a + xor e + ld e,a + ld a,h ; t = x ^ (x << 1) + add a,a + xor h + ld d,a + rra ; t = t ^ (t >> 1) ^ w + xor d + xor e + ld d,l ; y = z + ld e,a ; w = t + ld (RANDOM_SEED_HIGH),de + ret + ENDP +RND: + ; Returns a FLOATING point integer + ; using RAND as a mantissa + PROC + LOCAL RND_LOOP + call RAND + ; BC = HL since ZX BASIC uses ED CB A registers for FP + ld b, h + ld c, l + ld a, e + or d + or c + or b + ret z ; Returns 0 if BC=DE=0 + ; We already have a random 32 bit mantissa in ED CB + ; From 0001h to FFFFh + ld l, 81h ; Exponent + ; At this point we have [0 .. 1) FP number; + ; Now we must shift mantissa left until highest bit goes into carry + ld a, e ; Use A register for rotating E faster (using RLA instead of RL E) +RND_LOOP: + dec l + sla b + rl c + rl d + rla + jp nc, RND_LOOP + ; Now undo last mantissa left-shift once + ccf ; Clears carry to insert a 0 bit back into mantissa -> positive FP number + rra + rr d + rr c + rr b + ld e, a ; E must have the highest byte + ld a, l ; exponent in A + ret + ENDP + pop namespace +#line 18 "arch/zx48k/opt2_rnd_opt.bas" + END diff --git a/tests/functional/arch/zx48k/opt2_rnd_opt.bas b/tests/functional/arch/zx48k/opt2_rnd_opt.bas new file mode 100644 index 000000000..bf14adb3f --- /dev/null +++ b/tests/functional/arch/zx48k/opt2_rnd_opt.bas @@ -0,0 +1,3 @@ +REM USR 0 must be compiled despite c being optimized +LET c = RND + diff --git a/tests/functional/arch/zx48k/opt2_usr_opt.asm b/tests/functional/arch/zx48k/opt2_usr_opt.asm new file mode 100644 index 000000000..d43d09267 --- /dev/null +++ b/tests/functional/arch/zx48k/opt2_usr_opt.asm @@ -0,0 +1,73 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + call .core.USR + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + pop iy + pop ix + exx + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/usr.asm" + ; Emulates the USR Sinclair BASIC function + ; Result value returns in BC + ; We use HL for returning values, su we must + ; copy BC into HL before returning + ; + ; The incoming parameter is HL (Address to JUMP) + ; +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/table_jump.asm" + push namespace core +JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A + add a, a +JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE + ld e, a + ld d, 0 +JUMP_HL_PLUS_DE: ; Does JP (HL + DE) + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl +CALL_HL: + jp (hl) + pop namespace +#line 10 "/zxbasic/src/lib/arch/zx48k/runtime/usr.asm" + push namespace core +USR: + push ix ; must preserve IX + call CALL_HL + pop ix + ld h, b + ld l, c + ret + pop namespace +#line 19 "arch/zx48k/opt2_usr_opt.bas" + END diff --git a/tests/functional/arch/zx48k/opt2_usr_opt.bas b/tests/functional/arch/zx48k/opt2_usr_opt.bas new file mode 100644 index 000000000..9e2df382a --- /dev/null +++ b/tests/functional/arch/zx48k/opt2_usr_opt.bas @@ -0,0 +1,3 @@ +REM USR 0 must be compiled despite c being optimized +LET c = USR 0 + diff --git a/tests/functional/arch/zx48k/print42.asm b/tests/functional/arch/zx48k/print42.asm index 2f7070389..904c893f7 100644 --- a/tests/functional/arch/zx48k/print42.asm +++ b/tests/functional/arch/zx48k/print42.asm @@ -73,6 +73,10 @@ _print42: LOCAL examineChar examineChar: LD A,(HL) + CP 165 + JR NC, nextChar + CP 144 + JR NC, prn CP 128 JR NC, nextChar CP 22 @@ -144,6 +148,8 @@ printachar: EXX PUSH HL EXX + CP 144 + jr nc,printudg ld c, a ld h, 0 ld l, a @@ -158,6 +164,18 @@ printachar: ld b, h ld c, l jr printdata + LOCAL printudg + printudg + sub 144 + ld hl,$5C7B + ld e,(hl) + inc hl + ld d,(hl) + ld l,a + call mult8 + ld b, h + ld c, l + jr printdata LOCAL calcChar calcChar: ld de, 15360 @@ -315,9 +333,9 @@ ycoord: ret c ld d, 0 ret -#line 329 "/zxbasic/src/lib/arch/zx48k/stdlib/print42.bas" +#line 351 "/zxbasic/src/lib/arch/zx48k/stdlib/print42.bas" .LABEL._printAt42Coords: -#line 330 "/zxbasic/src/lib/arch/zx48k/stdlib/print42.bas" +#line 352 "/zxbasic/src/lib/arch/zx48k/stdlib/print42.bas" LOCAL xycoords xycoords: defb 0 @@ -491,7 +509,7 @@ characters: LOCAL print42end print42end: ENDP -#line 522 "/zxbasic/src/lib/arch/zx48k/stdlib/print42.bas" +#line 544 "/zxbasic/src/lib/arch/zx48k/stdlib/print42.bas" _print42__leave: ex af, af' exx @@ -508,7 +526,7 @@ _print42__leave: exx ret ;; --- end of user code --- -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/free.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -568,7 +586,7 @@ _print42__leave: ; HL = BLOCK Start & DE = Length. ; An init directive is useful for initialization routines. ; They will be added automatically if needed. -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/heapinit.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -675,7 +693,7 @@ __MEM_INIT2: ret ENDP pop namespace -#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/free.asm" +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -774,5 +792,5 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ret ENDP pop namespace -#line 540 "/zxbasic/src/lib/arch/zx48k/stdlib/print42.bas" +#line 562 "/zxbasic/src/lib/arch/zx48k/stdlib/print42.bas" END diff --git a/tests/functional/arch/zx48k/print_at2.asm b/tests/functional/arch/zx48k/print_at2.asm new file mode 100644 index 000000000..8486934b8 --- /dev/null +++ b/tests/functional/arch/zx48k/print_at2.asm @@ -0,0 +1,962 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_row: + DEFB 00 +_col: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call .core.COPY_ATTR + ld a, (_row) + add a, 2 + push af + ld a, (_col) + inc a + call .core.PRINT_AT + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +; vim:ts=4:sw=4:et: + ; PRINT command routine + ; Does not print attribute. Use PRINT_STR or PRINT_NUM for that +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; Flags for controlling output to screen + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 6 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/in_screen.asm" + push namespace core +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits + PROC + LOCAL __IN_SCREEN_ERR + ld hl, SCR_SIZE + ld a, e + cp l + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld a, d + cp h + ret c ; Return if carry (OK) +__IN_SCREEN_ERR: +__OUT_OF_SCREEN_ERR: + ; Jumps here if out of screen + ld a, ERROR_OutOfScreen + jp __STOP ; Saves error code and exits + ENDP + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + push namespace core +__ATTR_ADDR: + ; calc start address in DE (as (32 * d) + e) + ; Contributed by Santiago Romero at http://www.speccy.org + ld h, 0 ; 7 T-States + ld a, d ; 4 T-States + ld d, h + add a, a ; a * 2 ; 4 T-States + add a, a ; a * 4 ; 4 T-States + ld l, a ; HL = A * 4 ; 4 T-States + add hl, hl ; HL = A * 8 ; 15 T-States + add hl, hl ; HL = A * 16 ; 15 T-States + add hl, hl ; HL = A * 32 ; 15 T-States + add hl, de + ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address + add hl, de + ; Return current screen address in HL + ret + ; Sets the attribute at a given screen coordinate (D, E). + ; The attribute is taken from the ATTR_T memory variable + ; Used by PRINT routines +SET_ATTR: + ; Checks for valid coords + call __IN_SCREEN + ret nc + call __ATTR_ADDR +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + ; HL contains the address of the ATTR cell to set + PROC +__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + ld a, d + and (hl) + ld c, a ; C = current screen color, masked + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + ret + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" + ; Printing positioning library. + push namespace core + ; Loads into DE current ROW, COL print position from S_POSN mem var. +__LOAD_S_POSN: + PROC + ld de, (S_POSN) + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + ret + ENDP + ; Saves ROW, COL from DE into S_POSN mem var. +__SAVE_S_POSN: + PROC + ld hl, SCR_SIZE + or a + sbc hl, de + ld (S_POSN), hl ; saves it again +__SET_SCR_PTR: ;; Fast + push de + call __ATTR_ADDR + ld (DFCCL), hl + pop de + ld a, d + ld c, a ; Saves it for later + and 0F8h ; Masks 3 lower bit ; zy + ld d, a + ld a, c ; Recovers it + and 07h ; MOD 7 ; y1 + rrca + rrca + rrca + or e + ld e, a + ld hl, (SCREEN_ADDR) + add hl, de ; HL = Screen address + DE + ld (DFCC), hl + ret + ENDP + pop namespace +#line 6 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/table_jump.asm" + push namespace core +JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A + add a, a +JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE + ld e, a + ld d, 0 +JUMP_HL_PLUS_DE: ; Does JP (HL + DE) + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl +CALL_HL: + jp (hl) + pop namespace +#line 8 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/ink.asm" + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 + ld de, ATTR_P +__SET_INK: + cp 8 + jr nz, __SET_INK2 + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + or 7 ; Set bits 0,1,2 to enable transparency + ld (de), a + ret +__SET_INK2: + ; Another entry. This will set the ink color at location pointer by DE + and 7 ; # Gets color mod 8 + ld b, a ; Saves the color + ld a, (de) + and 0F8h ; Clears previous value + or b + ld (de), a + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + and 0F8h ; Reset bits 0,1,2 sign to disable transparency + ld (de), a ; Store new attr + ret + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + ld de, ATTR_P +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret + ; Another entry. This will set the paper color at location pointer by DE +__SET_PAPER2: + and 7 ; # Remove + rlca + rlca + rlca ; a *= 8 + ld b, a ; Saves the color + ld a, (de) + and 0C7h ; Clears previous value + or b + ld (de), a + inc de ; Points to MASK_T or MASK_P accordingly + ld a, (de) + and 0C7h ; Resets bits 3,4,5 + ld (de), a + ret + ; Sets the PAPER color passed in A register in the ATTR_T variable +PAPER_TMP: + ld de, ATTR_T + jp __SET_PAPER + ENDP + pop namespace +#line 10 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/flash.asm" + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +FLASH: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x80 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 07Fh ; Clears previous value + or b + ld (hl), a + inc hl + res 7, (hl) ;Reset bit 7 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 7, (hl) ;Set bit 7 to enable transparency + ret + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld hl, ATTR_T + jr __SET_FLASH + ENDP + pop namespace +#line 11 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/bright.asm" + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +BRIGHT: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x40 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 0BFh ; Clears previous value + or b + ld (hl), a + inc hl + res 6, (hl) ;Reset bit 6 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 6, (hl) ;Set bit 6 to enable transparency + ret + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld hl, ATTR_T + jr __SET_BRIGHT + ENDP + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/over.asm" + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register + push namespace core +OVER: + PROC + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + ret + ; Sets OVER flag in P_FLAG temporarily +OVER_TMP: + ld c, a ; saves it for later + and 2 ; gets bit 1; clears carry + rra + ld hl, FLAGS2 + res 0, (hl) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + push namespace core +INVERSE: + PROC + and 1 ; # Convert to 0/1 + add a, a; # Shift left 3 bits for permanent + add a, a + add a, a + ld hl, P_FLAG + res 3, (hl) + or (hl) + ld (hl), a + ret + ; Sets INVERSE flag in P_FLAG temporarily +INVERSE_TMP: + and 1 + add a, a + add a, a; # Shift left 2 bits for temporary + ld hl, P_FLAG + res 2, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/bold.asm" + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + push namespace core +BOLD: + PROC + and 1 + rlca + rlca + rlca + ld hl, FLAGS2 + res 3, (HL) + or (hl) + ld (hl), a + ret + ; Sets BOLD flag in P_FLAG temporarily +BOLD_TMP: + and 1 + rlca + rlca + ld hl, FLAGS2 + res 2, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 15 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/italic.asm" + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + push namespace core +ITALIC: + PROC + and 1 + rrca + rrca + rrca + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + ret + ; Sets ITALIC flag in P_FLAG temporarily +ITALIC_TMP: + and 1 + rrca + rrca + rrca + rrca + ld hl, FLAGS2 + res 4, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 16 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + push namespace core +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + ;; Clears ATTR2 flags (OVER 2, etc) + xor a + ld (FLAGS2), a + ld hl, TV_FLAG + res 0, (hl) + LOCAL SET_SCR_ADDR + call __LOAD_S_POSN + jp __SET_SCR_PTR + ;; Receives HL = future value of S_POSN + ;; Stores it at (S_POSN) and refresh screen pointers (ATTR, SCR) +SET_SCR_ADDR: + ld (S_POSN), hl + ex de, hl + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + dec e + jp __SET_SCR_PTR +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A + LOCAL PO_GR_1 + LOCAL __PRCHAR + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + PRINT_JUMP_STATE EQU __PRINT_JUMP + 2 +__PRINT_JUMP: + exx ; Switch to alternative registers + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively +__PRINT_START: +__PRINT_CHR: + cp ' ' + jr c, __PRINT_SPECIAL ; Characters below ' ' are special ones + ex af, af' ; Saves a value (char to print) for later + ld hl, (S_POSN) + dec l + jr nz, 1f + ld l, SCR_COLS - 1 + dec h + jr nz, 2f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 94 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +2: + call SET_SCR_ADDR + jr 4f +1: + ld (S_POSN), hl +4: + ex af, af' + cp 80h ; Is it a "normal" (printable) char + jr c, __SRCADDR + cp 90h ; Is it an UDG? + jr nc, __PRINT_UDG + ; Print an 8 bit pattern (80h to 8Fh) + ld b, a + call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 + ld hl, MEM0 + jp __PRGRAPH + PO_GR_1 EQU 0B38h +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jr __PRGRAPH0 + __SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source +__SRCADDR: + ld bc, (CHARS) +__PRGRAPH0: + add a, a ; A = a * 2 (since a < 80h) ; Thanks to Metalbrain at http://foro.speccy.org + ld l, a + ld h, 0 ; HL = a * 2 (accumulator) + add hl, hl + add hl, hl ; HL = a * 8 + add hl, bc ; HL = CHARS address +__PRGRAPH: + ex de, hl ; HL = Write Address, DE = CHARS address + bit 2, (iy + $47) + call nz, __BOLD +#line 141 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + bit 4, (iy + $47) + call nz, __ITALIC +#line 146 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + ld hl, (DFCC) + push hl + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be source, and HL destiny +PRINT_MODE: ; Which operation is used to write on the screen + ; Set it with: + ; LD A, + ; LD (PRINT_MODE), A + ; + ; Available operations: + ; NORMAL : 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; Set to one of the values above +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + ld (hl), a + inc de + inc h ; Next line + djnz __PRCHAR + pop hl + inc hl + ld (DFCC), hl + ld hl, (DFCCL) ; current ATTR Pos + inc hl + ld (DFCCL), hl + dec hl + call __SET_ATTR + exx + ret + ; ------------- SPECIAL CHARS (< 32) ----------------- +__PRINT_SPECIAL: ; Jumps here if it is a special char + ld hl, __PRINT_TABLE + jp JUMP_HL_PLUS_2A +PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence + exx +__PRINT_0Dh: ; Called WHEN printing CHR$(13) + ld hl, (S_POSN) + dec l + jr nz, 1f + dec h + jr nz, 1f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 211 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +1: + ld l, 1 +__PRINT_EOL_END: + call SET_SCR_ADDR + exx + ret +__PRINT_COM: + exx + push hl + push de + push bc + call PRINT_COMMA + pop bc + pop de + pop hl + ret +__PRINT_TAB: + ld hl, __PRINT_TAB1 + jr __PRINT_SET_STATE +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + jr __PRINT_SET_STATE +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + exx + push hl + push bc + push de + call PRINT_TAB + pop de + pop bc + pop hl + ret +__PRINT_AT: + ld hl, __PRINT_AT1 + jr __PRINT_SET_STATE +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + ld hl, (S_POSN) + ld h, a + ld a, SCR_ROWS + sub h + ld (S_POSN + 1), a + ld hl, __PRINT_AT2 + jr __PRINT_SET_STATE +__PRINT_AT2: + call __LOAD_S_POSN + ld e, a + call __SAVE_S_POSN + jr __PRINT_RESTART +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jr nz, 3f + ld e, SCR_COLS - 2 + dec d + cp d + jr nz, 3f + ld d, SCR_ROWS - 1 +3: + call __SAVE_S_POSN + exx + ret +__PRINT_INK: + ld hl, __PRINT_INK2 + jr __PRINT_SET_STATE +__PRINT_INK2: + call INK_TMP + jr __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jr __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jr __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jr __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jr __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jr __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 355 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 365 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + LOCAL __BOLD +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +1: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz 1b + pop hl + ld de, MEM0 + ret +#line 386 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + LOCAL __ITALIC +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) + inc hl + srl (hl) + inc hl + srl (hl) + inc hl + inc hl + inc hl + sla (hl) + inc hl + sla (hl) + inc hl + sla (hl) + pop hl + ld de, MEM0 + ret +#line 414 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + LOCAL __SCROLL_SCR +#line 488 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + __SCROLL_SCR EQU 0DFEh ; Use ROM SCROLL +#line 490 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 491 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 +PRINT_TAB: + ; Tabulates the number of spaces in A register + ; If the current cursor position is already A, does nothing + PROC + LOCAL LOOP + call __LOAD_S_POSN ; e = current row + sub e + and 31 + ret z + ld b, a +LOOP: + ld a, ' ' + call __PRINTCHAR + djnz LOOP + ret + ENDP +PRINT_AT: ; Changes cursor to ROW, COL + ; COL in A register + ; ROW in stack + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl + call __IN_SCREEN + ret nc ; Return if out of screen + jp __SAVE_S_POSN + LOCAL __PRINT_COM + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_BOLD + LOCAL __PRINT_ITA + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + LOCAL __PRINT_ITA2 +#line 547 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 553 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +__PRINT_TABLE: ; Jump table for 0 .. 22 codes + DW __PRINT_NOP ; 0 + DW __PRINT_NOP ; 1 + DW __PRINT_NOP ; 2 + DW __PRINT_NOP ; 3 + DW __PRINT_NOP ; 4 + DW __PRINT_NOP ; 5 + DW __PRINT_COM ; 6 COMMA + DW __PRINT_NOP ; 7 + DW __PRINT_DEL ; 8 DEL + DW __PRINT_NOP ; 9 + DW __PRINT_NOP ; 10 + DW __PRINT_NOP ; 11 + DW __PRINT_NOP ; 12 + DW __PRINT_0Dh ; 13 + DW __PRINT_BOLD ; 14 + DW __PRINT_ITA ; 15 + DW __PRINT_INK ; 16 + DW __PRINT_PAP ; 17 + DW __PRINT_FLA ; 18 + DW __PRINT_BRI ; 19 + DW __PRINT_INV ; 20 + DW __PRINT_OVR ; 21 + DW __PRINT_AT ; 22 AT + DW __PRINT_TAB ; 23 TAB + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" + push namespace core +COPY_ATTR: + ; Just copies current permanent attribs into temporal attribs + ; and sets print mode + PROC + LOCAL INVERSE1 + LOCAL __REFRESH_TMP + INVERSE1 EQU 02Fh + ld hl, (ATTR_P) + ld (ATTR_T), hl + ld hl, FLAGS2 + call __REFRESH_TMP + ld hl, P_FLAG + call __REFRESH_TMP +__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) + LOCAL TABLE + LOCAL CONT2 + rra ; Over bit to carry + ld a, (FLAGS2) + rla ; Over bit in bit 1, Over2 bit in bit 2 + and 3 ; Only bit 0 and 1 (OVER flag) + ld c, a + ld b, 0 + ld hl, TABLE + add hl, bc + ld a, (hl) + ld (PRINT_MODE), a + ld hl, (P_FLAG) + xor a ; NOP -> INVERSE0 + bit 2, l + jr z, CONT2 + ld a, INVERSE1 ; CPL -> INVERSE1 +CONT2: + ld (INVERSE_MODE), a + ret +TABLE: + nop ; NORMAL MODE + xor (hl) ; OVER 1 MODE + and (hl) ; OVER 2 MODE + or (hl) ; OVER 3 MODE +#line 67 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +__REFRESH_TMP: + ld a, (hl) + and 0b10101010 + ld c, a + rra + or c + ld (hl), a + ret + ENDP + pop namespace +#line 24 "arch/zx48k/print_at2.bas" + END diff --git a/tests/functional/arch/zx48k/print_at2.bas b/tests/functional/arch/zx48k/print_at2.bas new file mode 100644 index 000000000..f2b3d0c14 --- /dev/null +++ b/tests/functional/arch/zx48k/print_at2.bas @@ -0,0 +1,3 @@ +DIM row, col as UByte +PRINT AT row + 2, col + 1; + diff --git a/tests/functional/arch/zx48k/print_tab2.asm b/tests/functional/arch/zx48k/print_tab2.asm new file mode 100644 index 000000000..f219ee8fb --- /dev/null +++ b/tests/functional/arch/zx48k/print_tab2.asm @@ -0,0 +1,957 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_i: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call .core.COPY_ATTR + ld a, (_i) + add a, 2 + call .core.PRINT_TAB + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +; vim:ts=4:sw=4:et: + ; PRINT command routine + ; Does not print attribute. Use PRINT_STR or PRINT_NUM for that +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; Flags for controlling output to screen + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 6 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/in_screen.asm" + push namespace core +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits + PROC + LOCAL __IN_SCREEN_ERR + ld hl, SCR_SIZE + ld a, e + cp l + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld a, d + cp h + ret c ; Return if carry (OK) +__IN_SCREEN_ERR: +__OUT_OF_SCREEN_ERR: + ; Jumps here if out of screen + ld a, ERROR_OutOfScreen + jp __STOP ; Saves error code and exits + ENDP + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + push namespace core +__ATTR_ADDR: + ; calc start address in DE (as (32 * d) + e) + ; Contributed by Santiago Romero at http://www.speccy.org + ld h, 0 ; 7 T-States + ld a, d ; 4 T-States + ld d, h + add a, a ; a * 2 ; 4 T-States + add a, a ; a * 4 ; 4 T-States + ld l, a ; HL = A * 4 ; 4 T-States + add hl, hl ; HL = A * 8 ; 15 T-States + add hl, hl ; HL = A * 16 ; 15 T-States + add hl, hl ; HL = A * 32 ; 15 T-States + add hl, de + ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address + add hl, de + ; Return current screen address in HL + ret + ; Sets the attribute at a given screen coordinate (D, E). + ; The attribute is taken from the ATTR_T memory variable + ; Used by PRINT routines +SET_ATTR: + ; Checks for valid coords + call __IN_SCREEN + ret nc + call __ATTR_ADDR +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + ; HL contains the address of the ATTR cell to set + PROC +__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + ld a, d + and (hl) + ld c, a ; C = current screen color, masked + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + ret + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" + ; Printing positioning library. + push namespace core + ; Loads into DE current ROW, COL print position from S_POSN mem var. +__LOAD_S_POSN: + PROC + ld de, (S_POSN) + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + ret + ENDP + ; Saves ROW, COL from DE into S_POSN mem var. +__SAVE_S_POSN: + PROC + ld hl, SCR_SIZE + or a + sbc hl, de + ld (S_POSN), hl ; saves it again +__SET_SCR_PTR: ;; Fast + push de + call __ATTR_ADDR + ld (DFCCL), hl + pop de + ld a, d + ld c, a ; Saves it for later + and 0F8h ; Masks 3 lower bit ; zy + ld d, a + ld a, c ; Recovers it + and 07h ; MOD 7 ; y1 + rrca + rrca + rrca + or e + ld e, a + ld hl, (SCREEN_ADDR) + add hl, de ; HL = Screen address + DE + ld (DFCC), hl + ret + ENDP + pop namespace +#line 6 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/table_jump.asm" + push namespace core +JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A + add a, a +JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE + ld e, a + ld d, 0 +JUMP_HL_PLUS_DE: ; Does JP (HL + DE) + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl +CALL_HL: + jp (hl) + pop namespace +#line 8 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/ink.asm" + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 + ld de, ATTR_P +__SET_INK: + cp 8 + jr nz, __SET_INK2 + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + or 7 ; Set bits 0,1,2 to enable transparency + ld (de), a + ret +__SET_INK2: + ; Another entry. This will set the ink color at location pointer by DE + and 7 ; # Gets color mod 8 + ld b, a ; Saves the color + ld a, (de) + and 0F8h ; Clears previous value + or b + ld (de), a + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + and 0F8h ; Reset bits 0,1,2 sign to disable transparency + ld (de), a ; Store new attr + ret + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + ld de, ATTR_P +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret + ; Another entry. This will set the paper color at location pointer by DE +__SET_PAPER2: + and 7 ; # Remove + rlca + rlca + rlca ; a *= 8 + ld b, a ; Saves the color + ld a, (de) + and 0C7h ; Clears previous value + or b + ld (de), a + inc de ; Points to MASK_T or MASK_P accordingly + ld a, (de) + and 0C7h ; Resets bits 3,4,5 + ld (de), a + ret + ; Sets the PAPER color passed in A register in the ATTR_T variable +PAPER_TMP: + ld de, ATTR_T + jp __SET_PAPER + ENDP + pop namespace +#line 10 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/flash.asm" + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +FLASH: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x80 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 07Fh ; Clears previous value + or b + ld (hl), a + inc hl + res 7, (hl) ;Reset bit 7 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 7, (hl) ;Set bit 7 to enable transparency + ret + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld hl, ATTR_T + jr __SET_FLASH + ENDP + pop namespace +#line 11 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/bright.asm" + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +BRIGHT: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x40 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 0BFh ; Clears previous value + or b + ld (hl), a + inc hl + res 6, (hl) ;Reset bit 6 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 6, (hl) ;Set bit 6 to enable transparency + ret + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld hl, ATTR_T + jr __SET_BRIGHT + ENDP + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/over.asm" + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register + push namespace core +OVER: + PROC + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + ret + ; Sets OVER flag in P_FLAG temporarily +OVER_TMP: + ld c, a ; saves it for later + and 2 ; gets bit 1; clears carry + rra + ld hl, FLAGS2 + res 0, (hl) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + push namespace core +INVERSE: + PROC + and 1 ; # Convert to 0/1 + add a, a; # Shift left 3 bits for permanent + add a, a + add a, a + ld hl, P_FLAG + res 3, (hl) + or (hl) + ld (hl), a + ret + ; Sets INVERSE flag in P_FLAG temporarily +INVERSE_TMP: + and 1 + add a, a + add a, a; # Shift left 2 bits for temporary + ld hl, P_FLAG + res 2, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/bold.asm" + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + push namespace core +BOLD: + PROC + and 1 + rlca + rlca + rlca + ld hl, FLAGS2 + res 3, (HL) + or (hl) + ld (hl), a + ret + ; Sets BOLD flag in P_FLAG temporarily +BOLD_TMP: + and 1 + rlca + rlca + ld hl, FLAGS2 + res 2, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 15 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/italic.asm" + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + push namespace core +ITALIC: + PROC + and 1 + rrca + rrca + rrca + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + ret + ; Sets ITALIC flag in P_FLAG temporarily +ITALIC_TMP: + and 1 + rrca + rrca + rrca + rrca + ld hl, FLAGS2 + res 4, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 16 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + push namespace core +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + ;; Clears ATTR2 flags (OVER 2, etc) + xor a + ld (FLAGS2), a + ld hl, TV_FLAG + res 0, (hl) + LOCAL SET_SCR_ADDR + call __LOAD_S_POSN + jp __SET_SCR_PTR + ;; Receives HL = future value of S_POSN + ;; Stores it at (S_POSN) and refresh screen pointers (ATTR, SCR) +SET_SCR_ADDR: + ld (S_POSN), hl + ex de, hl + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + dec e + jp __SET_SCR_PTR +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A + LOCAL PO_GR_1 + LOCAL __PRCHAR + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + PRINT_JUMP_STATE EQU __PRINT_JUMP + 2 +__PRINT_JUMP: + exx ; Switch to alternative registers + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively +__PRINT_START: +__PRINT_CHR: + cp ' ' + jr c, __PRINT_SPECIAL ; Characters below ' ' are special ones + ex af, af' ; Saves a value (char to print) for later + ld hl, (S_POSN) + dec l + jr nz, 1f + ld l, SCR_COLS - 1 + dec h + jr nz, 2f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 94 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +2: + call SET_SCR_ADDR + jr 4f +1: + ld (S_POSN), hl +4: + ex af, af' + cp 80h ; Is it a "normal" (printable) char + jr c, __SRCADDR + cp 90h ; Is it an UDG? + jr nc, __PRINT_UDG + ; Print an 8 bit pattern (80h to 8Fh) + ld b, a + call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 + ld hl, MEM0 + jp __PRGRAPH + PO_GR_1 EQU 0B38h +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jr __PRGRAPH0 + __SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source +__SRCADDR: + ld bc, (CHARS) +__PRGRAPH0: + add a, a ; A = a * 2 (since a < 80h) ; Thanks to Metalbrain at http://foro.speccy.org + ld l, a + ld h, 0 ; HL = a * 2 (accumulator) + add hl, hl + add hl, hl ; HL = a * 8 + add hl, bc ; HL = CHARS address +__PRGRAPH: + ex de, hl ; HL = Write Address, DE = CHARS address + bit 2, (iy + $47) + call nz, __BOLD +#line 141 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + bit 4, (iy + $47) + call nz, __ITALIC +#line 146 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + ld hl, (DFCC) + push hl + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be source, and HL destiny +PRINT_MODE: ; Which operation is used to write on the screen + ; Set it with: + ; LD A, + ; LD (PRINT_MODE), A + ; + ; Available operations: + ; NORMAL : 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; Set to one of the values above +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + ld (hl), a + inc de + inc h ; Next line + djnz __PRCHAR + pop hl + inc hl + ld (DFCC), hl + ld hl, (DFCCL) ; current ATTR Pos + inc hl + ld (DFCCL), hl + dec hl + call __SET_ATTR + exx + ret + ; ------------- SPECIAL CHARS (< 32) ----------------- +__PRINT_SPECIAL: ; Jumps here if it is a special char + ld hl, __PRINT_TABLE + jp JUMP_HL_PLUS_2A +PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence + exx +__PRINT_0Dh: ; Called WHEN printing CHR$(13) + ld hl, (S_POSN) + dec l + jr nz, 1f + dec h + jr nz, 1f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 211 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +1: + ld l, 1 +__PRINT_EOL_END: + call SET_SCR_ADDR + exx + ret +__PRINT_COM: + exx + push hl + push de + push bc + call PRINT_COMMA + pop bc + pop de + pop hl + ret +__PRINT_TAB: + ld hl, __PRINT_TAB1 + jr __PRINT_SET_STATE +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + jr __PRINT_SET_STATE +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + exx + push hl + push bc + push de + call PRINT_TAB + pop de + pop bc + pop hl + ret +__PRINT_AT: + ld hl, __PRINT_AT1 + jr __PRINT_SET_STATE +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + ld hl, (S_POSN) + ld h, a + ld a, SCR_ROWS + sub h + ld (S_POSN + 1), a + ld hl, __PRINT_AT2 + jr __PRINT_SET_STATE +__PRINT_AT2: + call __LOAD_S_POSN + ld e, a + call __SAVE_S_POSN + jr __PRINT_RESTART +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jr nz, 3f + ld e, SCR_COLS - 2 + dec d + cp d + jr nz, 3f + ld d, SCR_ROWS - 1 +3: + call __SAVE_S_POSN + exx + ret +__PRINT_INK: + ld hl, __PRINT_INK2 + jr __PRINT_SET_STATE +__PRINT_INK2: + call INK_TMP + jr __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jr __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jr __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jr __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jr __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jr __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 355 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 365 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + LOCAL __BOLD +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +1: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz 1b + pop hl + ld de, MEM0 + ret +#line 386 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + LOCAL __ITALIC +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) + inc hl + srl (hl) + inc hl + srl (hl) + inc hl + inc hl + inc hl + sla (hl) + inc hl + sla (hl) + inc hl + sla (hl) + pop hl + ld de, MEM0 + ret +#line 414 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + LOCAL __SCROLL_SCR +#line 488 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + __SCROLL_SCR EQU 0DFEh ; Use ROM SCROLL +#line 490 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +#line 491 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 +PRINT_TAB: + ; Tabulates the number of spaces in A register + ; If the current cursor position is already A, does nothing + PROC + LOCAL LOOP + call __LOAD_S_POSN ; e = current row + sub e + and 31 + ret z + ld b, a +LOOP: + ld a, ' ' + call __PRINTCHAR + djnz LOOP + ret + ENDP +PRINT_AT: ; Changes cursor to ROW, COL + ; COL in A register + ; ROW in stack + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl + call __IN_SCREEN + ret nc ; Return if out of screen + jp __SAVE_S_POSN + LOCAL __PRINT_COM + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_BOLD + LOCAL __PRINT_ITA + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + LOCAL __PRINT_ITA2 +#line 547 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 553 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" +__PRINT_TABLE: ; Jump table for 0 .. 22 codes + DW __PRINT_NOP ; 0 + DW __PRINT_NOP ; 1 + DW __PRINT_NOP ; 2 + DW __PRINT_NOP ; 3 + DW __PRINT_NOP ; 4 + DW __PRINT_NOP ; 5 + DW __PRINT_COM ; 6 COMMA + DW __PRINT_NOP ; 7 + DW __PRINT_DEL ; 8 DEL + DW __PRINT_NOP ; 9 + DW __PRINT_NOP ; 10 + DW __PRINT_NOP ; 11 + DW __PRINT_NOP ; 12 + DW __PRINT_0Dh ; 13 + DW __PRINT_BOLD ; 14 + DW __PRINT_ITA ; 15 + DW __PRINT_INK ; 16 + DW __PRINT_PAP ; 17 + DW __PRINT_FLA ; 18 + DW __PRINT_BRI ; 19 + DW __PRINT_INV ; 20 + DW __PRINT_OVR ; 21 + DW __PRINT_AT ; 22 AT + DW __PRINT_TAB ; 23 TAB + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" + push namespace core +COPY_ATTR: + ; Just copies current permanent attribs into temporal attribs + ; and sets print mode + PROC + LOCAL INVERSE1 + LOCAL __REFRESH_TMP + INVERSE1 EQU 02Fh + ld hl, (ATTR_P) + ld (ATTR_T), hl + ld hl, FLAGS2 + call __REFRESH_TMP + ld hl, P_FLAG + call __REFRESH_TMP +__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) + LOCAL TABLE + LOCAL CONT2 + rra ; Over bit to carry + ld a, (FLAGS2) + rla ; Over bit in bit 1, Over2 bit in bit 2 + and 3 ; Only bit 0 and 1 (OVER flag) + ld c, a + ld b, 0 + ld hl, TABLE + add hl, bc + ld a, (hl) + ld (PRINT_MODE), a + ld hl, (P_FLAG) + xor a ; NOP -> INVERSE0 + bit 2, l + jr z, CONT2 + ld a, INVERSE1 ; CPL -> INVERSE1 +CONT2: + ld (INVERSE_MODE), a + ret +TABLE: + nop ; NORMAL MODE + xor (hl) ; OVER 1 MODE + and (hl) ; OVER 2 MODE + or (hl) ; OVER 3 MODE +#line 67 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +__REFRESH_TMP: + ld a, (hl) + and 0b10101010 + ld c, a + rra + or c + ld (hl), a + ret + ENDP + pop namespace +#line 21 "arch/zx48k/print_tab2.bas" + END diff --git a/tests/functional/arch/zx48k/print_tab2.bas b/tests/functional/arch/zx48k/print_tab2.bas new file mode 100644 index 000000000..f49690308 --- /dev/null +++ b/tests/functional/arch/zx48k/print_tab2.bas @@ -0,0 +1,4 @@ +DIM i As UByte + +PRINT TAB i + 2; + diff --git a/tests/functional/arch/zx48k/rel_include.asm b/tests/functional/arch/zx48k/rel_include.asm new file mode 100644 index 000000000..cf14001cb --- /dev/null +++ b/tests/functional/arch/zx48k/rel_include.asm @@ -0,0 +1,42 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, 1 + ld (_a), a + ld a, 1 + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx48k/rel_include.bas b/tests/functional/arch/zx48k/rel_include.bas new file mode 100644 index 000000000..d069e5074 --- /dev/null +++ b/tests/functional/arch/zx48k/rel_include.bas @@ -0,0 +1,3 @@ +#include once "../zx48k/rel_include.bas" + +LET a = 1 diff --git a/tests/functional/arch/zx48k/stdlib_hex.asm b/tests/functional/arch/zx48k/stdlib_hex.asm index 0e7be338a..ea52826f0 100644 --- a/tests/functional/arch/zx48k/stdlib_hex.asm +++ b/tests/functional/arch/zx48k/stdlib_hex.asm @@ -91,9 +91,6 @@ END_CHAR: ENDP pop namespace #line 91 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" -#line 30 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" - ld hl, 0 -#line 33 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" _hex__leave: ret _hex16: @@ -175,7 +172,7 @@ _hex8__leave: exx ret ;; --- end of user code --- -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -269,8 +266,8 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/heapinit.asm" +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -377,7 +374,7 @@ __MEM_INIT2: ret ENDP pop namespace -#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -408,9 +405,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ret z ; NULL -#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -475,8 +472,8 @@ __MEM_SUBTRACT: ret ENDP pop namespace -#line 116 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/free.asm" +#line 174 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -634,7 +631,7 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ret ENDP pop namespace -#line 117 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" +#line 175 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/pstorestr2.asm" ; vim:ts=4:et:sw=4 ; @@ -683,7 +680,7 @@ __PSTORE_STR2: add hl, bc jp __STORE_STR2 pop namespace -#line 118 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" +#line 176 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/strslice.asm" ; String slicing library ; HL = Str pointer @@ -785,5 +782,5 @@ __FREE_ON_EXIT: ret ENDP pop namespace -#line 119 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" +#line 177 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" END diff --git a/tests/functional/arch/zx48k/stdlib_scroll.asm b/tests/functional/arch/zx48k/stdlib_scroll.asm index f7de2f0c5..7ee1d7a83 100644 --- a/tests/functional/arch/zx48k/stdlib_scroll.asm +++ b/tests/functional/arch/zx48k/stdlib_scroll.asm @@ -33,7 +33,581 @@ ei ret _ScrollRight: -#line 25 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +#line 26 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1, LOOP2, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + ld a, h + sub b + ret c + inc a + ld e, a + ld a,c + and 7 + inc a + ld b,a + xor a +MASK1: + rra + scf + djnz MASK1 + ld (AND1+1),a + ld (AND5+1),a + cpl + ld (AND2+1),a + ld (AND7+1),a + ld a,d + and 7 + inc a + ld b,a + xor a +MASK2: + scf + rra + djnz MASK2 + ld (AND4+1),a + ld (AND8+1),a + cpl + ld (AND3+1),a + ld (AND6+1),a + ld a,c + and %11111000 + rrca + rrca + rrca + ld b,a + ld a,d + and %11111000 + rrca + rrca + rrca + sub b + ex af,af' + ld b, h + ld a, 191 + call 22ACh + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ex af,af' + jr z,NEQ1 + dec a + ld d,a +LOOP1: + push hl + ld a,(hl) +AND1: + and %11100000 + ld c,a + ld a,(hl) +AND2: + and %00011111 + rra + rl b + or c + ld (hl),a + inc hl + ld a,d + and a + jr z,COLUMNN + rr b + ld b,a +LOOP2: + rr (hl) + inc hl + djnz LOOP2 + rl b +COLUMNN: + ld a,(hl) +AND3: + and %00000011 + ld c,a + ld a,(hl) + rr b + rra +AND4: + and %11111100 + or c + ld (hl),a + pop hl + dec e + ret z + call SP.PixelDown + jp LOOP1 +NEQ1: + ld b,(hl) + ld a,b +AND5: + and %11100000 + ld c,a + ld a,b +AND6: + and %00000011 + or c + ld c,a + ld a,b +AND7: + and %00011111 + rra +AND8: + and %11111100 + or c + ld (hl),a + dec e + ret z + call SP.PixelDown + jp NEQ1 + ENDP + pop namespace +#line 187 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollRight__leave: + ret +_ScrollLeft: +#line 195 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1, LOOP2, MASK1, MASK2, COLUMN1, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + ld a, h + sub b + ret c + inc a + ld e, a + ld a,c + and 7 + inc a + ld b,a + xor a +MASK1: + rra + scf + djnz MASK1 + ld (AND3+1),a + ld (AND6+1),a + cpl + ld (AND4+1),a + ld (AND8+1),a + ld a,d + and 7 + inc a + ld b,a + xor a +MASK2: + scf + rra + djnz MASK2 + ld (AND2+1),a + ld (AND7+1),a + cpl + ld (AND1+1),a + ld (AND5+1),a + ld a,c + and %11111000 + rrca + rrca + rrca + ld b,a + ld a,d + and %11111000 + rrca + rrca + rrca + sub b + ex af,af' + ld c, d + ld b, h + ld a, 191 + call 22ACh + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ex af,af' + jr z,NEQ1 + dec a + ld d,a +LOOP1: + push hl + ld a,(hl) +AND1: + and %00000011 + ld c,a + ld a,(hl) +AND2: + and %11111100 + rla + rl b + or c + ld (hl),a + dec hl + ld a,d + and a + jr z,COLUMN1 + rr b + ld b,a +LOOP2: + rl (hl) + dec hl + djnz LOOP2 + rl b +COLUMN1: + ld a,(hl) +AND3: + and %11100000 + ld c,a + ld a,(hl) + rr b + rla +AND4: + and %00011111 + or c + ld (hl),a + pop hl + dec e + ret z + call SP.PixelDown + jp LOOP1 +NEQ1: + ld b,(hl) + ld a,b +AND5: + and %00000011 + ld c,a + ld a,b +AND6: + and %11100000 + or c + ld c,a + ld a,b +AND7: + and %11111100 + rla +AND8: + and %00011111 + or c + ld (hl),a + dec e + ret z + call SP.PixelDown + jp NEQ1 + ENDP + pop namespace +#line 357 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollLeft__leave: + ret +_ScrollUp: +#line 366 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + LOCAL EMPTYLINE + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + ld a, h + sub b + ret c + push ix + inc a + ld ixL,a + ld a,c + and 7 + inc a + ld b,a + xor a +MASK1: + rra + scf + djnz MASK1 + ld (AND1+1),a + ld (AND5+1),a + cpl + ld (AND2+1),a + ld (AND7+1),a + ld a,d + and 7 + inc a + ld b,a + xor a +MASK2: + scf + rra + djnz MASK2 + ld (AND4+1),a + ld (AND8+1),a + cpl + ld (AND3+1),a + ld (AND6+1),a + ld a,c + and %11111000 + rrca + rrca + rrca + ld b,a + ld a,d + and %11111000 + rrca + rrca + rrca + sub b + ex af,af' + ld b, h + ld a, 191 + call 22ACh + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ex af,af' + jr z,NEQ1 + dec a + ld ixH,a + ld b,0 +LOOP1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE + call nz,SP.PixelDown + inc ixL + push hl + ld a,(hl) +AND2: + and %00011111 + ld c,a + ld a,(de) +AND1: + and %11100000 + or c + ld (de),a + inc hl + inc de + ld a,ixH + and a + jr z,COLUMNN + ld c,a + ldir +COLUMNN: + ld a,(hl) +AND4: + and %11111100 + ld c,a + ld a,(de) +AND3: + and %00000011 + or c + ld (de),a + pop hl + dec ixL + jp nz,LOOP1 + pop ix + ret +NEQ1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE + call nz,SP.PixelDown + inc ixL + ld a,(hl) +AND7: + and %00011111 +AND8: + and %11111100 + ld c,a + ld a,(de) +AND5: + and %11100000 + ld b,a + ld a,(de) +AND6: + and %00000011 + or b + or c + ld (de),a + dec ixL + jp nz,NEQ1 + pop ix + ret + defs 32,0 +EMPTYLINE: + ld hl,EMPTYLINE-32 + ENDP + pop namespace +#line 537 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollUp__leave: + ret +_ScrollDown: +#line 546 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + LOCAL EMPTYLINE + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + ld a, h + sub b + ret c + push ix + inc a + ld ixL,a + ld h,b + ld a,c + and 7 + inc a + ld b,a + xor a +MASK1: + rra + scf + djnz MASK1 + ld (AND1+1),a + ld (AND5+1),a + cpl + ld (AND2+1),a + ld (AND7+1),a + ld a,d + and 7 + inc a + ld b,a + xor a +MASK2: + scf + rra + djnz MASK2 + ld (AND4+1),a + ld (AND8+1),a + cpl + ld (AND3+1),a + ld (AND6+1),a + ld a,c + and %11111000 + rrca + rrca + rrca + ld b,a + ld a,d + and %11111000 + rrca + rrca + rrca + sub b + ex af,af' + ld b, h + ld a, 191 + call 22ACh + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ex af,af' + jr z,NEQ1 + dec a + ld ixH,a + ld b,0 +LOOP1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE + call nz,SP.PixelUp + inc ixL + push hl + ld a,(hl) +AND2: + and %00011111 + ld c,a + ld a,(de) +AND1: + and %11100000 + or c + ld (de),a + inc hl + inc de + ld a,ixH + and a + jr z,COLUMNN + ld c,a + ldir +COLUMNN: + ld a,(hl) +AND4: + and %11111100 + ld c,a + ld a,(de) +AND3: + and %00000011 + or c + ld (de),a + pop hl + dec ixL + jp nz,LOOP1 + pop ix + ret +NEQ1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE + call nz,SP.PixelUp + inc ixL + ld a,(hl) +AND7: + and %00011111 +AND8: + and %11111100 + ld c,a + ld a,(de) +AND5: + and %11100000 + ld b,a + ld a,(de) +AND6: + and %00000011 + or b + or c + ld (de),a + dec ixL + jp nz,NEQ1 + pop ix + ret + defs 32,0 +EMPTYLINE: + ld hl,EMPTYLINE-32 + ENDP + pop namespace +#line 718 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollDown__leave: + ret +_ScrollRightAligned: +#line 729 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" push namespace core PROC LOCAL LOOP1 @@ -79,11 +653,11 @@ LOOP2: jp LOOP1 ENDP pop namespace -#line 83 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" -_ScrollRight__leave: +#line 787 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollRightAligned__leave: ret -_ScrollLeft: -#line 92 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollLeftAligned: +#line 798 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" push namespace core PROC LOCAL LOOP1 @@ -130,11 +704,11 @@ LOOP2: jp LOOP1 ENDP pop namespace -#line 151 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" -_ScrollLeft__leave: +#line 857 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollLeftAligned__leave: ret -_ScrollUp: -#line 160 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollUpAligned: +#line 868 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" push namespace core PROC LOCAL LOOP1 @@ -197,11 +771,11 @@ LOOP_START: ldir ENDP pop namespace -#line 237 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" -_ScrollUp__leave: +#line 945 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollUpAligned__leave: ret -_ScrollDown: -#line 246 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollDownAligned: +#line 956 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" push namespace core PROC LOCAL LOOP1 @@ -263,8 +837,8 @@ LOOP_START: ldir ENDP pop namespace -#line 323 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" -_ScrollDown__leave: +#line 1033 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollDownAligned__leave: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/SP/PixelDown.asm" @@ -344,7 +918,7 @@ SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS pop namespace #line 58 "/zxbasic/src/lib/arch/zx48k/runtime/SP/PixelDown.asm" -#line 328 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +#line 1038 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/SP/PixelUp.asm" ; ; PixelUp @@ -391,36 +965,5 @@ leave: ret ENDP pop namespace -#line 329 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cls.asm" - ;; Clears the user screen (24 rows) - push namespace core -CLS: - PROC - ld hl, 0 - ld (COORDS), hl - ld hl, SCR_SIZE - ld (S_POSN), hl - ld hl, (SCREEN_ADDR) - ld (DFCC), hl - ld (hl), 0 - ld d, h - ld e, l - inc de - ld bc, 6143 - ldir - ; Now clear attributes - ld hl, (SCREEN_ATTR_ADDR) - ld (DFCCL), hl - ld d, h - ld e, l - inc de - ld a, (ATTR_P) - ld (hl), a - ld bc, 767 - ldir - ret - ENDP - pop namespace -#line 330 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +#line 1039 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" END diff --git a/tests/functional/arch/zx48k/stdlib_winscroll.asm b/tests/functional/arch/zx48k/stdlib_winscroll.asm index 1929d3fa2..b19c0a96f 100644 --- a/tests/functional/arch/zx48k/stdlib_winscroll.asm +++ b/tests/functional/arch/zx48k/stdlib_winscroll.asm @@ -33,68 +33,31 @@ ei ret _WinScrollRight: -#line 24 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" +#line 27 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" push namespace core - LOCAL BucleChars - LOCAL BucleScans - LOCAL BucleAttrs PROC + LOCAL BucleRows, BucleScans, AfterLDDR, AfterLDDR2, AfterLDDR3 ld b, a pop hl pop de - ld c, d + ld a, d pop de + add a, d + dec a + ld c, a ex (sp), hl ld e, h ld a, e or a ret z - or d + ld a, d + or a ret z - push bc - ld a,b - and 18h - ld h,a - ld a,b - and 07h - add a,a - add a,a - add a,a - add a,a - add a,a - add a,c - add a,d - dec a - ld l,a - ld bc, (SCREEN_ADDR) - add hl, bc - ld b,e -BucleChars: - push bc - ld b,8 -BucleScans: - push bc - push de - push hl - ld c,d - dec c - ld b,0 - ld d,h - ld e,l - dec l - lddr - xor a - ld (de),a - pop hl - pop de - inc h - pop bc - djnz BucleScans - dec h - call SP.PixelDown - pop bc - djnz BucleChars - pop bc + sub 2 + inc a + ex af,af' + push ix + ld ixL,e ld l,b ld h,0 add hl,hl @@ -104,43 +67,79 @@ BucleScans: add hl,hl ld a,l add a,c - add a,d - dec a ld l,a - ld a,h - ld h,a - ld bc, (SCREEN_ATTR_ADDR) - add hl, bc - ld b,e -BucleAttrs: + ld de,(SCREEN_ATTR_ADDR) + add hl,de +BucleRows: push bc - push de push hl - ld c,d - dec c + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de ld b,0 + ex af,af' + ld ixH,7 +BucleScans: + push hl + jr c,AfterLDDR ld d,h ld e,l - dec l + dec hl + ld c,a + lddr + ex de,hl +AfterLDDR: + ld (hl),b + pop hl + inc h + dec ixH + jp nz,BucleScans + jr c,AfterLDDR2 + ld d,h + ld e,l + dec hl + ld c,a + lddr + ex de,hl +AfterLDDR2: + ld (hl),b + pop hl + jr c,AfterLDDR3 + ld d,h + ld e,l + dec hl + ld c,a + push de lddr pop hl +AfterLDDR3: + ex af,af' ld de,32 add hl,de - pop de pop bc - djnz BucleAttrs + inc b + dec ixL + jp nz,BucleRows + pop ix ENDP pop namespace -#line 130 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" +#line 139 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" _WinScrollRight__leave: ret _WinScrollLeft: -#line 138 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" +#line 149 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" push namespace core PROC - LOCAL BucleChars - LOCAL BucleScans - LOCAL BucleAttrs + LOCAL BucleRows, BucleScans, AfterLDIR, AfterLDIR2, AfterLDIR3 ld b, a pop hl pop de @@ -151,51 +150,14 @@ _WinScrollLeft: ld a, e or a ret z - or d + ld a, d + or a ret z - push bc - ld a,b - and 18h - ld h,a - ld a,b - and 07h - add a,a - add a,a - add a,a - add a,a - add a,a - add a,c - ld l,a - ld bc, (SCREEN_ADDR) - add hl, bc - ld b,e -BucleChars: - push bc - ld b,8 -BucleScans: - push bc - push de - push hl - ld c,d - dec c - ld b,0 - ld d,h - ld e,l - inc e - ex de,hl - ldir - xor a - ld (de),a - pop hl - pop de - inc h - pop bc - djnz BucleScans - dec h - call SP.PixelDown - pop bc - djnz BucleChars - pop bc + sub 2 + inc a + ex af,af' + push ix + ld ixL,e ld l,b ld h,0 add hl,hl @@ -206,40 +168,79 @@ BucleScans: ld a,l add a,c ld l,a - ld a,h - ld h,a - ld bc, (SCREEN_ATTR_ADDR) - add hl, bc - ld b,e -BucleAttrs: + ld de,(SCREEN_ATTR_ADDR) + add hl,de +BucleRows: push bc - push de push hl - ld c,d - dec c + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de ld b,0 + ex af,af' + ld ixH,7 +BucleScans: + push hl + jr c,AfterLDIR + ld d,h + ld e,l + inc hl + ld c,a + ldir + ex de,hl +AfterLDIR: + ld (hl),b + pop hl + inc h + dec ixH + jp nz,BucleScans + jr c,AfterLDIR2 ld d,h ld e,l - inc e + inc hl + ld c,a + ldir ex de,hl +AfterLDIR2: + ld (hl),b + pop hl + jr c,AfterLDIR3 + ld d,h + ld e,l + inc hl + ld c,a + push de ldir pop hl +AfterLDIR3: + ex af,af' ld de,32 add hl,de - pop de pop bc - djnz BucleAttrs + inc b + dec ixL + jp nz,BucleRows + pop ix ENDP pop namespace -#line 241 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" +#line 258 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" _WinScrollLeft__leave: ret _WinScrollUp: -#line 249 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" +#line 268 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" push namespace core PROC - LOCAL BucleScans, BucleAttrs, ScrollAttrs - LOCAL CleanLast, CleanLastLoop, EndCleanScan + LOCAL BucleRows, BucleScans, AttrAddress + LOCAL CleanBottomRow, CleanBottomScans, AfterLDIR ld b, a pop hl pop de @@ -250,81 +251,12 @@ _WinScrollUp: ld a, e or a ret z - or d + ld a, d + or a ret z - push bc - push de - ld a,b - and 18h - ld h,a - ld a,b - and 07h - add a,a - add a,a - add a,a - add a,a - add a,a - add a,c - ld l,a - ld bc, (SCREEN_ADDR) - add hl, bc - ld a,e - ld c, d - ld d, h - ld e, l - dec a - jr z, CleanLast - add a,a - add a,a - add a,a - ld b, a - inc h - inc h - inc h - inc h - inc h - inc h - inc h - call SP.PixelDown -BucleScans: - push bc - push de - push hl - ld b, 0 - ldir - pop hl - pop de - pop bc - call SP.PixelDown - ex de, hl - call SP.PixelDown - ex de, hl - djnz BucleScans -CleanLast: - ex de,hl - pop de - ld b, 8 - ld c, d - push de -CleanLastLoop: - push bc - push hl - ld (hl), 0 - dec c - jr z, EndCleanScan - ld d, h - ld e, l - inc de - ld b, 0 - ldir -EndCleanScan: - pop hl - pop bc - inc h - djnz CleanLastLoop -ScrollAttrs: - pop de - pop bc + ex af,af' + push ix + ld ixL,e ld l,b ld h,0 add hl,hl @@ -335,40 +267,101 @@ ScrollAttrs: ld a,l add a,c ld l,a - ld a,h + ld de,(SCREEN_ATTR_ADDR) + add hl,de + ld (AttrAddress+1),hl + ld a,b + and %00011000 ld h,a - ld bc, (SCREEN_ATTR_ADDR) - add hl, bc - ld b,e - dec b - ret z -BucleAttrs: + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + push hl +BucleRows: + dec ixL + jr z,CleanBottomRow + inc b + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + pop de + push hl push bc + ld b,0 + ex af,af' + ld ixH,7 +BucleScans: + ld c,a push de push hl - ld b,0 - ld c,d - ex de,hl - ld hl,32 - add hl,de ldir pop hl - ld de,32 - add hl,de pop de + inc h + inc d + dec ixH + jp nz,BucleScans + ld c,a + ldir +AttrAddress: + ld hl,AttrAddress + ld d,h + ld e,l + ld c,32 + add hl,bc + ld (AttrAddress+1),hl + ld c,a + ldir + ex af,af' pop bc - djnz BucleAttrs + jp BucleRows +CleanBottomRow: + ld b,0 + ex af,af' + ld ixH,8 + pop hl +CleanBottomScans: + ld (hl),b + ld c,a + dec c + jr z,AfterLDIR + push hl + ld d,h + ld e,l + inc de + ldir + pop hl +AfterLDIR: + inc h + dec ixH + jp nz,CleanBottomScans + pop ix ENDP pop namespace -#line 385 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" +#line 402 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" _WinScrollUp__leave: ret _WinScrollDown: -#line 394 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" +#line 412 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" push namespace core PROC - LOCAL BucleScans, BucleAttrs, ScrollAttrs - LOCAL CleanLast, CleanLastLoop, EndCleanScan + LOCAL BucleRows, BucleScans, AttrAddress + LOCAL CleanTopRow, CleanTopScans, AfterLDIR ld b, a pop hl pop de @@ -376,88 +369,19 @@ _WinScrollDown: pop de ex (sp), hl ld e, h - ld a, e - or a - ret z - or d - ret z ld a, b add a, e dec a ld b, a - push bc - push de - ld a,b - and 18h - ld h,a - ld a,b - and 07h - add a,a - add a,a - add a,a - add a,a - add a,a - add a,c - ld l,a - ld bc, (SCREEN_ADDR) - add hl, bc - ld a,e - ld c, d - ld d, h - ld e, l - dec a - jr z, CleanLast - add a,a - add a,a - add a,a - ld b, a - inc d - inc d - inc d - inc d - inc d - inc d - inc d - call SP.PixelUp -BucleScans: - push bc - push de - push hl - ld b, 0 - ldir - pop hl - pop de - pop bc - call SP.PixelUp - ex de, hl - call SP.PixelUp - ex de, hl - djnz BucleScans -CleanLast: - ex de,hl - pop de - ld b, 8 - ld c, d - push de -CleanLastLoop: - push bc - push hl - ld (hl), 0 - dec c - jr z, EndCleanScan - ld d, h - ld e, l - inc de - ld b, 0 - ldir -EndCleanScan: - pop hl - pop bc - dec h - djnz CleanLastLoop -ScrollAttrs: - pop de - pop bc + ld a, e + or a + ret z + ld a, d + or a + ret z + ex af,af' + push ix + ld ixL,e ld l,b ld h,0 add hl,hl @@ -468,85 +392,96 @@ ScrollAttrs: ld a,l add a,c ld l,a - ld a,h + ld de,(SCREEN_ATTR_ADDR) + add hl,de + ld (AttrAddress+1),hl + ld a,b + and %00011000 ld h,a - ld bc, (SCREEN_ATTR_ADDR) - add hl, bc - ld b,e + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + push hl +BucleRows: + dec ixL + jr z,CleanTopRow dec b - ret z -BucleAttrs: + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + pop de + push hl push bc + ld b,0 + ex af,af' + ld ixH,7 +BucleScans: + ld c,a push de push hl - ld b,0 - ld c,d - ex de,hl - ld hl,-32 - add hl,de ldir pop hl - ld de,-32 - add hl,de pop de + inc h + inc d + dec ixH + jp nz,BucleScans + ld c,a + ldir +AttrAddress: + ld hl,AttrAddress + ld d,h + ld e,l + ld c,32 + sbc hl,bc + ld (AttrAddress+1),hl + ld c,a + ldir + ex af,af' pop bc - djnz BucleAttrs + jp BucleRows +CleanTopRow: + ld b,0 + ex af,af' + ld ixH,8 + pop hl +CleanTopScans: + ld (hl),b + ld c,a + dec c + jr z,AfterLDIR + push hl + ld d,h + ld e,l + inc de + ldir + pop hl +AfterLDIR: + inc h + dec ixH + jp nz,CleanTopScans + pop ix ENDP pop namespace -#line 535 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" +#line 550 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" _WinScrollDown__leave: ret ;; --- end of user code --- -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/SP/PixelDown.asm" - ; - ; PixelDown - ; Alvin Albrecht 2002 - ; - ; Pixel Down - ; - ; Adjusts screen address HL to move one pixel down in the display. - ; (0,0) is located at the top left corner of the screen. - ; -; enter: HL = valid screen address -; exit : Carry = moved off screen - ; Carry'= moved off current cell (needs ATTR update) - ; HL = moves one pixel down -; used : AF, HL - push namespace core -SP.PixelDown: - PROC - LOCAL leave - push de - ld de, (SCREEN_ADDR) - or a - sbc hl, de - inc h - ld a,h - and $07 - jr nz, leave - scf ; Sets carry on F', which flags ATTR must be updated - ex af, af' - ld a,h - sub $08 - ld h,a - ld a,l - add a,$20 - ld l,a - jr nc, leave - ld a,h - add a,$08 - ld h,a - cp $19 ; carry = 0 => Out of screen - jr c, leave ; returns if out of screen - ccf - pop de - ret -leave: - add hl, de ; This always sets Carry = 0 - pop de - ret - ENDP - pop namespace #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sysvars.asm" ;; ----------------------------------------------------------------------- ;; ZX Basic System Vars @@ -573,84 +508,5 @@ SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) SCR_ROWS EQU 24 ; Screen height in rows SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS pop namespace -#line 58 "/zxbasic/src/lib/arch/zx48k/runtime/SP/PixelDown.asm" -#line 540 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/SP/PixelUp.asm" - ; - ; PixelUp - ; Alvin Albrecht 2002 - ; - ; Pixel Up - ; - ; Adjusts screen address HL to move one pixel up in the display. - ; (0,0) is located at the top left corner of the screen. - ; -; enter: HL = valid screen address -; exit : Carry = moved off screen - ; HL = moves one pixel up -; used : AF, HL - push namespace core -SP.PixelUp: - PROC - LOCAL leave - push de - ld de, (SCREEN_ADDR) - or a - sbc hl, de - ld a,h - dec h - and $07 - jr nz, leave - scf ; sets C' to 1 (ATTR update needed) - ex af, af' - ld a,$08 - add a,h - ld h,a - ld a,l - sub $20 - ld l,a - jr nc, leave - ld a,h - sub $08 - ld h,a -leave: - push af - add hl, de - pop af - pop de - ret - ENDP - pop namespace -#line 541 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cls.asm" - ;; Clears the user screen (24 rows) - push namespace core -CLS: - PROC - ld hl, 0 - ld (COORDS), hl - ld hl, SCR_SIZE - ld (S_POSN), hl - ld hl, (SCREEN_ADDR) - ld (DFCC), hl - ld (hl), 0 - ld d, h - ld e, l - inc de - ld bc, 6143 - ldir - ; Now clear attributes - ld hl, (SCREEN_ATTR_ADDR) - ld (DFCCL), hl - ld d, h - ld e, l - inc de - ld a, (ATTR_P) - ld (hl), a - ld bc, 767 - ldir - ret - ENDP - pop namespace -#line 542 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" +#line 555 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" END diff --git a/tests/functional/arch/zx48k/subrec.asm b/tests/functional/arch/zx48k/subrec.asm index a21e61db9..46a2cb3f7 100644 --- a/tests/functional/arch/zx48k/subrec.asm +++ b/tests/functional/arch/zx48k/subrec.asm @@ -201,7 +201,6 @@ __TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) ld l, c ret pop namespace - f #line 94 "arch/zx48k/subrec.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" ; SUB32 diff --git a/tests/functional/arch/zxnext/addf.asm b/tests/functional/arch/zxnext/addf.asm index 7095c62f8..c8b3c636d 100644 --- a/tests/functional/arch/zxnext/addf.asm +++ b/tests/functional/arch/zxnext/addf.asm @@ -54,6 +54,13 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/addf.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/addf.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC @@ -93,7 +100,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/addf.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/addf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -111,6 +118,7 @@ __ADDF: ; Addition defb 38h; ; END CALC jp __FPSTACK_POP pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/addf.asm" #line 38 "arch/zxnext/addf.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/pushf.asm" ; Routine to push Float pointed by HL diff --git a/tests/functional/arch/zxnext/array03.asm b/tests/functional/arch/zxnext/array03.asm index 9b6f3cfe8..fa2dae907 100644 --- a/tests/functional/arch/zxnext/array03.asm +++ b/tests/functional/arch/zxnext/array03.asm @@ -75,7 +75,7 @@ _a.__DATA__: ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" push namespace core -__MUL16: ; Mutiplies HL with the last value stored into de stack +__MUL16: ; Multiplies HL with the last value stored into de stack ; Works for both signed and unsigned PROC ex de, hl diff --git a/tests/functional/arch/zxnext/array06.asm b/tests/functional/arch/zxnext/array06.asm index 3dbc676bf..2acf1f6b8 100644 --- a/tests/functional/arch/zxnext/array06.asm +++ b/tests/functional/arch/zxnext/array06.asm @@ -86,7 +86,7 @@ _a.__DATA__: ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" push namespace core -__MUL16: ; Mutiplies HL with the last value stored into de stack +__MUL16: ; Multiplies HL with the last value stored into de stack ; Works for both signed and unsigned PROC ex de, hl diff --git a/tests/functional/arch/zxnext/array07.asm b/tests/functional/arch/zxnext/array07.asm index 0f18713e0..7b611a76e 100644 --- a/tests/functional/arch/zxnext/array07.asm +++ b/tests/functional/arch/zxnext/array07.asm @@ -111,7 +111,7 @@ _test__leave: ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" push namespace core -__MUL16: ; Mutiplies HL with the last value stored into de stack +__MUL16: ; Multiplies HL with the last value stored into de stack ; Works for both signed and unsigned PROC ex de, hl @@ -252,7 +252,7 @@ __FNMUL2: ENDP pop namespace #line 41 "arch/zxnext/array07.bas" -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/free.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -312,7 +312,7 @@ __FNMUL2: ; HL = BLOCK Start & DE = Length. ; An init directive is useful for initialization routines. ; They will be added automatically if needed. -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/heapinit.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -419,7 +419,7 @@ __MEM_INIT2: ret ENDP pop namespace -#line 69 "/zxbasic/src/lib/arch/zxnext/runtime/free.asm" +#line 69 "/zxbasic/src/lib/arch/zxnext/runtime/mem/free.asm" ; --------------------------------------------------------------------- ; MEM_FREE ; Frees a block of memory @@ -531,7 +531,7 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/strcpy.asm" -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/realloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -625,8 +625,19 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/mem/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -716,9 +727,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 113 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ret z ; NULL -#line 115 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -783,7 +794,8 @@ __MEM_SUBTRACT: ret ENDP pop namespace -#line 71 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +#line 71 "/zxbasic/src/lib/arch/zxnext/runtime/mem/realloc.asm" ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. diff --git a/tests/functional/arch/zxnext/array08.asm b/tests/functional/arch/zxnext/array08.asm index 549aab9e6..1e39962a1 100644 --- a/tests/functional/arch/zxnext/array08.asm +++ b/tests/functional/arch/zxnext/array08.asm @@ -91,7 +91,7 @@ _a.__DATA__: ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" push namespace core -__MUL16: ; Mutiplies HL with the last value stored into de stack +__MUL16: ; Multiplies HL with the last value stored into de stack ; Works for both signed and unsigned PROC ex de, hl @@ -244,7 +244,7 @@ __FNMUL2: ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/strcpy.asm" -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/realloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -338,8 +338,19 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/mem/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -399,7 +410,7 @@ __STOP: ; HL = BLOCK Start & DE = Length. ; An init directive is useful for initialization routines. ; They will be added automatically if needed. -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/heapinit.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -506,7 +517,7 @@ __MEM_INIT2: ret ENDP pop namespace -#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -537,9 +548,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 113 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ret z ; NULL -#line 115 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -604,8 +615,9 @@ __MEM_SUBTRACT: ret ENDP pop namespace -#line 71 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/free.asm" +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +#line 71 "/zxbasic/src/lib/arch/zxnext/runtime/mem/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -763,7 +775,7 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ret ENDP pop namespace -#line 72 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" +#line 72 "/zxbasic/src/lib/arch/zxnext/runtime/mem/realloc.asm" ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. diff --git a/tests/functional/arch/zxnext/array09.asm b/tests/functional/arch/zxnext/array09.asm index cb700e325..49e2fa613 100644 --- a/tests/functional/arch/zxnext/array09.asm +++ b/tests/functional/arch/zxnext/array09.asm @@ -91,7 +91,7 @@ _a.__DATA__: ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" push namespace core -__MUL16: ; Mutiplies HL with the last value stored into de stack +__MUL16: ; Multiplies HL with the last value stored into de stack ; Works for both signed and unsigned PROC ex de, hl @@ -244,7 +244,7 @@ __FNMUL2: ; This function will resize (REALLOC) the space pointed by HL ; before copying the content of b$ into a$ #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/strcpy.asm" -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/realloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -338,8 +338,19 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/mem/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -399,7 +410,7 @@ __STOP: ; HL = BLOCK Start & DE = Length. ; An init directive is useful for initialization routines. ; They will be added automatically if needed. -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/heapinit.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -506,7 +517,7 @@ __MEM_INIT2: ret ENDP pop namespace -#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -537,9 +548,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 113 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ret z ; NULL -#line 115 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -604,8 +615,9 @@ __MEM_SUBTRACT: ret ENDP pop namespace -#line 71 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/free.asm" +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +#line 71 "/zxbasic/src/lib/arch/zxnext/runtime/mem/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -763,7 +775,7 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ret ENDP pop namespace -#line 72 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" +#line 72 "/zxbasic/src/lib/arch/zxnext/runtime/mem/realloc.asm" ; --------------------------------------------------------------------- ; MEM_REALLOC ; Reallocates a block of memory in the heap. diff --git a/tests/functional/arch/zxnext/bound02.asm b/tests/functional/arch/zxnext/bound02.asm index 76afb9755..fa0f865fa 100644 --- a/tests/functional/arch/zxnext/bound02.asm +++ b/tests/functional/arch/zxnext/bound02.asm @@ -21,7 +21,7 @@ _a: DEFW .LABEL.__LABEL0 _a.__DATA__.__PTR__: DEFW _a.__DATA__ - DEFW 0 + DEFW _a.__LBOUND__ DEFW 0 _a.__DATA__: DEFB 00h @@ -84,6 +84,9 @@ _a.__DATA__: DEFW 0001h DEFW 0004h DEFB 02h +_a.__LBOUND__: + DEFW 0002h + DEFW 0003h .core.ZXBASIC_USER_DATA_END: .core.__MAIN_PROGRAM__: ld a, 1 diff --git a/tests/functional/arch/zxnext/cast_const_to_int.asm b/tests/functional/arch/zxnext/cast_const_to_int.asm new file mode 100644 index 000000000..035405911 --- /dev/null +++ b/tests/functional/arch/zxnext/cast_const_to_int.asm @@ -0,0 +1,225 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_var_neg: + DEFB 00h + DEFB 80h + DEFB 0FEh + DEFB 0FFh +_var: + DEFB 00h + DEFB 80h + DEFB 01h + DEFB 00h +_var_neg_fl: + DEFB 81h + DEFB 0C0h + DEFB 00h + DEFB 00h + DEFB 00h +_r_int: + DEFB 00, 00 +_r_long: + DEFB 00, 00, 00, 00 +_r_ulong: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_var_neg) + ld de, (_var_neg + 2) + ex de, hl + ld (_r_int), hl + ld hl, 65534 + ld (_r_int), hl + ld hl, (_var_neg) + ld de, (_var_neg + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_r_long), hl + ld (_r_long + 2), de + ld hl, (_var_neg) + ld de, (_var_neg + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_r_ulong), hl + ld (_r_ulong + 2), de + ld hl, (_var) + ld de, (_var + 2) + ex de, hl + ld (_r_int), hl + ld hl, 1 + ld (_r_int), hl + ld hl, (_var) + ld de, (_var + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_r_long), hl + ld (_r_long + 2), de + ld hl, (_var) + ld de, (_var + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_r_ulong), hl + ld (_r_ulong + 2), de + ld a, (_var_neg_fl) + ld de, (_var_neg_fl + 1) + ld bc, (_var_neg_fl + 3) + call .core.__FTOU32REG + ld (_r_int), hl + ld hl, 65534 + ld (_r_int), hl + ld a, (_var_neg_fl) + ld de, (_var_neg_fl + 1) + ld bc, (_var_neg_fl + 3) + call .core.__FTOU32REG + ld (_r_long), hl + ld (_r_long + 2), de + ld a, (_var_neg_fl) + ld de, (_var_neg_fl + 1) + ld bc, (_var_neg_fl + 3) + call .core.__FTOU32REG + ld (_r_ulong), hl + ld (_r_ulong + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/ftou32reg.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/ftou32reg.asm" + push namespace core +__FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS 32 bit signed) + ; Input FP number in A EDCB (A exponent, EDCB mantissa) + ; Output: DEHL 32 bit number (signed) + PROC + LOCAL __IS_FLOAT + LOCAL __NEGATE + or a + jr nz, __IS_FLOAT + ; Here if it is a ZX ROM Integer + ld h, c + ld l, d + ld d, e + ret +__IS_FLOAT: ; Jumps here if it is a true floating point number + ld h, e + push hl ; Stores it for later (Contains Sign in H) + push de + push bc + exx + pop de ; Loads mantissa into C'B' E'D' + pop bc ; + set 7, c ; Highest mantissa bit is always 1 + exx + ld hl, 0 ; DEHL = 0 + ld d, h + ld e, l + ;ld a, c ; Get exponent + sub 128 ; Exponent -= 128 + jr z, __FTOU32REG_END ; If it was <= 128, we are done (Integers must be > 128) + jr c, __FTOU32REG_END ; It was decimal (0.xxx). We are done (return 0) + ld b, a ; Loop counter = exponent - 128 +__FTOU32REG_LOOP: + exx ; Shift C'B' E'D' << 1, output bit stays in Carry + sla d + rl e + rl b + rl c + exx ; Shift DEHL << 1, inserting the carry on the right + rl l + rl h + rl e + rl d + djnz __FTOU32REG_LOOP +__FTOU32REG_END: + pop af ; Take the sign bit + or a ; Sets SGN bit to 1 if negative + jp m, __NEGATE ; Negates DEHL + ret +__NEGATE: + exx + ld a, d + or e + or b + or c + exx + jr z, __END + inc l + jr nz, __END + inc h + jr nz, __END + inc de + LOCAL __END +__END: + jp __NEG32 + ENDP +__FTOU8: ; Converts float in C ED LH to Unsigned byte in A + call __FTOU32REG + ld a, l + ret + pop namespace +#line 88 "arch/zxnext/cast_const_to_int.bas" + END diff --git a/tests/functional/arch/zxnext/cast_const_to_int.bas b/tests/functional/arch/zxnext/cast_const_to_int.bas new file mode 100644 index 000000000..25d7b2b04 --- /dev/null +++ b/tests/functional/arch/zxnext/cast_const_to_int.bas @@ -0,0 +1,25 @@ +dim var_neg as fixed = -1.5 +const const_neg as fixed = -1.5 +dim var as Fixed = 1.5 +const const_ as fixed = 1.5 +dim var_neg_fl as Float = -1.5 +const const_neg_fl as Float = -1.5 + +DIM r_int as Integer +DIM r_long as Long +DIM r_ulong as ULong + +LET r_int = cast(integer, var_neg) +LET r_int = cast(integer, const_neg) +LET r_long = cast(long, var_neg) +LET r_ulong = cast(ulong, var_neg) + +LET r_int = cast(integer, var) +LET r_int = cast(integer, const_) +LET r_long = cast(long, var) +LET r_ulong = cast(ulong, var) + +LET r_int = cast(integer, var_neg_fl) +LET r_int = cast(integer, const_neg_fl) +LET r_long = cast(long, var_neg_fl) +LET r_ulong = cast(ulong, var_neg_fl) diff --git a/tests/functional/arch/zxnext/cast_f16_to_long.asm b/tests/functional/arch/zxnext/cast_f16_to_long.asm new file mode 100644 index 000000000..6c0c77264 --- /dev/null +++ b/tests/functional/arch/zxnext/cast_f16_to_long.asm @@ -0,0 +1,47 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_v1: + DEFB 00h + DEFB 80h + DEFB 01h + DEFB 00h +_v2: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_v1) + ld de, (_v1 + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_v2), hl + ld (_v2 + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/cast_f16_to_long.bas b/tests/functional/arch/zxnext/cast_f16_to_long.bas new file mode 100644 index 000000000..b8f1315d7 --- /dev/null +++ b/tests/functional/arch/zxnext/cast_f16_to_long.bas @@ -0,0 +1,5 @@ +DIM v1 As Fixed = 1.5 +DIM v2 As Long + +v2 = v1 + diff --git a/tests/functional/arch/zxnext/cast_f16_to_ulong.asm b/tests/functional/arch/zxnext/cast_f16_to_ulong.asm new file mode 100644 index 000000000..6c0c77264 --- /dev/null +++ b/tests/functional/arch/zxnext/cast_f16_to_ulong.asm @@ -0,0 +1,47 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_v1: + DEFB 00h + DEFB 80h + DEFB 01h + DEFB 00h +_v2: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_v1) + ld de, (_v1 + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_v2), hl + ld (_v2 + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/cast_f16_to_ulong.bas b/tests/functional/arch/zxnext/cast_f16_to_ulong.bas new file mode 100644 index 000000000..e805c2240 --- /dev/null +++ b/tests/functional/arch/zxnext/cast_f16_to_ulong.bas @@ -0,0 +1,5 @@ +DIM v1 As Fixed = 1.5 +DIM v2 As ULong + +v2 = v1 + diff --git a/tests/functional/arch/zxnext/divf00.asm b/tests/functional/arch/zxnext/divf00.asm index 800f362e2..da0a18f3b 100644 --- a/tests/functional/arch/zxnext/divf00.asm +++ b/tests/functional/arch/zxnext/divf00.asm @@ -47,6 +47,13 @@ _b: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC @@ -86,7 +93,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" ; Simple error control routines ; vim:ts=4:et: @@ -121,7 +128,7 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf.asm" +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses C EDHL registers as 1st paramter. @@ -166,6 +173,7 @@ __DIVBYZERO: ret ENDP pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf.asm" #line 21 "arch/zxnext/divf00.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/pushf.asm" ; Routine to push Float pointed by HL diff --git a/tests/functional/arch/zxnext/divf01.asm b/tests/functional/arch/zxnext/divf01.asm index 31baaf880..67c32839d 100644 --- a/tests/functional/arch/zxnext/divf01.asm +++ b/tests/functional/arch/zxnext/divf01.asm @@ -51,6 +51,13 @@ _b: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC @@ -90,7 +97,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" ; Simple error control routines ; vim:ts=4:et: @@ -125,7 +132,7 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf.asm" +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses C EDHL registers as 1st paramter. @@ -170,6 +177,7 @@ __DIVBYZERO: ret ENDP pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf.asm" #line 25 "arch/zxnext/divf01.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storef.asm" push namespace core diff --git a/tests/functional/arch/zxnext/divf16.asm b/tests/functional/arch/zxnext/divf16.asm index 6b6723e23..8a2a54ee7 100644 --- a/tests/functional/arch/zxnext/divf16.asm +++ b/tests/functional/arch/zxnext/divf16.asm @@ -98,7 +98,21 @@ _b: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf16.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" push namespace core __ABS32: @@ -124,7 +138,7 @@ __NEG32: ; Negates DEHL (Two's complement) inc de ret pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" ; --------------------------------------------------------- push namespace core __DIVU32: ; 32 bit unsigned division @@ -237,7 +251,8 @@ __MODI32: ; 32bits signed division modulus call __DIVI32START jp __MODU32START pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf16.asm" push namespace core __DIVF16: ; 16.16 Fixed point Division (signed) ; DE.HL = Dividend, Stack Top = Divisor @@ -307,5 +322,6 @@ __ENDF16DIV: ; Put the sign on the result ret z jp __NEG32 ; Negates DEHL and returns from there pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" #line 80 "arch/zxnext/divf16.bas" END diff --git a/tests/functional/arch/zxnext/divf16a.asm b/tests/functional/arch/zxnext/divf16a.asm index ef662a0dd..3a85505f8 100644 --- a/tests/functional/arch/zxnext/divf16a.asm +++ b/tests/functional/arch/zxnext/divf16a.asm @@ -43,7 +43,21 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf16.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" push namespace core __ABS32: @@ -69,7 +83,7 @@ __NEG32: ; Negates DEHL (Two's complement) inc de ret pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" ; --------------------------------------------------------- push namespace core __DIVU32: ; 32 bit unsigned division @@ -182,7 +196,8 @@ __MODI32: ; 32bits signed division modulus call __DIVI32START jp __MODU32START pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf16.asm" push namespace core __DIVF16: ; 16.16 Fixed point Division (signed) ; DE.HL = Dividend, Stack Top = Divisor @@ -252,5 +267,6 @@ __ENDF16DIV: ; Put the sign on the result ret z jp __NEG32 ; Negates DEHL and returns from there pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" #line 27 "arch/zxnext/divf16a.bas" END diff --git a/tests/functional/arch/zxnext/divf16b.asm b/tests/functional/arch/zxnext/divf16b.asm index d17b750f4..8215bcb86 100644 --- a/tests/functional/arch/zxnext/divf16b.asm +++ b/tests/functional/arch/zxnext/divf16b.asm @@ -53,7 +53,21 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf16.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" push namespace core __ABS32: @@ -79,7 +93,7 @@ __NEG32: ; Negates DEHL (Two's complement) inc de ret pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" ; --------------------------------------------------------- push namespace core __DIVU32: ; 32 bit unsigned division @@ -192,7 +206,8 @@ __MODI32: ; 32bits signed division modulus call __DIVI32START jp __MODU32START pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf16.asm" push namespace core __DIVF16: ; 16.16 Fixed point Division (signed) ; DE.HL = Dividend, Stack Top = Divisor @@ -262,5 +277,6 @@ __ENDF16DIV: ; Put the sign on the result ret z jp __NEG32 ; Negates DEHL and returns from there pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" #line 37 "arch/zxnext/divf16b.bas" END diff --git a/tests/functional/arch/zxnext/divf16c.asm b/tests/functional/arch/zxnext/divf16c.asm index 18191336f..ba334fab9 100644 --- a/tests/functional/arch/zxnext/divf16c.asm +++ b/tests/functional/arch/zxnext/divf16c.asm @@ -85,7 +85,21 @@ _l: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf16.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" push namespace core __ABS32: @@ -111,7 +125,7 @@ __NEG32: ; Negates DEHL (Two's complement) inc de ret pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" ; --------------------------------------------------------- push namespace core __DIVU32: ; 32 bit unsigned division @@ -224,7 +238,8 @@ __MODI32: ; 32bits signed division modulus call __DIVI32START jp __MODU32START pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf16.asm" push namespace core __DIVF16: ; 16.16 Fixed point Division (signed) ; DE.HL = Dividend, Stack Top = Divisor @@ -294,6 +309,7 @@ __ENDF16DIV: ; Put the sign on the result ret z jp __NEG32 ; Negates DEHL and returns from there pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" #line 59 "arch/zxnext/divf16c.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" ; Exchanges current DE HL with the diff --git a/tests/functional/arch/zxnext/divi16a.asm b/tests/functional/arch/zxnext/divi16a.asm index ee8c86523..d3ddff193 100644 --- a/tests/functional/arch/zxnext/divi16a.asm +++ b/tests/functional/arch/zxnext/divi16a.asm @@ -35,6 +35,13 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" ; 16 bit division and modulo functions ; for both signed and unsigned values #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg16.asm" @@ -53,7 +60,7 @@ __NEGHL: inc hl ret pop namespace -#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" +#line 5 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" push namespace core __DIVU16: ; 16 bit unsigned division ; HL = Dividend, Stack Top = Divisor @@ -126,5 +133,6 @@ __MODI16: ; 16 bit modulus ; de = quotient ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" #line 19 "arch/zxnext/divi16a.bas" END diff --git a/tests/functional/arch/zxnext/divi16b.asm b/tests/functional/arch/zxnext/divi16b.asm index 8f574f369..a9abbb4a5 100644 --- a/tests/functional/arch/zxnext/divi16b.asm +++ b/tests/functional/arch/zxnext/divi16b.asm @@ -42,6 +42,13 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" ; 16 bit division and modulo functions ; for both signed and unsigned values #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg16.asm" @@ -60,7 +67,7 @@ __NEGHL: inc hl ret pop namespace -#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" +#line 5 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" push namespace core __DIVU16: ; 16 bit unsigned division ; HL = Dividend, Stack Top = Divisor @@ -133,5 +140,6 @@ __MODI16: ; 16 bit modulus ; de = quotient ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" #line 26 "arch/zxnext/divi16b.bas" END diff --git a/tests/functional/arch/zxnext/divi32c.asm b/tests/functional/arch/zxnext/divi32c.asm index f6b2fb51d..8032f1097 100644 --- a/tests/functional/arch/zxnext/divi32c.asm +++ b/tests/functional/arch/zxnext/divi32c.asm @@ -88,6 +88,13 @@ _l: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" push namespace core __ABS32: @@ -113,7 +120,7 @@ __NEG32: ; Negates DEHL (Two's complement) inc de ret pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" ; --------------------------------------------------------- push namespace core __DIVU32: ; 32 bit unsigned division @@ -226,6 +233,7 @@ __MODI32: ; 32bits signed division modulus call __DIVI32START jp __MODU32START pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" #line 62 "arch/zxnext/divi32c.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" ; Exchanges current DE HL with the diff --git a/tests/functional/arch/zxnext/divi8.asm b/tests/functional/arch/zxnext/divi8.asm index 9b68aafde..cac68778c 100644 --- a/tests/functional/arch/zxnext/divi8.asm +++ b/tests/functional/arch/zxnext/divi8.asm @@ -58,6 +58,13 @@ _b: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -122,5 +129,6 @@ __MODI8_FAST: ; __FASTCALL__ entry ld a, l ; remainder ret ; a = Modulus pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" #line 40 "arch/zxnext/divi8.bas" END diff --git a/tests/functional/arch/zxnext/divi8a.asm b/tests/functional/arch/zxnext/divi8a.asm index 1b2fe13d6..d2d17a125 100644 --- a/tests/functional/arch/zxnext/divi8a.asm +++ b/tests/functional/arch/zxnext/divi8a.asm @@ -35,6 +35,13 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -99,5 +106,6 @@ __MODI8_FAST: ; __FASTCALL__ entry ld a, l ; remainder ret ; a = Modulus pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" #line 19 "arch/zxnext/divi8a.bas" END diff --git a/tests/functional/arch/zxnext/divi8b.asm b/tests/functional/arch/zxnext/divi8b.asm index 4eac0033a..6167a3b63 100644 --- a/tests/functional/arch/zxnext/divi8b.asm +++ b/tests/functional/arch/zxnext/divi8b.asm @@ -41,6 +41,13 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -105,5 +112,6 @@ __MODI8_FAST: ; __FASTCALL__ entry ld a, l ; remainder ret ; a = Modulus pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" #line 25 "arch/zxnext/divi8b.bas" END diff --git a/tests/functional/arch/zxnext/divu16.asm b/tests/functional/arch/zxnext/divu16.asm index 5084ee7f8..d864a7b02 100644 --- a/tests/functional/arch/zxnext/divu16.asm +++ b/tests/functional/arch/zxnext/divu16.asm @@ -59,6 +59,13 @@ _b: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" ; 16 bit division and modulo functions ; for both signed and unsigned values #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg16.asm" @@ -77,7 +84,7 @@ __NEGHL: inc hl ret pop namespace -#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" +#line 5 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" push namespace core __DIVU16: ; 16 bit unsigned division ; HL = Dividend, Stack Top = Divisor @@ -150,5 +157,6 @@ __MODI16: ; 16 bit modulus ; de = quotient ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" #line 41 "arch/zxnext/divu16.bas" END diff --git a/tests/functional/arch/zxnext/divu16a.asm b/tests/functional/arch/zxnext/divu16a.asm index b0e494c83..f435badbd 100644 --- a/tests/functional/arch/zxnext/divu16a.asm +++ b/tests/functional/arch/zxnext/divu16a.asm @@ -35,6 +35,13 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" ; 16 bit division and modulo functions ; for both signed and unsigned values #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg16.asm" @@ -53,7 +60,7 @@ __NEGHL: inc hl ret pop namespace -#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" +#line 5 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" push namespace core __DIVU16: ; 16 bit unsigned division ; HL = Dividend, Stack Top = Divisor @@ -126,5 +133,6 @@ __MODI16: ; 16 bit modulus ; de = quotient ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" #line 19 "arch/zxnext/divu16a.bas" END diff --git a/tests/functional/arch/zxnext/divu16b.asm b/tests/functional/arch/zxnext/divu16b.asm index 0945964d4..b296a0e3a 100644 --- a/tests/functional/arch/zxnext/divu16b.asm +++ b/tests/functional/arch/zxnext/divu16b.asm @@ -42,6 +42,13 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" ; 16 bit division and modulo functions ; for both signed and unsigned values #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg16.asm" @@ -60,7 +67,7 @@ __NEGHL: inc hl ret pop namespace -#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" +#line 5 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" push namespace core __DIVU16: ; 16 bit unsigned division ; HL = Dividend, Stack Top = Divisor @@ -133,5 +140,6 @@ __MODI16: ; 16 bit modulus ; de = quotient ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" #line 26 "arch/zxnext/divu16b.bas" END diff --git a/tests/functional/arch/zxnext/divu32c.asm b/tests/functional/arch/zxnext/divu32c.asm index 9a8a31f85..581d0b191 100644 --- a/tests/functional/arch/zxnext/divu32c.asm +++ b/tests/functional/arch/zxnext/divu32c.asm @@ -88,6 +88,13 @@ _l: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" push namespace core __ABS32: @@ -113,7 +120,7 @@ __NEG32: ; Negates DEHL (Two's complement) inc de ret pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" ; --------------------------------------------------------- push namespace core __DIVU32: ; 32 bit unsigned division @@ -226,6 +233,7 @@ __MODI32: ; 32bits signed division modulus call __DIVI32START jp __MODU32START pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" #line 62 "arch/zxnext/divu32c.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" ; Exchanges current DE HL with the diff --git a/tests/functional/arch/zxnext/divu8.asm b/tests/functional/arch/zxnext/divu8.asm index b27a42334..6a31559eb 100644 --- a/tests/functional/arch/zxnext/divu8.asm +++ b/tests/functional/arch/zxnext/divu8.asm @@ -58,6 +58,13 @@ _b: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -122,5 +129,6 @@ __MODI8_FAST: ; __FASTCALL__ entry ld a, l ; remainder ret ; a = Modulus pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" #line 40 "arch/zxnext/divu8.bas" END diff --git a/tests/functional/arch/zxnext/divu8a.asm b/tests/functional/arch/zxnext/divu8a.asm index bd80b4dba..7e236c98d 100644 --- a/tests/functional/arch/zxnext/divu8a.asm +++ b/tests/functional/arch/zxnext/divu8a.asm @@ -35,6 +35,13 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -99,5 +106,6 @@ __MODI8_FAST: ; __FASTCALL__ entry ld a, l ; remainder ret ; a = Modulus pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" #line 19 "arch/zxnext/divu8a.bas" END diff --git a/tests/functional/arch/zxnext/divu8b.asm b/tests/functional/arch/zxnext/divu8b.asm index fc153b041..b8509aea6 100644 --- a/tests/functional/arch/zxnext/divu8b.asm +++ b/tests/functional/arch/zxnext/divu8b.asm @@ -41,6 +41,13 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -105,5 +112,6 @@ __MODI8_FAST: ; __FASTCALL__ entry ld a, l ; remainder ret ; a = Modulus pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" #line 25 "arch/zxnext/divu8b.bas" END diff --git a/tests/functional/arch/zxnext/equf.asm b/tests/functional/arch/zxnext/equf.asm index f38199460..918367e2e 100644 --- a/tests/functional/arch/zxnext/equf.asm +++ b/tests/functional/arch/zxnext/equf.asm @@ -46,6 +46,13 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/addf.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/addf.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC @@ -85,7 +92,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/addf.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/addf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -103,6 +110,7 @@ __ADDF: ; Addition defb 38h; ; END CALC jp __FPSTACK_POP pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/addf.asm" #line 30 "arch/zxnext/equf.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/eqf.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/u32tofreg.asm" diff --git a/tests/functional/arch/zxnext/fact.asm b/tests/functional/arch/zxnext/fact.asm index 5b1a93ff3..7e875f4ad 100644 --- a/tests/functional/arch/zxnext/fact.asm +++ b/tests/functional/arch/zxnext/fact.asm @@ -97,7 +97,21 @@ _fact__leave: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/_mul32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/_mul32.asm" ; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 ; Used with permission. ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') @@ -156,7 +170,8 @@ __LMULSTART: djnz __LMULLOOP ret ; result in h'l'hlb'c'ac pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul32.asm" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/_mul32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" push namespace core __MUL32: ; multiplies 32 bit un/signed integer. @@ -178,8 +193,16 @@ __TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) ld l, c ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul32.asm" #line 79 "arch/zxnext/fact.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" ; SUB32 ; Perform TOP of the stack - DEHL ; Pops operand out of the stack (CALLEE) @@ -205,5 +228,6 @@ __SUB32: exx ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" #line 80 "arch/zxnext/fact.bas" END diff --git a/tests/functional/arch/zxnext/gef16.asm b/tests/functional/arch/zxnext/gef16.asm index 55090532f..d90cc89ef 100644 --- a/tests/functional/arch/zxnext/gef16.asm +++ b/tests/functional/arch/zxnext/gef16.asm @@ -105,6 +105,13 @@ _l: ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lti32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" ; SUB32 ; Perform TOP of the stack - DEHL ; Pops operand out of the stack (CALLEE) @@ -130,6 +137,7 @@ __SUB32: exx ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" #line 3 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lti32.asm" push namespace core __LTI32: ; Test 32 bit values in Top of the stack < HLDE diff --git a/tests/functional/arch/zxnext/gei32.asm b/tests/functional/arch/zxnext/gei32.asm index cf153f38b..de31b572d 100644 --- a/tests/functional/arch/zxnext/gei32.asm +++ b/tests/functional/arch/zxnext/gei32.asm @@ -121,6 +121,13 @@ _l: ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lti32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" ; SUB32 ; Perform TOP of the stack - DEHL ; Pops operand out of the stack (CALLEE) @@ -146,6 +153,7 @@ __SUB32: exx ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" #line 3 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lti32.asm" push namespace core __LTI32: ; Test 32 bit values in Top of the stack < HLDE diff --git a/tests/functional/arch/zxnext/geu32.asm b/tests/functional/arch/zxnext/geu32.asm index ce0f8ad1a..d66a4656b 100644 --- a/tests/functional/arch/zxnext/geu32.asm +++ b/tests/functional/arch/zxnext/geu32.asm @@ -120,6 +120,13 @@ _l: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" ; SUB32 ; Perform TOP of the stack - DEHL ; Pops operand out of the stack (CALLEE) @@ -145,6 +152,7 @@ __SUB32: exx ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" #line 94 "arch/zxnext/geu32.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" ; Exchanges current DE HL with the diff --git a/tests/functional/arch/zxnext/gtf16.asm b/tests/functional/arch/zxnext/gtf16.asm index 798b17d75..3a39cde3e 100644 --- a/tests/functional/arch/zxnext/gtf16.asm +++ b/tests/functional/arch/zxnext/gtf16.asm @@ -105,6 +105,13 @@ _l: ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lei32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" ; SUB32 ; Perform TOP of the stack - DEHL ; Pops operand out of the stack (CALLEE) @@ -130,6 +137,7 @@ __SUB32: exx ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" #line 3 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lei32.asm" push namespace core __LEI32: ; Test 32 bit values Top of the stack <= HL,DE diff --git a/tests/functional/arch/zxnext/gti32.asm b/tests/functional/arch/zxnext/gti32.asm index 3729236b1..1c37f5ffd 100644 --- a/tests/functional/arch/zxnext/gti32.asm +++ b/tests/functional/arch/zxnext/gti32.asm @@ -121,6 +121,13 @@ _l: ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lei32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" ; SUB32 ; Perform TOP of the stack - DEHL ; Pops operand out of the stack (CALLEE) @@ -146,6 +153,7 @@ __SUB32: exx ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" #line 3 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lei32.asm" push namespace core __LEI32: ; Test 32 bit values Top of the stack <= HL,DE diff --git a/tests/functional/arch/zxnext/lbound12.asm b/tests/functional/arch/zxnext/lbound12.asm index e1f60398b..aebcadcaf 100644 --- a/tests/functional/arch/zxnext/lbound12.asm +++ b/tests/functional/arch/zxnext/lbound12.asm @@ -76,10 +76,14 @@ _test1: push hl push hl push hl + ld hl, 0 + push hl + ld hl, _test1.a1.__LBOUND__ + push hl ld hl, -6 ld de, .LABEL.__LABEL5 ld bc, 18 - call .core.__ALLOC_LOCAL_ARRAY + call .core.__ALLOC_LOCAL_ARRAY_WITH_BOUNDS push ix pop hl ld de, -6 @@ -99,6 +103,9 @@ _test1__leave: ld sp, ix pop ix ret +_test1.a1.__LBOUND__: + DEFW 0003h + DEFW 0007h _test2: push ix ld ix, 0 @@ -117,7 +124,7 @@ _test2__leave: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/array/arrayalloc.asm" -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/calloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/calloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -128,7 +135,7 @@ _test2__leave: ; closed source programs). ; ; Please read the MIT license on the internet -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -222,8 +229,8 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 69 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/heapinit.asm" +#line 69 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/heapinit.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -330,7 +337,7 @@ __MEM_INIT2: ret ENDP pop namespace -#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" ; --------------------------------------------------------------------- ; MEM_ALLOC ; Allocates a block of memory in the heap. @@ -361,9 +368,9 @@ __MEM_START: __MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE ld a, h ; HL = NULL (No memory available?) or l -#line 113 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +#line 113 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" ret z ; NULL -#line 115 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +#line 115 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -428,7 +435,7 @@ __MEM_SUBTRACT: ret ENDP pop namespace -#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/calloc.asm" +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/mem/calloc.asm" ; --------------------------------------------------------------------- ; MEM_CALLOC ; Allocates a block of memory in the heap, and clears it filling it @@ -526,9 +533,69 @@ __ALLOC_INITIALIZED_LOCAL_ARRAY: ldir pop hl ; HL = addr of LBound area if used ret + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes. Then sets LBOUND and UBOUND ptrs + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] PTR to the lbound element area + ; [SP + 4] PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS: + call __ALLOC_LOCAL_ARRAY +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS2: + pop bc ;; ret address + pop de ;; lbound + inc hl + ld (hl), e + inc hl + ld (hl), d + pop de ;; PTR to ubound table + push bc ;; puts ret address back + ld a, d + or e + ret z ;; if PTR for UBound is 0, it's not used + inc hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; TOP of the stack = PTR to the element area + ; [SP + 2] = PTR to the element area + ; [SP + 4] = PTR to the lbound element area + ; [SP + 6] = PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS: + ;; Swaps [SP] and [SP + 2] + exx + pop hl ;; Ret address + ex (sp), hl ;; HL <- PTR to Element area, (sp) = Ret address + push hl ;; [SP] = PTR to element area, [SP + 2] = Ret address + exx + call __ALLOC_INITIALIZED_LOCAL_ARRAY + jp __ALLOC_LOCAL_ARRAY_WITH_BOUNDS2 #line 142 "/zxbasic/src/lib/arch/zxnext/runtime/array/arrayalloc.asm" pop namespace -#line 94 "arch/zxnext/lbound12.bas" +#line 101 "arch/zxnext/lbound12.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/array/arraybound.asm" ; --------------------------------------------------------- ; Copyleft (k)2011 by Jose Rodriguez (a.k.a. Boriel) @@ -606,12 +673,12 @@ __DIM_NOT_EXIST: ret ENDP pop namespace -#line 95 "arch/zxnext/lbound12.bas" +#line 102 "arch/zxnext/lbound12.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/array/arraystrfree.asm" ; This routine is in charge of freeing an array of strings from memory ; HL = Pointer to start of array in memory ; Top of the stack = Number of elements of the array -#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/free.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/free.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa ; (a.k.a. Boriel) @@ -811,7 +878,7 @@ __ARRAYSTR_FREE_MEM: ; like the above, buf also frees the array itself pop hl ; recovers array block pointer jp __MEM_FREE ; Frees it and returns from __MEM_FREE pop namespace -#line 96 "arch/zxnext/lbound12.bas" +#line 103 "arch/zxnext/lbound12.bas" .LABEL.__LABEL5: DEFB 01h DEFB 00h diff --git a/tests/functional/arch/zxnext/lei32.asm b/tests/functional/arch/zxnext/lei32.asm index c797304f2..4e337f4b7 100644 --- a/tests/functional/arch/zxnext/lei32.asm +++ b/tests/functional/arch/zxnext/lei32.asm @@ -106,6 +106,13 @@ _l: ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lei32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" ; SUB32 ; Perform TOP of the stack - DEHL ; Pops operand out of the stack (CALLEE) @@ -131,6 +138,7 @@ __SUB32: exx ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" #line 3 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lei32.asm" push namespace core __LEI32: ; Test 32 bit values Top of the stack <= HL,DE diff --git a/tests/functional/arch/zxnext/ltf16.asm b/tests/functional/arch/zxnext/ltf16.asm index c5cc87d85..44a6fccaf 100644 --- a/tests/functional/arch/zxnext/ltf16.asm +++ b/tests/functional/arch/zxnext/ltf16.asm @@ -93,6 +93,13 @@ _l: ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lti32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" ; SUB32 ; Perform TOP of the stack - DEHL ; Pops operand out of the stack (CALLEE) @@ -118,6 +125,7 @@ __SUB32: exx ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" #line 3 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lti32.asm" push namespace core __LTI32: ; Test 32 bit values in Top of the stack < HLDE diff --git a/tests/functional/arch/zxnext/lti32.asm b/tests/functional/arch/zxnext/lti32.asm index 8664b6a11..fd9c08a94 100644 --- a/tests/functional/arch/zxnext/lti32.asm +++ b/tests/functional/arch/zxnext/lti32.asm @@ -106,6 +106,13 @@ _l: ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lti32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" ; SUB32 ; Perform TOP of the stack - DEHL ; Pops operand out of the stack (CALLEE) @@ -131,6 +138,7 @@ __SUB32: exx ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" #line 3 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lti32.asm" push namespace core __LTI32: ; Test 32 bit values in Top of the stack < HLDE diff --git a/tests/functional/arch/zxnext/ltu32.asm b/tests/functional/arch/zxnext/ltu32.asm index 62861b6ba..2b1a05ea4 100644 --- a/tests/functional/arch/zxnext/ltu32.asm +++ b/tests/functional/arch/zxnext/ltu32.asm @@ -115,6 +115,13 @@ _l: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" ; SUB32 ; Perform TOP of the stack - DEHL ; Pops operand out of the stack (CALLEE) @@ -140,6 +147,7 @@ __SUB32: exx ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" #line 89 "arch/zxnext/ltu32.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" ; Exchanges current DE HL with the diff --git a/tests/functional/arch/zxnext/modf.asm b/tests/functional/arch/zxnext/modf.asm index c78b9ea75..caffde7eb 100644 --- a/tests/functional/arch/zxnext/modf.asm +++ b/tests/functional/arch/zxnext/modf.asm @@ -37,6 +37,13 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/modf.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/modf.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC @@ -76,7 +83,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/modf.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/modf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -96,6 +103,7 @@ __MODF: ; MODULO defb 38h; ; END CALC jp __FPSTACK_POP pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/modf.asm" #line 21 "arch/zxnext/modf.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/pushf.asm" ; Routine to push Float pointed by HL diff --git a/tests/functional/arch/zxnext/modf16c.asm b/tests/functional/arch/zxnext/modf16c.asm index 51183c80a..0ca0b87b3 100644 --- a/tests/functional/arch/zxnext/modf16c.asm +++ b/tests/functional/arch/zxnext/modf16c.asm @@ -85,9 +85,30 @@ _l: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/modf16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/modf16.asm" ; Computes A % B for fixed values #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf16.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" push namespace core __ABS32: @@ -113,7 +134,7 @@ __NEG32: ; Negates DEHL (Two's complement) inc de ret pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" ; --------------------------------------------------------- push namespace core __DIVU32: ; 32 bit unsigned division @@ -226,7 +247,8 @@ __MODI32: ; 32bits signed division modulus call __DIVI32START jp __MODU32START pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf16.asm" push namespace core __DIVF16: ; 16.16 Fixed point Division (signed) ; DE.HL = Dividend, Stack Top = Divisor @@ -296,9 +318,24 @@ __ENDF16DIV: ; Put the sign on the result ret z jp __NEG32 ; Negates DEHL and returns from there pop namespace -#line 4 "/zxbasic/src/lib/arch/zxnext/runtime/arith/modf16.asm" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" +#line 4 "/zxbasic/src/lib/arch/zx48k/runtime/arith/modf16.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf16.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/_mul32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/_mul32.asm" ; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 ; Used with permission. ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') @@ -357,7 +394,8 @@ __LMULSTART: djnz __LMULLOOP ret ; result in h'l'hlb'c'ac pop namespace -#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/_mul32.asm" +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf16.asm" push namespace core __MULF16: ; ld a, d ; load sgn into a @@ -392,7 +430,8 @@ __ROUND_FIX: ; rounds a 64bit (32.32) fixed point number to 16.16 jp m, __NEG32 ; if negative, negates it ret pop namespace -#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/modf16.asm" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" +#line 5 "/zxbasic/src/lib/arch/zx48k/runtime/arith/modf16.asm" push namespace core __MODF16: ; 16.16 Fixed point Division (signed) @@ -419,6 +458,7 @@ __MODF16: jp __MULF16 ; multiplies and return from there ENDP pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/modf16.asm" #line 59 "arch/zxnext/modf16c.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" ; Exchanges current DE HL with the diff --git a/tests/functional/arch/zxnext/modi32c.asm b/tests/functional/arch/zxnext/modi32c.asm index 501158e1a..275a73e93 100644 --- a/tests/functional/arch/zxnext/modi32c.asm +++ b/tests/functional/arch/zxnext/modi32c.asm @@ -88,6 +88,13 @@ _l: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" push namespace core __ABS32: @@ -113,7 +120,7 @@ __NEG32: ; Negates DEHL (Two's complement) inc de ret pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" ; --------------------------------------------------------- push namespace core __DIVU32: ; 32 bit unsigned division @@ -226,6 +233,7 @@ __MODI32: ; 32bits signed division modulus call __DIVI32START jp __MODU32START pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" #line 62 "arch/zxnext/modi32c.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" ; Exchanges current DE HL with the diff --git a/tests/functional/arch/zxnext/modi8.asm b/tests/functional/arch/zxnext/modi8.asm index 390dfdbed..88c9e15b8 100644 --- a/tests/functional/arch/zxnext/modi8.asm +++ b/tests/functional/arch/zxnext/modi8.asm @@ -57,6 +57,13 @@ _b: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -121,5 +128,6 @@ __MODI8_FAST: ; __FASTCALL__ entry ld a, l ; remainder ret ; a = Modulus pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" #line 39 "arch/zxnext/modi8.bas" END diff --git a/tests/functional/arch/zxnext/modi8a.asm b/tests/functional/arch/zxnext/modi8a.asm index c8193f9c7..ee3b7f28a 100644 --- a/tests/functional/arch/zxnext/modi8a.asm +++ b/tests/functional/arch/zxnext/modi8a.asm @@ -35,6 +35,13 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -99,5 +106,6 @@ __MODI8_FAST: ; __FASTCALL__ entry ld a, l ; remainder ret ; a = Modulus pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" #line 19 "arch/zxnext/modi8a.bas" END diff --git a/tests/functional/arch/zxnext/modi8b.asm b/tests/functional/arch/zxnext/modi8b.asm index 9b682ac45..3455987b9 100644 --- a/tests/functional/arch/zxnext/modi8b.asm +++ b/tests/functional/arch/zxnext/modi8b.asm @@ -41,6 +41,13 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -105,5 +112,6 @@ __MODI8_FAST: ; __FASTCALL__ entry ld a, l ; remainder ret ; a = Modulus pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" #line 25 "arch/zxnext/modi8b.bas" END diff --git a/tests/functional/arch/zxnext/modu32c.asm b/tests/functional/arch/zxnext/modu32c.asm index f376e8117..28f8ecf51 100644 --- a/tests/functional/arch/zxnext/modu32c.asm +++ b/tests/functional/arch/zxnext/modu32c.asm @@ -88,6 +88,13 @@ _l: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" push namespace core __ABS32: @@ -113,7 +120,7 @@ __NEG32: ; Negates DEHL (Two's complement) inc de ret pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" ; --------------------------------------------------------- push namespace core __DIVU32: ; 32 bit unsigned division @@ -226,6 +233,7 @@ __MODI32: ; 32bits signed division modulus call __DIVI32START jp __MODU32START pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" #line 62 "arch/zxnext/modu32c.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" ; Exchanges current DE HL with the diff --git a/tests/functional/arch/zxnext/modu8.asm b/tests/functional/arch/zxnext/modu8.asm index acf6fd419..5527e0e55 100644 --- a/tests/functional/arch/zxnext/modu8.asm +++ b/tests/functional/arch/zxnext/modu8.asm @@ -57,6 +57,13 @@ _b: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -121,5 +128,6 @@ __MODI8_FAST: ; __FASTCALL__ entry ld a, l ; remainder ret ; a = Modulus pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" #line 39 "arch/zxnext/modu8.bas" END diff --git a/tests/functional/arch/zxnext/modu8a.asm b/tests/functional/arch/zxnext/modu8a.asm index 01cae54e7..c5c067e1d 100644 --- a/tests/functional/arch/zxnext/modu8a.asm +++ b/tests/functional/arch/zxnext/modu8a.asm @@ -35,6 +35,13 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -99,5 +106,6 @@ __MODI8_FAST: ; __FASTCALL__ entry ld a, l ; remainder ret ; a = Modulus pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" #line 19 "arch/zxnext/modu8a.bas" END diff --git a/tests/functional/arch/zxnext/modu8b.asm b/tests/functional/arch/zxnext/modu8b.asm index cc3112a80..d0f35a0e4 100644 --- a/tests/functional/arch/zxnext/modu8b.asm +++ b/tests/functional/arch/zxnext/modu8b.asm @@ -41,6 +41,13 @@ _a: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -105,5 +112,6 @@ __MODI8_FAST: ; __FASTCALL__ entry ld a, l ; remainder ret ; a = Modulus pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" #line 25 "arch/zxnext/modu8b.bas" END diff --git a/tests/functional/arch/zxnext/mul16.asm b/tests/functional/arch/zxnext/mul16.asm index 941f4d432..697065a93 100644 --- a/tests/functional/arch/zxnext/mul16.asm +++ b/tests/functional/arch/zxnext/mul16.asm @@ -44,7 +44,7 @@ _b: ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" push namespace core -__MUL16: ; Mutiplies HL with the last value stored into de stack +__MUL16: ; Multiplies HL with the last value stored into de stack ; Works for both signed and unsigned PROC ex de, hl diff --git a/tests/functional/arch/zxnext/mul16a.asm b/tests/functional/arch/zxnext/mul16a.asm index 805eb2686..c8087c4d1 100644 --- a/tests/functional/arch/zxnext/mul16a.asm +++ b/tests/functional/arch/zxnext/mul16a.asm @@ -37,7 +37,7 @@ _a: ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" push namespace core -__MUL16: ; Mutiplies HL with the last value stored into de stack +__MUL16: ; Multiplies HL with the last value stored into de stack ; Works for both signed and unsigned PROC ex de, hl diff --git a/tests/functional/arch/zxnext/mul16b.asm b/tests/functional/arch/zxnext/mul16b.asm index 1b7c5aea7..b6a51aebe 100644 --- a/tests/functional/arch/zxnext/mul16b.asm +++ b/tests/functional/arch/zxnext/mul16b.asm @@ -43,7 +43,7 @@ _a: ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" push namespace core -__MUL16: ; Mutiplies HL with the last value stored into de stack +__MUL16: ; Multiplies HL with the last value stored into de stack ; Works for both signed and unsigned PROC ex de, hl diff --git a/tests/functional/arch/zxnext/mul16c.asm b/tests/functional/arch/zxnext/mul16c.asm index 3cb09d496..00b5b1bcb 100644 --- a/tests/functional/arch/zxnext/mul16c.asm +++ b/tests/functional/arch/zxnext/mul16c.asm @@ -34,7 +34,7 @@ _a: ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" push namespace core -__MUL16: ; Mutiplies HL with the last value stored into de stack +__MUL16: ; Multiplies HL with the last value stored into de stack ; Works for both signed and unsigned PROC ex de, hl diff --git a/tests/functional/arch/zxnext/mul32.asm b/tests/functional/arch/zxnext/mul32.asm new file mode 100644 index 000000000..b73ceca01 --- /dev/null +++ b/tests/functional/arch/zxnext/mul32.asm @@ -0,0 +1,74 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +_b: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld (_b), hl + ld hl, (_a) + ld (_b), hl + ld hl, 0 + ld (_b), hl + ld hl, (_a) + ld (_b), hl + ld de, (_a) + ld hl, (_a) + call .core.__MUL16_FAST + ld (_b), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Multiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld a,d ; a = xh + ld d,h ; d = yh + ld h,a ; h = xh + ld c,e ; c = xl + ld b,l ; b = yl + mul d,e ; yh * yl + ex de,hl + mul d,e ; xh * yl + add hl,de ; add cross products + ld e,c + ld d,b + mul d,e ; yl * xl + ld a,l ; cross products lsb + add a,d ; add to msb final + ld h,a + ld l,e ; hl = final + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 25 "arch/zxnext/mul32.bas" + END diff --git a/tests/functional/arch/zxnext/mul32.bas b/tests/functional/arch/zxnext/mul32.bas new file mode 100644 index 000000000..8cdb72ac1 --- /dev/null +++ b/tests/functional/arch/zxnext/mul32.bas @@ -0,0 +1,10 @@ +' TEST for ADD16 + +DIM a as UInteger +DIM b as UInteger + +b = a * 0 +b = a * 1 +b = 0 * a +b = 1 * a +b = a * a diff --git a/tests/functional/arch/zxnext/mul32a.asm b/tests/functional/arch/zxnext/mul32a.asm new file mode 100644 index 000000000..fcec6d54c --- /dev/null +++ b/tests/functional/arch/zxnext/mul32a.asm @@ -0,0 +1,67 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld de, (_a) + ld hl, (_a) + call .core.__MUL16_FAST + ex de, hl + ld hl, (_a) + call .core.__MUL16_FAST + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Multiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld a,d ; a = xh + ld d,h ; d = yh + ld h,a ; h = xh + ld c,e ; c = xl + ld b,l ; b = yl + mul d,e ; yh * yl + ex de,hl + mul d,e ; xh * yl + add hl,de ; add cross products + ld e,c + ld d,b + mul d,e ; yl * xl + ld a,l ; cross products lsb + add a,d ; add to msb final + ld h,a + ld l,e ; hl = final + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 20 "arch/zxnext/mul32a.bas" + END diff --git a/tests/functional/arch/zxnext/mul32a.bas b/tests/functional/arch/zxnext/mul32a.bas new file mode 100644 index 000000000..15611ed0b --- /dev/null +++ b/tests/functional/arch/zxnext/mul32a.bas @@ -0,0 +1,4 @@ + +REM another MUL16 test +DIM a As Integer +a = a * a * a diff --git a/tests/functional/arch/zxnext/mul32b.asm b/tests/functional/arch/zxnext/mul32b.asm new file mode 100644 index 000000000..47d65adbc --- /dev/null +++ b/tests/functional/arch/zxnext/mul32b.asm @@ -0,0 +1,73 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld de, (_a) + ld hl, (_a) + call .core.__MUL16_FAST + push hl + ld hl, (_a) + add hl, hl + ex de, hl + ld hl, (_a) + call .core.__MUL16_FAST + ex de, hl + pop hl + call .core.__MUL16_FAST + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Multiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld a,d ; a = xh + ld d,h ; d = yh + ld h,a ; h = xh + ld c,e ; c = xl + ld b,l ; b = yl + mul d,e ; yh * yl + ex de,hl + mul d,e ; xh * yl + add hl,de ; add cross products + ld e,c + ld d,b + mul d,e ; yl * xl + ld a,l ; cross products lsb + add a,d ; add to msb final + ld h,a + ld l,e ; hl = final + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 26 "arch/zxnext/mul32b.bas" + END diff --git a/tests/functional/arch/zxnext/mul32b.bas b/tests/functional/arch/zxnext/mul32b.bas new file mode 100644 index 000000000..1d849dff4 --- /dev/null +++ b/tests/functional/arch/zxnext/mul32b.bas @@ -0,0 +1,4 @@ + +REM another ADD16 tests +DIM a As Integer +a = a * a * (a * 2 * a) diff --git a/tests/functional/arch/zxnext/mul32c.asm b/tests/functional/arch/zxnext/mul32c.asm new file mode 100644 index 000000000..934db8338 --- /dev/null +++ b/tests/functional/arch/zxnext/mul32c.asm @@ -0,0 +1,64 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, -1 + call .core.__MUL16_FAST + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Multiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld a,d ; a = xh + ld d,h ; d = yh + ld h,a ; h = xh + ld c,e ; c = xl + ld b,l ; b = yl + mul d,e ; yh * yl + ex de,hl + mul d,e ; xh * yl + add hl,de ; add cross products + ld e,c + ld d,b + mul d,e ; yl * xl + ld a,l ; cross products lsb + add a,d ; add to msb final + ld h,a + ld l,e ; hl = final + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 17 "arch/zxnext/mul32c.bas" + END diff --git a/tests/functional/arch/zxnext/mul32c.bas b/tests/functional/arch/zxnext/mul32c.bas new file mode 100644 index 000000000..6462127b8 --- /dev/null +++ b/tests/functional/arch/zxnext/mul32c.bas @@ -0,0 +1,3 @@ +DIM a as Integer + +a = -1 * a diff --git a/tests/functional/arch/zxnext/mulf00.asm b/tests/functional/arch/zxnext/mulf00.asm index dd3cc7166..f575707be 100644 --- a/tests/functional/arch/zxnext/mulf00.asm +++ b/tests/functional/arch/zxnext/mulf00.asm @@ -47,6 +47,13 @@ _b: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC @@ -86,7 +93,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -104,6 +111,7 @@ __MULF: ; Multiplication defb 38h; ; END CALC jp __FPSTACK_POP pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf.asm" #line 21 "arch/zxnext/mulf00.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/pushf.asm" ; Routine to push Float pointed by HL diff --git a/tests/functional/arch/zxnext/mulf01.asm b/tests/functional/arch/zxnext/mulf01.asm index f10d744e4..d2f80a325 100644 --- a/tests/functional/arch/zxnext/mulf01.asm +++ b/tests/functional/arch/zxnext/mulf01.asm @@ -51,6 +51,13 @@ _b: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC @@ -90,7 +97,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -108,6 +115,7 @@ __MULF: ; Multiplication defb 38h; ; END CALC jp __FPSTACK_POP pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf.asm" #line 25 "arch/zxnext/mulf01.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storef.asm" push namespace core diff --git a/tests/functional/arch/zxnext/mulf16.asm b/tests/functional/arch/zxnext/mulf16.asm index af0617336..73797e05d 100644 --- a/tests/functional/arch/zxnext/mulf16.asm +++ b/tests/functional/arch/zxnext/mulf16.asm @@ -62,6 +62,13 @@ _b: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf16.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" push namespace core __ABS32: @@ -87,8 +94,15 @@ __NEG32: ; Negates DEHL (Two's complement) inc de ret pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf16.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/_mul32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/_mul32.asm" ; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 ; Used with permission. ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') @@ -147,7 +161,8 @@ __LMULSTART: djnz __LMULLOOP ret ; result in h'l'hlb'c'ac pop namespace -#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/_mul32.asm" +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf16.asm" push namespace core __MULF16: ; ld a, d ; load sgn into a @@ -182,5 +197,6 @@ __ROUND_FIX: ; rounds a 64bit (32.32) fixed point number to 16.16 jp m, __NEG32 ; if negative, negates it ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" #line 44 "arch/zxnext/mulf16.bas" END diff --git a/tests/functional/arch/zxnext/mulf16a.asm b/tests/functional/arch/zxnext/mulf16a.asm index a03907b20..55ca31cba 100644 --- a/tests/functional/arch/zxnext/mulf16a.asm +++ b/tests/functional/arch/zxnext/mulf16a.asm @@ -46,6 +46,13 @@ _b: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf16.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" push namespace core __ABS32: @@ -71,8 +78,15 @@ __NEG32: ; Negates DEHL (Two's complement) inc de ret pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf16.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/_mul32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/_mul32.asm" ; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 ; Used with permission. ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') @@ -131,7 +145,8 @@ __LMULSTART: djnz __LMULLOOP ret ; result in h'l'hlb'c'ac pop namespace -#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/_mul32.asm" +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf16.asm" push namespace core __MULF16: ; ld a, d ; load sgn into a @@ -166,5 +181,6 @@ __ROUND_FIX: ; rounds a 64bit (32.32) fixed point number to 16.16 jp m, __NEG32 ; if negative, negates it ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" #line 22 "arch/zxnext/mulf16a.bas" END diff --git a/tests/functional/arch/zxnext/mulu16.asm b/tests/functional/arch/zxnext/mulu16.asm index 7fa21b93f..b1a1b7c51 100644 --- a/tests/functional/arch/zxnext/mulu16.asm +++ b/tests/functional/arch/zxnext/mulu16.asm @@ -34,7 +34,7 @@ _a: ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" push namespace core -__MUL16: ; Mutiplies HL with the last value stored into de stack +__MUL16: ; Multiplies HL with the last value stored into de stack ; Works for both signed and unsigned PROC ex de, hl diff --git a/tests/functional/arch/zxnext/opt4_mul8.asm b/tests/functional/arch/zxnext/opt4_mul8.asm index a698591de..78df9053f 100644 --- a/tests/functional/arch/zxnext/opt4_mul8.asm +++ b/tests/functional/arch/zxnext/opt4_mul8.asm @@ -55,7 +55,7 @@ _printTest__leave: ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" push namespace core -__MUL16: ; Mutiplies HL with the last value stored into de stack +__MUL16: ; Multiplies HL with the last value stored into de stack ; Works for both signed and unsigned PROC ex de, hl diff --git a/tests/functional/arch/zxnext/print_at2.asm b/tests/functional/arch/zxnext/print_at2.asm new file mode 100644 index 000000000..3cb99fc78 --- /dev/null +++ b/tests/functional/arch/zxnext/print_at2.asm @@ -0,0 +1,955 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_row: + DEFB 00 +_col: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call .core.COPY_ATTR + ld a, (_row) + add a, 2 + push af + ld a, (_col) + inc a + call .core.PRINT_AT + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +; vim:ts=4:sw=4:et: + ; PRINT command routine + ; Does not print attribute. Use PRINT_STR or PRINT_NUM for that +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/in_screen.asm" + push namespace core +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits + PROC + LOCAL __IN_SCREEN_ERR + ld hl, SCR_SIZE + ld a, e + cp l + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld a, d + cp h + ret c ; Return if carry (OK) +__IN_SCREEN_ERR: +__OUT_OF_SCREEN_ERR: + ; Jumps here if out of screen + ld a, ERROR_OutOfScreen + jp __STOP ; Saves error code and exits + ENDP + pop namespace +#line 7 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + push namespace core +__ATTR_ADDR: + ; calc start address in DE (as (32 * d) + e) + ; Contributed by Santiago Romero at http://www.speccy.org + ld h, 0 ; 7 T-States + ld a, d ; 4 T-States + ld d, h + add a, a ; a * 2 ; 4 T-States + add a, a ; a * 4 ; 4 T-States + ld l, a ; HL = A * 4 ; 4 T-States + add hl, hl ; HL = A * 8 ; 15 T-States + add hl, hl ; HL = A * 16 ; 15 T-States + add hl, hl ; HL = A * 32 ; 15 T-States + add hl, de + ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address + add hl, de + ; Return current screen address in HL + ret + ; Sets the attribute at a given screen coordinate (D, E). + ; The attribute is taken from the ATTR_T memory variable + ; Used by PRINT routines +SET_ATTR: + ; Checks for valid coords + call __IN_SCREEN + ret nc + call __ATTR_ADDR +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + ; HL contains the address of the ATTR cell to set + PROC +__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + ld a, d + and (hl) + ld c, a ; C = current screen color, masked + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + ret + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" + ; Printing positioning library. + push namespace core + ; Loads into DE current ROW, COL print position from S_POSN mem var. +__LOAD_S_POSN: + PROC + ld de, (S_POSN) + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + ret + ENDP + ; Saves ROW, COL from DE into S_POSN mem var. +__SAVE_S_POSN: + PROC + ld hl, SCR_SIZE + or a + sbc hl, de + ld (S_POSN), hl ; saves it again +__SET_SCR_PTR: ;; Fast + push de + call __ATTR_ADDR + ld (DFCCL), hl + pop de + ld a, d + ld c, a ; Saves it for later + and 0F8h ; Masks 3 lower bit ; zy + ld d, a + ld a, c ; Recovers it + and 07h ; MOD 7 ; y1 + rrca + rrca + rrca + or e + ld e, a + ld hl, (SCREEN_ADDR) + add hl, de ; HL = Screen address + DE + ld (DFCC), hl + ret + ENDP + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/table_jump.asm" + push namespace core +JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A + add a, a +JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE + ld e, a + ld d, 0 +JUMP_HL_PLUS_DE: ; Does JP (HL + DE) + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl +CALL_HL: + jp (hl) + pop namespace +#line 8 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/ink.asm" + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 + ld de, ATTR_P +__SET_INK: + cp 8 + jr nz, __SET_INK2 + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + or 7 ; Set bits 0,1,2 to enable transparency + ld (de), a + ret +__SET_INK2: + ; Another entry. This will set the ink color at location pointer by DE + and 7 ; # Gets color mod 8 + ld b, a ; Saves the color + ld a, (de) + and 0F8h ; Clears previous value + or b + ld (de), a + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + and 0F8h ; Reset bits 0,1,2 sign to disable transparency + ld (de), a ; Store new attr + ret + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + ld de, ATTR_P +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret + ; Another entry. This will set the paper color at location pointer by DE +__SET_PAPER2: + and 7 ; # Remove + rlca + rlca + rlca ; a *= 8 + ld b, a ; Saves the color + ld a, (de) + and 0C7h ; Clears previous value + or b + ld (de), a + inc de ; Points to MASK_T or MASK_P accordingly + ld a, (de) + and 0C7h ; Resets bits 3,4,5 + ld (de), a + ret + ; Sets the PAPER color passed in A register in the ATTR_T variable +PAPER_TMP: + ld de, ATTR_T + jp __SET_PAPER + ENDP + pop namespace +#line 10 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/flash.asm" + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +FLASH: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x80 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 07Fh ; Clears previous value + or b + ld (hl), a + inc hl + res 7, (hl) ;Reset bit 7 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 7, (hl) ;Set bit 7 to enable transparency + ret + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld hl, ATTR_T + jr __SET_FLASH + ENDP + pop namespace +#line 11 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bright.asm" + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +BRIGHT: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x40 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 0BFh ; Clears previous value + or b + ld (hl), a + inc hl + res 6, (hl) ;Reset bit 6 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 6, (hl) ;Set bit 6 to enable transparency + ret + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld hl, ATTR_T + jr __SET_BRIGHT + ENDP + pop namespace +#line 12 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/over.asm" + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register + push namespace core +OVER: + PROC + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + ret + ; Sets OVER flag in P_FLAG temporarily +OVER_TMP: + ld c, a ; saves it for later + and 2 ; gets bit 1; clears carry + rra + ld hl, FLAGS2 + res 0, (hl) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + push namespace core +INVERSE: + PROC + and 1 ; # Convert to 0/1 + add a, a; # Shift left 3 bits for permanent + add a, a + add a, a + ld hl, P_FLAG + res 3, (hl) + or (hl) + ld (hl), a + ret + ; Sets INVERSE flag in P_FLAG temporarily +INVERSE_TMP: + and 1 + add a, a + add a, a; # Shift left 2 bits for temporary + ld hl, P_FLAG + res 2, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bold.asm" + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + push namespace core +BOLD: + PROC + and 1 + rlca + rlca + rlca + ld hl, FLAGS2 + res 3, (HL) + or (hl) + ld (hl), a + ret + ; Sets BOLD flag in P_FLAG temporarily +BOLD_TMP: + and 1 + rlca + rlca + ld hl, FLAGS2 + res 2, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 15 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/italic.asm" + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + push namespace core +ITALIC: + PROC + and 1 + rrca + rrca + rrca + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + ret + ; Sets ITALIC flag in P_FLAG temporarily +ITALIC_TMP: + and 1 + rrca + rrca + rrca + rrca + ld hl, FLAGS2 + res 4, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 16 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + push namespace core +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + ;; Clears ATTR2 flags (OVER 2, etc) + xor a + ld (FLAGS2), a + ld hl, TV_FLAG + res 0, (hl) + LOCAL SET_SCR_ADDR + call __LOAD_S_POSN + jp __SET_SCR_PTR + ;; Receives HL = future value of S_POSN + ;; Stores it at (S_POSN) and refresh screen pointers (ATTR, SCR) +SET_SCR_ADDR: + ld (S_POSN), hl + ex de, hl + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + dec e + jp __SET_SCR_PTR +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A + LOCAL PO_GR_1 + LOCAL __PRCHAR + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + PRINT_JUMP_STATE EQU __PRINT_JUMP + 2 +__PRINT_JUMP: + exx ; Switch to alternative registers + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively +__PRINT_START: +__PRINT_CHR: + cp ' ' + jr c, __PRINT_SPECIAL ; Characters below ' ' are special ones + ex af, af' ; Saves a value (char to print) for later + ld hl, (S_POSN) + dec l + jr nz, 1f + ld l, SCR_COLS - 1 + dec h + jr nz, 2f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 94 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +2: + call SET_SCR_ADDR + jr 4f +1: + ld (S_POSN), hl +4: + ex af, af' + cp 80h ; Is it a "normal" (printable) char + jr c, __SRCADDR + cp 90h ; Is it an UDG? + jr nc, __PRINT_UDG + ; Print an 8 bit pattern (80h to 8Fh) + ld b, a + call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 + ld hl, MEM0 + jp __PRGRAPH + PO_GR_1 EQU 0B38h +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jr __PRGRAPH0 + __SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source +__SRCADDR: + ld bc, (CHARS) +__PRGRAPH0: + add a, a ; A = a * 2 (since a < 80h) ; Thanks to Metalbrain at http://foro.speccy.org + ld l, a + ld h, 0 ; HL = a * 2 (accumulator) + add hl, hl + add hl, hl ; HL = a * 8 + add hl, bc ; HL = CHARS address +__PRGRAPH: + ex de, hl ; HL = Write Address, DE = CHARS address + bit 2, (iy + $47) + call nz, __BOLD +#line 141 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + bit 4, (iy + $47) + call nz, __ITALIC +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ld hl, (DFCC) + push hl + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be source, and HL destiny +PRINT_MODE: ; Which operation is used to write on the screen + ; Set it with: + ; LD A, + ; LD (PRINT_MODE), A + ; + ; Available operations: + ; NORMAL : 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; Set to one of the values above +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + ld (hl), a + inc de + inc h ; Next line + djnz __PRCHAR + pop hl + inc hl + ld (DFCC), hl + ld hl, (DFCCL) ; current ATTR Pos + inc hl + ld (DFCCL), hl + dec hl + call __SET_ATTR + exx + ret + ; ------------- SPECIAL CHARS (< 32) ----------------- +__PRINT_SPECIAL: ; Jumps here if it is a special char + ld hl, __PRINT_TABLE + jp JUMP_HL_PLUS_2A +PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence + exx +__PRINT_0Dh: ; Called WHEN printing CHR$(13) + ld hl, (S_POSN) + dec l + jr nz, 1f + dec h + jr nz, 1f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 211 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +1: + ld l, 1 +__PRINT_EOL_END: + call SET_SCR_ADDR + exx + ret +__PRINT_COM: + exx + push hl + push de + push bc + call PRINT_COMMA + pop bc + pop de + pop hl + ret +__PRINT_TAB: + ld hl, __PRINT_TAB1 + jr __PRINT_SET_STATE +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + jr __PRINT_SET_STATE +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + exx + push hl + push bc + push de + call PRINT_TAB + pop de + pop bc + pop hl + ret +__PRINT_AT: + ld hl, __PRINT_AT1 + jr __PRINT_SET_STATE +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + ld hl, (S_POSN) + ld h, a + ld a, SCR_ROWS + sub h + ld (S_POSN + 1), a + ld hl, __PRINT_AT2 + jr __PRINT_SET_STATE +__PRINT_AT2: + call __LOAD_S_POSN + ld e, a + call __SAVE_S_POSN + jr __PRINT_RESTART +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jr nz, 3f + ld e, SCR_COLS - 2 + dec d + cp d + jr nz, 3f + ld d, SCR_ROWS - 1 +3: + call __SAVE_S_POSN + exx + ret +__PRINT_INK: + ld hl, __PRINT_INK2 + jr __PRINT_SET_STATE +__PRINT_INK2: + call INK_TMP + jr __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jr __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jr __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jr __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jr __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jr __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 355 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 365 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __BOLD +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +1: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz 1b + pop hl + ld de, MEM0 + ret +#line 386 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __ITALIC +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) + inc hl + srl (hl) + inc hl + srl (hl) + inc hl + inc hl + inc hl + sla (hl) + inc hl + sla (hl) + inc hl + sla (hl) + pop hl + ld de, MEM0 + ret +#line 414 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __SCROLL_SCR +#line 488 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + __SCROLL_SCR EQU 0DFEh ; Use ROM SCROLL +#line 490 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 491 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 +PRINT_TAB: + ; Tabulates the number of spaces in A register + ; If the current cursor position is already A, does nothing + PROC + LOCAL LOOP + call __LOAD_S_POSN ; e = current row + sub e + and 31 + ret z + ld b, a +LOOP: + ld a, ' ' + call __PRINTCHAR + djnz LOOP + ret + ENDP +PRINT_AT: ; Changes cursor to ROW, COL + ; COL in A register + ; ROW in stack + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl + call __IN_SCREEN + ret nc ; Return if out of screen + jp __SAVE_S_POSN + LOCAL __PRINT_COM + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_BOLD + LOCAL __PRINT_ITA + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + LOCAL __PRINT_ITA2 +#line 547 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 553 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_TABLE: ; Jump table for 0 .. 22 codes + DW __PRINT_NOP ; 0 + DW __PRINT_NOP ; 1 + DW __PRINT_NOP ; 2 + DW __PRINT_NOP ; 3 + DW __PRINT_NOP ; 4 + DW __PRINT_NOP ; 5 + DW __PRINT_COM ; 6 COMMA + DW __PRINT_NOP ; 7 + DW __PRINT_DEL ; 8 DEL + DW __PRINT_NOP ; 9 + DW __PRINT_NOP ; 10 + DW __PRINT_NOP ; 11 + DW __PRINT_NOP ; 12 + DW __PRINT_0Dh ; 13 + DW __PRINT_BOLD ; 14 + DW __PRINT_ITA ; 15 + DW __PRINT_INK ; 16 + DW __PRINT_PAP ; 17 + DW __PRINT_FLA ; 18 + DW __PRINT_BRI ; 19 + DW __PRINT_INV ; 20 + DW __PRINT_OVR ; 21 + DW __PRINT_AT ; 22 AT + DW __PRINT_TAB ; 23 TAB + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" + push namespace core +COPY_ATTR: + ; Just copies current permanent attribs into temporal attribs + ; and sets print mode + PROC + LOCAL INVERSE1 + LOCAL __REFRESH_TMP + INVERSE1 EQU 02Fh + ld hl, (ATTR_P) + ld (ATTR_T), hl + ld hl, FLAGS2 + call __REFRESH_TMP + ld hl, P_FLAG + call __REFRESH_TMP +__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) + LOCAL TABLE + LOCAL CONT2 + rra ; Over bit to carry + ld a, (FLAGS2) + rla ; Over bit in bit 1, Over2 bit in bit 2 + and 3 ; Only bit 0 and 1 (OVER flag) + ld c, a + ld b, 0 + ld hl, TABLE + add hl, bc + ld a, (hl) + ld (PRINT_MODE), a + ld hl, (P_FLAG) + xor a ; NOP -> INVERSE0 + bit 2, l + jr z, CONT2 + ld a, INVERSE1 ; CPL -> INVERSE1 +CONT2: + ld (INVERSE_MODE), a + ret +TABLE: + nop ; NORMAL MODE + xor (hl) ; OVER 1 MODE + and (hl) ; OVER 2 MODE + or (hl) ; OVER 3 MODE +#line 67 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +__REFRESH_TMP: + ld a, (hl) + and 0b10101010 + ld c, a + rra + or c + ld (hl), a + ret + ENDP + pop namespace +#line 20 "arch/zxnext/print_at2.bas" + END diff --git a/tests/functional/arch/zxnext/print_at2.bas b/tests/functional/arch/zxnext/print_at2.bas new file mode 100644 index 000000000..f2b3d0c14 --- /dev/null +++ b/tests/functional/arch/zxnext/print_at2.bas @@ -0,0 +1,3 @@ +DIM row, col as UByte +PRINT AT row + 2, col + 1; + diff --git a/tests/functional/arch/zxnext/print_i16.asm b/tests/functional/arch/zxnext/print_i16.asm index ee0bfbb6d..570b3e2a3 100644 --- a/tests/functional/arch/zxnext/print_i16.asm +++ b/tests/functional/arch/zxnext/print_i16.asm @@ -972,6 +972,13 @@ __PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER must preserve registers pop namespace #line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printi16.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" ; 16 bit division and modulo functions ; for both signed and unsigned values #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg16.asm" @@ -990,7 +997,7 @@ __NEGHL: inc hl ret pop namespace -#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" +#line 5 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" push namespace core __DIVU16: ; 16 bit unsigned division ; HL = Dividend, Stack Top = Divisor @@ -1063,6 +1070,7 @@ __MODI16: ; 16 bit modulus ; de = quotient ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" #line 3 "/zxbasic/src/lib/arch/zxnext/runtime/printi16.asm" push namespace core __PRINTI16: ; Prints a 16bits signed in HL diff --git a/tests/functional/arch/zxnext/print_i32.asm b/tests/functional/arch/zxnext/print_i32.asm index efc701d22..24954cfe5 100644 --- a/tests/functional/arch/zxnext/print_i32.asm +++ b/tests/functional/arch/zxnext/print_i32.asm @@ -999,6 +999,13 @@ __NEG32: ; Negates DEHL (Two's complement) pop namespace #line 3 "/zxbasic/src/lib/arch/zxnext/runtime/printi32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" ; --------------------------------------------------------- push namespace core __DIVU32: ; 32 bit unsigned division @@ -1111,6 +1118,7 @@ __MODI32: ; 32bits signed division modulus call __DIVI32START jp __MODU32START pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" #line 4 "/zxbasic/src/lib/arch/zxnext/runtime/printi32.asm" push namespace core __PRINTI32: diff --git a/tests/functional/arch/zxnext/print_i8.asm b/tests/functional/arch/zxnext/print_i8.asm index ca2101efd..9a3d90cb1 100644 --- a/tests/functional/arch/zxnext/print_i8.asm +++ b/tests/functional/arch/zxnext/print_i8.asm @@ -972,6 +972,13 @@ __PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER must preserve registers pop namespace #line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printi8.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -1036,6 +1043,7 @@ __MODI8_FAST: ; __FASTCALL__ entry ld a, l ; remainder ret ; a = Modulus pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" #line 3 "/zxbasic/src/lib/arch/zxnext/runtime/printi8.asm" push namespace core __PRINTI8: ; Prints an 8 bits number in Accumulator (A) diff --git a/tests/functional/arch/zxnext/print_tab2.asm b/tests/functional/arch/zxnext/print_tab2.asm new file mode 100644 index 000000000..0d5840c84 --- /dev/null +++ b/tests/functional/arch/zxnext/print_tab2.asm @@ -0,0 +1,950 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_i: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call .core.COPY_ATTR + ld a, (_i) + add a, 2 + call .core.PRINT_TAB + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +; vim:ts=4:sw=4:et: + ; PRINT command routine + ; Does not print attribute. Use PRINT_STR or PRINT_NUM for that +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/in_screen.asm" + push namespace core +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits + PROC + LOCAL __IN_SCREEN_ERR + ld hl, SCR_SIZE + ld a, e + cp l + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld a, d + cp h + ret c ; Return if carry (OK) +__IN_SCREEN_ERR: +__OUT_OF_SCREEN_ERR: + ; Jumps here if out of screen + ld a, ERROR_OutOfScreen + jp __STOP ; Saves error code and exits + ENDP + pop namespace +#line 7 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + push namespace core +__ATTR_ADDR: + ; calc start address in DE (as (32 * d) + e) + ; Contributed by Santiago Romero at http://www.speccy.org + ld h, 0 ; 7 T-States + ld a, d ; 4 T-States + ld d, h + add a, a ; a * 2 ; 4 T-States + add a, a ; a * 4 ; 4 T-States + ld l, a ; HL = A * 4 ; 4 T-States + add hl, hl ; HL = A * 8 ; 15 T-States + add hl, hl ; HL = A * 16 ; 15 T-States + add hl, hl ; HL = A * 32 ; 15 T-States + add hl, de + ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address + add hl, de + ; Return current screen address in HL + ret + ; Sets the attribute at a given screen coordinate (D, E). + ; The attribute is taken from the ATTR_T memory variable + ; Used by PRINT routines +SET_ATTR: + ; Checks for valid coords + call __IN_SCREEN + ret nc + call __ATTR_ADDR +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + ; HL contains the address of the ATTR cell to set + PROC +__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + ld a, d + and (hl) + ld c, a ; C = current screen color, masked + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + ret + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" + ; Printing positioning library. + push namespace core + ; Loads into DE current ROW, COL print position from S_POSN mem var. +__LOAD_S_POSN: + PROC + ld de, (S_POSN) + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + ret + ENDP + ; Saves ROW, COL from DE into S_POSN mem var. +__SAVE_S_POSN: + PROC + ld hl, SCR_SIZE + or a + sbc hl, de + ld (S_POSN), hl ; saves it again +__SET_SCR_PTR: ;; Fast + push de + call __ATTR_ADDR + ld (DFCCL), hl + pop de + ld a, d + ld c, a ; Saves it for later + and 0F8h ; Masks 3 lower bit ; zy + ld d, a + ld a, c ; Recovers it + and 07h ; MOD 7 ; y1 + rrca + rrca + rrca + or e + ld e, a + ld hl, (SCREEN_ADDR) + add hl, de ; HL = Screen address + DE + ld (DFCC), hl + ret + ENDP + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/table_jump.asm" + push namespace core +JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A + add a, a +JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE + ld e, a + ld d, 0 +JUMP_HL_PLUS_DE: ; Does JP (HL + DE) + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl +CALL_HL: + jp (hl) + pop namespace +#line 8 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/ink.asm" + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 + ld de, ATTR_P +__SET_INK: + cp 8 + jr nz, __SET_INK2 + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + or 7 ; Set bits 0,1,2 to enable transparency + ld (de), a + ret +__SET_INK2: + ; Another entry. This will set the ink color at location pointer by DE + and 7 ; # Gets color mod 8 + ld b, a ; Saves the color + ld a, (de) + and 0F8h ; Clears previous value + or b + ld (de), a + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + and 0F8h ; Reset bits 0,1,2 sign to disable transparency + ld (de), a ; Store new attr + ret + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + ld de, ATTR_P +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret + ; Another entry. This will set the paper color at location pointer by DE +__SET_PAPER2: + and 7 ; # Remove + rlca + rlca + rlca ; a *= 8 + ld b, a ; Saves the color + ld a, (de) + and 0C7h ; Clears previous value + or b + ld (de), a + inc de ; Points to MASK_T or MASK_P accordingly + ld a, (de) + and 0C7h ; Resets bits 3,4,5 + ld (de), a + ret + ; Sets the PAPER color passed in A register in the ATTR_T variable +PAPER_TMP: + ld de, ATTR_T + jp __SET_PAPER + ENDP + pop namespace +#line 10 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/flash.asm" + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +FLASH: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x80 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 07Fh ; Clears previous value + or b + ld (hl), a + inc hl + res 7, (hl) ;Reset bit 7 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 7, (hl) ;Set bit 7 to enable transparency + ret + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld hl, ATTR_T + jr __SET_FLASH + ENDP + pop namespace +#line 11 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bright.asm" + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +BRIGHT: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x40 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 0BFh ; Clears previous value + or b + ld (hl), a + inc hl + res 6, (hl) ;Reset bit 6 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 6, (hl) ;Set bit 6 to enable transparency + ret + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld hl, ATTR_T + jr __SET_BRIGHT + ENDP + pop namespace +#line 12 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/over.asm" + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register + push namespace core +OVER: + PROC + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + ret + ; Sets OVER flag in P_FLAG temporarily +OVER_TMP: + ld c, a ; saves it for later + and 2 ; gets bit 1; clears carry + rra + ld hl, FLAGS2 + res 0, (hl) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + push namespace core +INVERSE: + PROC + and 1 ; # Convert to 0/1 + add a, a; # Shift left 3 bits for permanent + add a, a + add a, a + ld hl, P_FLAG + res 3, (hl) + or (hl) + ld (hl), a + ret + ; Sets INVERSE flag in P_FLAG temporarily +INVERSE_TMP: + and 1 + add a, a + add a, a; # Shift left 2 bits for temporary + ld hl, P_FLAG + res 2, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bold.asm" + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + push namespace core +BOLD: + PROC + and 1 + rlca + rlca + rlca + ld hl, FLAGS2 + res 3, (HL) + or (hl) + ld (hl), a + ret + ; Sets BOLD flag in P_FLAG temporarily +BOLD_TMP: + and 1 + rlca + rlca + ld hl, FLAGS2 + res 2, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 15 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/italic.asm" + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + push namespace core +ITALIC: + PROC + and 1 + rrca + rrca + rrca + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + ret + ; Sets ITALIC flag in P_FLAG temporarily +ITALIC_TMP: + and 1 + rrca + rrca + rrca + rrca + ld hl, FLAGS2 + res 4, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 16 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + push namespace core +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + ;; Clears ATTR2 flags (OVER 2, etc) + xor a + ld (FLAGS2), a + ld hl, TV_FLAG + res 0, (hl) + LOCAL SET_SCR_ADDR + call __LOAD_S_POSN + jp __SET_SCR_PTR + ;; Receives HL = future value of S_POSN + ;; Stores it at (S_POSN) and refresh screen pointers (ATTR, SCR) +SET_SCR_ADDR: + ld (S_POSN), hl + ex de, hl + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + dec e + jp __SET_SCR_PTR +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A + LOCAL PO_GR_1 + LOCAL __PRCHAR + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + PRINT_JUMP_STATE EQU __PRINT_JUMP + 2 +__PRINT_JUMP: + exx ; Switch to alternative registers + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively +__PRINT_START: +__PRINT_CHR: + cp ' ' + jr c, __PRINT_SPECIAL ; Characters below ' ' are special ones + ex af, af' ; Saves a value (char to print) for later + ld hl, (S_POSN) + dec l + jr nz, 1f + ld l, SCR_COLS - 1 + dec h + jr nz, 2f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 94 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +2: + call SET_SCR_ADDR + jr 4f +1: + ld (S_POSN), hl +4: + ex af, af' + cp 80h ; Is it a "normal" (printable) char + jr c, __SRCADDR + cp 90h ; Is it an UDG? + jr nc, __PRINT_UDG + ; Print an 8 bit pattern (80h to 8Fh) + ld b, a + call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 + ld hl, MEM0 + jp __PRGRAPH + PO_GR_1 EQU 0B38h +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jr __PRGRAPH0 + __SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source +__SRCADDR: + ld bc, (CHARS) +__PRGRAPH0: + add a, a ; A = a * 2 (since a < 80h) ; Thanks to Metalbrain at http://foro.speccy.org + ld l, a + ld h, 0 ; HL = a * 2 (accumulator) + add hl, hl + add hl, hl ; HL = a * 8 + add hl, bc ; HL = CHARS address +__PRGRAPH: + ex de, hl ; HL = Write Address, DE = CHARS address + bit 2, (iy + $47) + call nz, __BOLD +#line 141 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + bit 4, (iy + $47) + call nz, __ITALIC +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ld hl, (DFCC) + push hl + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be source, and HL destiny +PRINT_MODE: ; Which operation is used to write on the screen + ; Set it with: + ; LD A, + ; LD (PRINT_MODE), A + ; + ; Available operations: + ; NORMAL : 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; Set to one of the values above +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + ld (hl), a + inc de + inc h ; Next line + djnz __PRCHAR + pop hl + inc hl + ld (DFCC), hl + ld hl, (DFCCL) ; current ATTR Pos + inc hl + ld (DFCCL), hl + dec hl + call __SET_ATTR + exx + ret + ; ------------- SPECIAL CHARS (< 32) ----------------- +__PRINT_SPECIAL: ; Jumps here if it is a special char + ld hl, __PRINT_TABLE + jp JUMP_HL_PLUS_2A +PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence + exx +__PRINT_0Dh: ; Called WHEN printing CHR$(13) + ld hl, (S_POSN) + dec l + jr nz, 1f + dec h + jr nz, 1f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 211 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +1: + ld l, 1 +__PRINT_EOL_END: + call SET_SCR_ADDR + exx + ret +__PRINT_COM: + exx + push hl + push de + push bc + call PRINT_COMMA + pop bc + pop de + pop hl + ret +__PRINT_TAB: + ld hl, __PRINT_TAB1 + jr __PRINT_SET_STATE +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + jr __PRINT_SET_STATE +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + exx + push hl + push bc + push de + call PRINT_TAB + pop de + pop bc + pop hl + ret +__PRINT_AT: + ld hl, __PRINT_AT1 + jr __PRINT_SET_STATE +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + ld hl, (S_POSN) + ld h, a + ld a, SCR_ROWS + sub h + ld (S_POSN + 1), a + ld hl, __PRINT_AT2 + jr __PRINT_SET_STATE +__PRINT_AT2: + call __LOAD_S_POSN + ld e, a + call __SAVE_S_POSN + jr __PRINT_RESTART +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jr nz, 3f + ld e, SCR_COLS - 2 + dec d + cp d + jr nz, 3f + ld d, SCR_ROWS - 1 +3: + call __SAVE_S_POSN + exx + ret +__PRINT_INK: + ld hl, __PRINT_INK2 + jr __PRINT_SET_STATE +__PRINT_INK2: + call INK_TMP + jr __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jr __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jr __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jr __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jr __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jr __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 355 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 365 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __BOLD +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +1: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz 1b + pop hl + ld de, MEM0 + ret +#line 386 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __ITALIC +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) + inc hl + srl (hl) + inc hl + srl (hl) + inc hl + inc hl + inc hl + sla (hl) + inc hl + sla (hl) + inc hl + sla (hl) + pop hl + ld de, MEM0 + ret +#line 414 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __SCROLL_SCR +#line 488 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + __SCROLL_SCR EQU 0DFEh ; Use ROM SCROLL +#line 490 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 491 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 +PRINT_TAB: + ; Tabulates the number of spaces in A register + ; If the current cursor position is already A, does nothing + PROC + LOCAL LOOP + call __LOAD_S_POSN ; e = current row + sub e + and 31 + ret z + ld b, a +LOOP: + ld a, ' ' + call __PRINTCHAR + djnz LOOP + ret + ENDP +PRINT_AT: ; Changes cursor to ROW, COL + ; COL in A register + ; ROW in stack + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl + call __IN_SCREEN + ret nc ; Return if out of screen + jp __SAVE_S_POSN + LOCAL __PRINT_COM + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_BOLD + LOCAL __PRINT_ITA + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + LOCAL __PRINT_ITA2 +#line 547 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 553 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_TABLE: ; Jump table for 0 .. 22 codes + DW __PRINT_NOP ; 0 + DW __PRINT_NOP ; 1 + DW __PRINT_NOP ; 2 + DW __PRINT_NOP ; 3 + DW __PRINT_NOP ; 4 + DW __PRINT_NOP ; 5 + DW __PRINT_COM ; 6 COMMA + DW __PRINT_NOP ; 7 + DW __PRINT_DEL ; 8 DEL + DW __PRINT_NOP ; 9 + DW __PRINT_NOP ; 10 + DW __PRINT_NOP ; 11 + DW __PRINT_NOP ; 12 + DW __PRINT_0Dh ; 13 + DW __PRINT_BOLD ; 14 + DW __PRINT_ITA ; 15 + DW __PRINT_INK ; 16 + DW __PRINT_PAP ; 17 + DW __PRINT_FLA ; 18 + DW __PRINT_BRI ; 19 + DW __PRINT_INV ; 20 + DW __PRINT_OVR ; 21 + DW __PRINT_AT ; 22 AT + DW __PRINT_TAB ; 23 TAB + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" + push namespace core +COPY_ATTR: + ; Just copies current permanent attribs into temporal attribs + ; and sets print mode + PROC + LOCAL INVERSE1 + LOCAL __REFRESH_TMP + INVERSE1 EQU 02Fh + ld hl, (ATTR_P) + ld (ATTR_T), hl + ld hl, FLAGS2 + call __REFRESH_TMP + ld hl, P_FLAG + call __REFRESH_TMP +__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) + LOCAL TABLE + LOCAL CONT2 + rra ; Over bit to carry + ld a, (FLAGS2) + rla ; Over bit in bit 1, Over2 bit in bit 2 + and 3 ; Only bit 0 and 1 (OVER flag) + ld c, a + ld b, 0 + ld hl, TABLE + add hl, bc + ld a, (hl) + ld (PRINT_MODE), a + ld hl, (P_FLAG) + xor a ; NOP -> INVERSE0 + bit 2, l + jr z, CONT2 + ld a, INVERSE1 ; CPL -> INVERSE1 +CONT2: + ld (INVERSE_MODE), a + ret +TABLE: + nop ; NORMAL MODE + xor (hl) ; OVER 1 MODE + and (hl) ; OVER 2 MODE + or (hl) ; OVER 3 MODE +#line 67 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +__REFRESH_TMP: + ld a, (hl) + and 0b10101010 + ld c, a + rra + or c + ld (hl), a + ret + ENDP + pop namespace +#line 17 "arch/zxnext/print_tab2.bas" + END diff --git a/tests/functional/arch/zxnext/print_tab2.bas b/tests/functional/arch/zxnext/print_tab2.bas new file mode 100644 index 000000000..f49690308 --- /dev/null +++ b/tests/functional/arch/zxnext/print_tab2.bas @@ -0,0 +1,4 @@ +DIM i As UByte + +PRINT TAB i + 2; + diff --git a/tests/functional/arch/zxnext/print_u16.asm b/tests/functional/arch/zxnext/print_u16.asm index 305e88269..99b32db29 100644 --- a/tests/functional/arch/zxnext/print_u16.asm +++ b/tests/functional/arch/zxnext/print_u16.asm @@ -973,6 +973,13 @@ __PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER must preserve registers pop namespace #line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printi16.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" ; 16 bit division and modulo functions ; for both signed and unsigned values #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg16.asm" @@ -991,7 +998,7 @@ __NEGHL: inc hl ret pop namespace -#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" +#line 5 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" push namespace core __DIVU16: ; 16 bit unsigned division ; HL = Dividend, Stack Top = Divisor @@ -1064,6 +1071,7 @@ __MODI16: ; 16 bit modulus ; de = quotient ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" #line 3 "/zxbasic/src/lib/arch/zxnext/runtime/printi16.asm" push namespace core __PRINTI16: ; Prints a 16bits signed in HL diff --git a/tests/functional/arch/zxnext/print_u32.asm b/tests/functional/arch/zxnext/print_u32.asm index 3074ebe39..29f8dee6c 100644 --- a/tests/functional/arch/zxnext/print_u32.asm +++ b/tests/functional/arch/zxnext/print_u32.asm @@ -1000,6 +1000,13 @@ __NEG32: ; Negates DEHL (Two's complement) pop namespace #line 3 "/zxbasic/src/lib/arch/zxnext/runtime/printi32.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" ; --------------------------------------------------------- push namespace core __DIVU32: ; 32 bit unsigned division @@ -1112,6 +1119,7 @@ __MODI32: ; 32bits signed division modulus call __DIVI32START jp __MODU32START pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" #line 4 "/zxbasic/src/lib/arch/zxnext/runtime/printi32.asm" push namespace core __PRINTI32: diff --git a/tests/functional/arch/zxnext/print_u8.asm b/tests/functional/arch/zxnext/print_u8.asm index f53f3b604..0a4d6325e 100644 --- a/tests/functional/arch/zxnext/print_u8.asm +++ b/tests/functional/arch/zxnext/print_u8.asm @@ -973,6 +973,13 @@ __PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER must preserve registers pop namespace #line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printi8.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -1037,6 +1044,7 @@ __MODI8_FAST: ; __FASTCALL__ entry ld a, l ; remainder ret ; a = Modulus pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" #line 3 "/zxbasic/src/lib/arch/zxnext/runtime/printi8.asm" push namespace core __PRINTI8: ; Prints an 8 bits number in Accumulator (A) diff --git a/tests/functional/arch/zxnext/sgnf.asm b/tests/functional/arch/zxnext/sgnf.asm new file mode 100644 index 000000000..1915e4439 --- /dev/null +++ b/tests/functional/arch/zxnext/sgnf.asm @@ -0,0 +1,94 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_y: + DEFB 81h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_y) + ld de, (_y + 1) + ld bc, (_y + 3) + call .core.__SGNF + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sgnf.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgnf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sgn.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgn.asm" + ; Returns SGN (SIGN) for 32, 16 and 8 bits signed integers, Fixed and FLOAT + push namespace core + PROC + LOCAL __ENDSGN +__SGNF: + or b + or c + or d + or e + ret z + ld a, e + jr __ENDSGN +__SGNF16: +__SGNI32: + ld a, h + or l + or e + or d + ret z + ld a, d + jr __ENDSGN +__SGNI16: + ld a, h + or l + ret z + ld a, h +__ENDSGN: + or a + ld a, 1 + ret p + neg + ret + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/sgn.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sgnf.asm" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/sgnf.asm" +#line 18 "arch/zxnext/sgnf.bas" + END diff --git a/tests/functional/arch/zxnext/sgnf.bas b/tests/functional/arch/zxnext/sgnf.bas new file mode 100644 index 000000000..e652173d7 --- /dev/null +++ b/tests/functional/arch/zxnext/sgnf.bas @@ -0,0 +1,2 @@ +dim y as Float=1 +poke 0, sgn y diff --git a/tests/functional/arch/zxnext/sgnf16.asm b/tests/functional/arch/zxnext/sgnf16.asm new file mode 100644 index 000000000..9d4c01650 --- /dev/null +++ b/tests/functional/arch/zxnext/sgnf16.asm @@ -0,0 +1,92 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_y: + DEFB 00h + DEFB 00h + DEFB 01h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_y) + ld de, (_y + 2) + call .core.__SGNF16 + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sgnf16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgnf16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sgn.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgn.asm" + ; Returns SGN (SIGN) for 32, 16 and 8 bits signed integers, Fixed and FLOAT + push namespace core + PROC + LOCAL __ENDSGN +__SGNF: + or b + or c + or d + or e + ret z + ld a, e + jr __ENDSGN +__SGNF16: +__SGNI32: + ld a, h + or l + or e + or d + ret z + ld a, d + jr __ENDSGN +__SGNI16: + ld a, h + or l + ret z + ld a, h +__ENDSGN: + or a + ld a, 1 + ret p + neg + ret + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/sgn.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sgnf16.asm" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/sgnf16.asm" +#line 17 "arch/zxnext/sgnf16.bas" + END diff --git a/tests/functional/arch/zxnext/sgnf16.bas b/tests/functional/arch/zxnext/sgnf16.bas new file mode 100644 index 000000000..717a60512 --- /dev/null +++ b/tests/functional/arch/zxnext/sgnf16.bas @@ -0,0 +1,2 @@ +dim y as Fixed=1 +poke 0, sgn y diff --git a/tests/functional/arch/zxnext/sgni16.asm b/tests/functional/arch/zxnext/sgni16.asm new file mode 100644 index 000000000..e6a9f8a93 --- /dev/null +++ b/tests/functional/arch/zxnext/sgni16.asm @@ -0,0 +1,89 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_y: + DEFB 01h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_y) + call .core.__SGNI16 + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sgni16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgni16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sgn.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgn.asm" + ; Returns SGN (SIGN) for 32, 16 and 8 bits signed integers, Fixed and FLOAT + push namespace core + PROC + LOCAL __ENDSGN +__SGNF: + or b + or c + or d + or e + ret z + ld a, e + jr __ENDSGN +__SGNF16: +__SGNI32: + ld a, h + or l + or e + or d + ret z + ld a, d + jr __ENDSGN +__SGNI16: + ld a, h + or l + ret z + ld a, h +__ENDSGN: + or a + ld a, 1 + ret p + neg + ret + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/sgn.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sgni16.asm" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/sgni16.asm" +#line 16 "arch/zxnext/sgni16.bas" + END diff --git a/tests/functional/arch/zxnext/sgni16.bas b/tests/functional/arch/zxnext/sgni16.bas new file mode 100644 index 000000000..ae9e6710d --- /dev/null +++ b/tests/functional/arch/zxnext/sgni16.bas @@ -0,0 +1,2 @@ +dim y as Integer=1 +poke 0, sgn y diff --git a/tests/functional/arch/zxnext/sgni32.asm b/tests/functional/arch/zxnext/sgni32.asm new file mode 100644 index 000000000..691d66604 --- /dev/null +++ b/tests/functional/arch/zxnext/sgni32.asm @@ -0,0 +1,92 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_y: + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_y) + ld de, (_y + 2) + call .core.__SGNI32 + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sgni32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgni32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sgn.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgn.asm" + ; Returns SGN (SIGN) for 32, 16 and 8 bits signed integers, Fixed and FLOAT + push namespace core + PROC + LOCAL __ENDSGN +__SGNF: + or b + or c + or d + or e + ret z + ld a, e + jr __ENDSGN +__SGNF16: +__SGNI32: + ld a, h + or l + or e + or d + ret z + ld a, d + jr __ENDSGN +__SGNI16: + ld a, h + or l + ret z + ld a, h +__ENDSGN: + or a + ld a, 1 + ret p + neg + ret + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/sgn.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sgni32.asm" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/sgni32.asm" +#line 17 "arch/zxnext/sgni32.bas" + END diff --git a/tests/functional/arch/zxnext/sgni32.bas b/tests/functional/arch/zxnext/sgni32.bas new file mode 100644 index 000000000..8ae2f3c43 --- /dev/null +++ b/tests/functional/arch/zxnext/sgni32.bas @@ -0,0 +1,2 @@ +dim y as Long=1 +poke 0, sgn y diff --git a/tests/functional/arch/zxnext/sgni8.asm b/tests/functional/arch/zxnext/sgni8.asm new file mode 100644 index 000000000..fb22ca2a9 --- /dev/null +++ b/tests/functional/arch/zxnext/sgni8.asm @@ -0,0 +1,54 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_y: + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_y) + call .core.__SGNI8 + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sgni8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgni8.asm" + ; Returns SGN (SIGN) for 8 bits signed integer + push namespace core +__SGNI8: + or a + ret z + ld a, 1 + ret p + neg + ret + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/sgni8.asm" +#line 16 "arch/zxnext/sgni8.bas" + END diff --git a/tests/functional/arch/zxnext/sgni8.bas b/tests/functional/arch/zxnext/sgni8.bas new file mode 100644 index 000000000..ec06c5224 --- /dev/null +++ b/tests/functional/arch/zxnext/sgni8.bas @@ -0,0 +1,2 @@ +dim y as Byte=1 +poke 0, sgn y diff --git a/tests/functional/arch/zxnext/sgnu16.asm b/tests/functional/arch/zxnext/sgnu16.asm new file mode 100644 index 000000000..f8918e958 --- /dev/null +++ b/tests/functional/arch/zxnext/sgnu16.asm @@ -0,0 +1,54 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_y: + DEFB 01h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_y) + call .core.__SGNU16 + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sgnu16.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgnu16.asm" + ; Returns SGN (SIGN) for 16 bits unsigned integer + push namespace core +__SGNU16: + ld a, h + or l + ret z + ld a, 1 + ret + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/sgnu16.asm" +#line 16 "arch/zxnext/sgnu16.bas" + END diff --git a/tests/functional/arch/zxnext/sgnu16.bas b/tests/functional/arch/zxnext/sgnu16.bas new file mode 100644 index 000000000..af1acc29c --- /dev/null +++ b/tests/functional/arch/zxnext/sgnu16.bas @@ -0,0 +1,2 @@ +dim y as uInteger=1 +poke 0, sgn y diff --git a/tests/functional/arch/zxnext/sgnu32.asm b/tests/functional/arch/zxnext/sgnu32.asm new file mode 100644 index 000000000..e3c54660f --- /dev/null +++ b/tests/functional/arch/zxnext/sgnu32.asm @@ -0,0 +1,59 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_y: + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_y) + ld de, (_y + 2) + call .core.__SGNU32 + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sgnu32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgnu32.asm" + ; Returns SGN (SIGN) for 32 bits unsigned integer + push namespace core +__SGNU32: + ld a, h + or l + or d + or e + ret z + ld a, 1 + ret + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/sgnu32.asm" +#line 17 "arch/zxnext/sgnu32.bas" + END diff --git a/tests/functional/arch/zxnext/sgnu32.bas b/tests/functional/arch/zxnext/sgnu32.bas new file mode 100644 index 000000000..00bc09c8f --- /dev/null +++ b/tests/functional/arch/zxnext/sgnu32.bas @@ -0,0 +1,2 @@ +dim y as uLong=1 +poke 0, sgn y diff --git a/tests/functional/arch/zxnext/sgnu8.asm b/tests/functional/arch/zxnext/sgnu8.asm new file mode 100644 index 000000000..2b892a556 --- /dev/null +++ b/tests/functional/arch/zxnext/sgnu8.asm @@ -0,0 +1,52 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_y: + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_y) + call .core.__SGNU8 + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sgnu8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgnu8.asm" + ; Returns SGN (SIGN) for 8 bits unsigned integera + push namespace core +__SGNU8: + or a + ret z + ld a, 1 + ret + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/sgnu8.asm" +#line 16 "arch/zxnext/sgnu8.bas" + END diff --git a/tests/functional/arch/zxnext/sgnu8.bas b/tests/functional/arch/zxnext/sgnu8.bas new file mode 100644 index 000000000..497869458 --- /dev/null +++ b/tests/functional/arch/zxnext/sgnu8.bas @@ -0,0 +1,2 @@ +dim y as uByte=1 +poke 0, sgn y diff --git a/tests/functional/arch/zxnext/stdlib_SP_Fill.bas b/tests/functional/arch/zxnext/stdlib_SP_Fill.bas new file mode 100644 index 000000000..acc51cb3d --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_SP_Fill.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zxnext/stdlib_alloc.asm b/tests/functional/arch/zxnext/stdlib_alloc.asm new file mode 100644 index 000000000..a0f0e44fc --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_alloc.asm @@ -0,0 +1,774 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 4768 +.core.ZXBASIC_MEM_HEAP: + DEFS 4768 + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +_allocate: +#line 36 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" + push namespace core + ld b, h + ld c, l + jp __MEM_ALLOC + pop namespace +#line 43 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" +_allocate__leave: + ret +_callocate: +#line 66 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" + push namespace core + ld b, h + ld c, l + jp __MEM_CALLOC + pop namespace +#line 73 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" +_callocate__leave: + ret +_deallocate: +#line 86 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" + push namespace core + jp __MEM_FREE + pop namespace +#line 91 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" +_deallocate__leave: + ret +_reallocate: +#line 114 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" + push namespace core + ex de, hl + pop hl + ex (sp), hl + ld b, h + ld c, l + ex de, hl + jp __REALLOC + pop namespace +#line 125 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" +_reallocate__leave: + ret +_memavail: +#line 135 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" + push namespace core + PROC + LOCAL LOOP + ld hl, ZXBASIC_MEM_HEAP + ld de, 0 +LOOP: + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + ex de, hl + add hl, bc + ex de, hl + ld a, h + or l + jr nz, LOOP + ex de, hl + ENDP + pop namespace +#line 172 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" +_memavail__leave: + ret +_maxavail: +#line 181 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" + push namespace core + PROC + LOCAL LOOP, CONT + ld hl, ZXBASIC_MEM_HEAP + ld de, 0 +LOOP: + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + ex de, hl + or a + sbc hl, bc + add hl, bc + ex de, hl + jr nc, CONT + ld d, b + ld e, c +CONT: + ld a, h + or l + jr nz, LOOP + ex de, hl + ENDP + pop namespace +#line 228 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" +_maxavail__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be freed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/heapinit.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; __MEM_INIT must be called to initalize this library with the + ; standard parameters + ; --------------------------------------------------------------------- + push namespace core +__MEM_INIT: ; Initializes the library using (RAMTOP) as start, and + ld hl, ZXBASIC_MEM_HEAP ; Change this with other address of heap start + ld de, ZXBASIC_HEAP_SIZE ; Change this with your size + ; --------------------------------------------------------------------- + ; __MEM_INIT2 initalizes this library +; Parameters: +; HL : Memory address of 1st byte of the memory heap +; DE : Length in bytes of the Memory Heap + ; --------------------------------------------------------------------- +__MEM_INIT2: + ; HL as TOP + PROC + dec de + dec de + dec de + dec de ; DE = length - 4; HL = start + ; This is done, because we require 4 bytes for the empty dummy-header block + xor a + ld (hl), a + inc hl + ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 + inc hl + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block + ld (hl), c + inc hl + ld (hl), b + inc hl ; Pointer to next block + ld (hl), e + inc hl + ld (hl), d + inc hl ; Block size (should be length - 4 at start); This block contains all the available memory + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret + ENDP + pop namespace +#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" + ; --------------------------------------------------------------------- + ; MEM_ALLOC + ; Allocates a block of memory in the heap. + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 + TEMP EQU TEMP0 + 1 + ld hl, 0 + ld (TEMP), hl +__MEM_START: + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start + inc bc + inc bc ; BC = BC + 2 ; block size needs 2 extra bytes for hidden pointer +__MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE + ld a, h ; HL = NULL (No memory available?) + or l +#line 113 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" + ; HL = Pointer to Free block + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = Block Length + push hl ; HL = *pointer to -> next block + ex de, hl + or a ; CF = 0 + sbc hl, bc ; FREE >= BC (Length) (HL = BlockLength - Length) + jp nc, __MEM_DONE + pop hl + ld (TEMP), hl + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + jp __MEM_LOOP +__MEM_DONE: ; A free block has been found. + ; Check if at least 4 bytes remains free (HL >= 4) + push hl + exx ; exx to preserve bc + pop hl + ld bc, 4 + or a + sbc hl, bc + exx + jp nc, __MEM_SUBTRACT + ; At this point... + ; less than 4 bytes remains free. So we return this block entirely + ; We must link the previous block with the next to this one + ; (DE) => Pointer to next block + ; (TEMP) => &(previous->next) + pop hl ; Discard current block pointer + push de + ex de, hl ; DE = Previous block pointer; (HL) = Next block pointer + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex de, hl ; HL = Previous block pointer; DE = Next block pointer +TEMP0: + ld hl, 0 ; Pre-previous block pointer + ld (hl), e + inc hl + ld (hl), d ; LINKED + pop hl ; Returning block. + ret +__MEM_SUBTRACT: + ; At this point we have to store HL value (Length - BC) into (DE - 2) + ex de, hl + dec hl + ld (hl), d + dec hl + ld (hl), e ; Store new block length + add hl, de ; New length + DE => free-block start + pop de ; Remove previous HL off the stack + ld (hl), c ; Store length on its 1st word + inc hl + ld (hl), b + inc hl ; Return hl + ret + ENDP + pop namespace +#line 233 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/calloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet + ; --------------------------------------------------------------------- + ; MEM_CALLOC + ; Allocates a block of memory in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +__MEM_CALLOC: + push bc + call __MEM_ALLOC + pop bc + ld a, h + or l + ret z ; No memory + ld (hl), 0 + dec bc + ld a, b + or c + ret z ; Already filled (1 byte-length block) + ld d, h + ld e, l + inc de + push hl + ldir + pop hl + ret + pop namespace +#line 234 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/free.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- + push namespace core +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN + ld a, h + or l + ret z ; Return if NULL pointer + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous + or a ; Clear carry flag + sbc hl, bc ; Carry if BC > HL => This block if before + add hl, bc ; Restores HL, preserving Carry flag + jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) + inc hl + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 + call __MEM_JOIN_TEST + pop hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; + push hl ; Saves it for later + add hl, bc ; Adds its length. If HL == DE now, it must be joined + or a + sbc hl, de ; If Z, then HL == DE => We must join + pop hl + ret nz +__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC + push hl ; Saves it for later + ex de, hl + ld e, (hl) ; DE -> block->next->length + inc hl + ld d, (hl) + inc hl + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next + pop hl ; Recovers Pointer to block + ld (hl), c + inc hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved + ret + ENDP + pop namespace +#line 235 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/realloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_REALLOC + ; Reallocates a block of memory in the heap. + ; + ; Parameters + ; HL = Pointer to the original block + ; BC = New Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; +; Notes: + ; If BC = 0, the block is freed, otherwise + ; the content of the original block is copied to the new one, and + ; the new size is adjusted. If BC < original length, the content + ; will be truncated. Otherwise, extra block content might contain + ; memory garbage. + ; + ; --------------------------------------------------------------------- + push namespace core +__REALLOC: ; Reallocates block pointed by HL, with new length BC + PROC + LOCAL __REALLOC_END + ld a, h + or l + jp z, __MEM_ALLOC ; If HL == NULL, just do a malloc + ld e, (hl) + inc hl + ld d, (hl) ; DE = First 2 bytes of HL block + push hl + exx + pop de + inc de ; DE' <- HL + 2 + exx ; DE' <- HL (Saves current pointer into DE') + dec hl ; HL = Block start + push de + push bc + call __MEM_FREE ; Frees current block + pop bc + push bc + call __MEM_ALLOC ; Gets a new block of length BC + pop bc + pop de + ld a, h + or l + ret z ; Return if HL == NULL (No memory) + ld (hl), e + inc hl + ld (hl), d + inc hl ; Recovers first 2 bytes in HL + dec bc + dec bc ; BC = BC - 2 (Two bytes copied) + ld a, b + or c + jp z, __REALLOC_END ; Ret if nothing to copy (BC == 0) + exx + push de + exx + pop de ; DE <- DE' ; Start of remaining block + push hl ; Saves current Block + 2 start + ex de, hl ; Exchanges them: DE is destiny block + ldir ; Copies BC Bytes + pop hl ; Recovers Block + 2 start +__REALLOC_END: + dec hl ; Set HL + dec hl ; To begin of block + ret + ENDP + pop namespace +#line 236 "/zxbasic/src/lib/arch/zxnext/stdlib/alloc.bas" + END diff --git a/tests/functional/arch/zxnext/stdlib_alloc.bas b/tests/functional/arch/zxnext/stdlib_alloc.bas new file mode 100644 index 000000000..3c7a20be1 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_alloc.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zxnext/stdlib_attr.asm b/tests/functional/arch/zxnext/stdlib_attr.asm new file mode 100644 index 000000000..ba1190474 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_attr.asm @@ -0,0 +1,271 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +_attr: + push ix + ld ix, 0 + add ix, sp +#line 28 "/zxbasic/src/lib/arch/zxnext/stdlib/attr.bas" + push namespace core + PROC + LOCAL __ATTR_END + ld e, (ix+7) + ld d, (ix+5) + call __IN_SCREEN + jr nc, __ATTR_END + call __ATTR_ADDR + ld a, (hl) +__ATTR_END: + ENDP + pop namespace +#line 49 "/zxbasic/src/lib/arch/zxnext/stdlib/attr.bas" +_attr__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_setattr: + push ix + ld ix, 0 + add ix, sp +#line 66 "/zxbasic/src/lib/arch/zxnext/stdlib/attr.bas" + push namespace core + PROC + LOCAL __ATTR_END + ld e, (ix+7) + ld d, (ix+5) + call __IN_SCREEN + jr nc, __ATTR_END + call __ATTR_ADDR + ld a, (ix+9) + ld (hl), a +__ATTR_END: + ENDP + pop namespace +#line 88 "/zxbasic/src/lib/arch/zxnext/stdlib/attr.bas" +_setattr__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + ex (sp), hl + exx + ret +_attraddr: +#line 102 "/zxbasic/src/lib/arch/zxnext/stdlib/attr.bas" + push namespace core + pop hl + ex (sp), hl + ld d, a + ld e, h + jp __ATTR_ADDR + pop namespace +#line 112 "/zxbasic/src/lib/arch/zxnext/stdlib/attr.bas" +_attraddr__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" + ; Printing positioning library. + push namespace core + ; Loads into DE current ROW, COL print position from S_POSN mem var. +__LOAD_S_POSN: + PROC + ld de, (S_POSN) + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + ret + ENDP + ; Saves ROW, COL from DE into S_POSN mem var. +__SAVE_S_POSN: + PROC + ld hl, SCR_SIZE + or a + sbc hl, de + ld (S_POSN), hl ; saves it again +__SET_SCR_PTR: ;; Fast + push de + call __ATTR_ADDR + ld (DFCCL), hl + pop de + ld a, d + ld c, a ; Saves it for later + and 0F8h ; Masks 3 lower bit ; zy + ld d, a + ld a, c ; Recovers it + and 07h ; MOD 7 ; y1 + rrca + rrca + rrca + or e + ld e, a + ld hl, (SCREEN_ADDR) + add hl, de ; HL = Screen address + DE + ld (DFCC), hl + ret + ENDP + pop namespace +#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/in_screen.asm" + push namespace core +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits + PROC + LOCAL __IN_SCREEN_ERR + ld hl, SCR_SIZE + ld a, e + cp l + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld a, d + cp h + ret c ; Return if carry (OK) +__IN_SCREEN_ERR: +__OUT_OF_SCREEN_ERR: + ; Jumps here if out of screen + ld a, ERROR_OutOfScreen + jp __STOP ; Saves error code and exits + ENDP + pop namespace +#line 7 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + push namespace core +__ATTR_ADDR: + ; calc start address in DE (as (32 * d) + e) + ; Contributed by Santiago Romero at http://www.speccy.org + ld h, 0 ; 7 T-States + ld a, d ; 4 T-States + ld d, h + add a, a ; a * 2 ; 4 T-States + add a, a ; a * 4 ; 4 T-States + ld l, a ; HL = A * 4 ; 4 T-States + add hl, hl ; HL = A * 8 ; 15 T-States + add hl, hl ; HL = A * 16 ; 15 T-States + add hl, hl ; HL = A * 32 ; 15 T-States + add hl, de + ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address + add hl, de + ; Return current screen address in HL + ret + ; Sets the attribute at a given screen coordinate (D, E). + ; The attribute is taken from the ATTR_T memory variable + ; Used by PRINT routines +SET_ATTR: + ; Checks for valid coords + call __IN_SCREEN + ret nc + call __ATTR_ADDR +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + ; HL contains the address of the ATTR cell to set + PROC +__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + ld a, d + and (hl) + ld c, a ; C = current screen color, masked + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + ret + ENDP + pop namespace +#line 117 "/zxbasic/src/lib/arch/zxnext/stdlib/attr.bas" + END diff --git a/tests/functional/arch/zxnext/stdlib_attr.bas b/tests/functional/arch/zxnext/stdlib_attr.bas new file mode 100644 index 000000000..e7fe7d2cf --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_attr.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zxnext/stdlib_basic.asm b/tests/functional/arch/zxnext/stdlib_basic.asm new file mode 100644 index 000000000..28f01e42b --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_basic.asm @@ -0,0 +1,154 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +_EvalBASIC: +#line 49 "/zxbasic/src/lib/arch/zxnext/stdlib/basic.bas" + push namespace core + PROC + LOCAL E_LINE + LOCAL CH_ADD + LOCAL SET_MIN + LOCAL MAKE_ROOM + LOCAL K_CUR + LOCAL LINE_SCAN + LOCAL LINE_RUN + LOCAL DEF_ADD + LOCAL NEWPPC + LOCAL NSPPC + LOCAL PPC + LOCAL SUBPPC + LOCAL NXTLIN + E_LINE equ 5c59h + CH_ADD equ 5c5dh + SET_MIN equ 16b0h + MAKE_ROOM equ 1655h + K_CUR equ 5c5bh + LINE_SCAN equ 1b17h + LINE_RUN equ 1b8ah + DEF_ADD equ 5c0bh + NEWPPC equ 5c42h + NSPPC equ 5c44h + PPC equ 5c45h + SUBPPC equ 5c47h + NXTLIN equ 5c55h + ld a, h + or l + ret z + ld de,(CH_ADD) + push de + ld de,(NXTLIN) + push de + ld de,(PPC) + push de + ld a,(SUBPPC) + push af + ld de,(NEWPPC) + push de + ld a,(NSPPC) + push af + push ix + ld c, (hl) + inc hl + ld b, (hl) + inc hl + push hl + push bc + call SET_MIN + ld hl,(E_LINE) + pop bc + push bc + call MAKE_ROOM + pop bc + pop hl + ld de,(E_LINE) + ldir + ld (K_CUR),de + call LINE_SCAN + bit 7,(iy+0) + ld a, ERROR_NonsenseInBasic + jp z, __ERROR + ld hl,(E_LINE) + ld (CH_ADD),hl + set 7,(iy+1) + ld (iy+0),0xff + ld (iy+10),1 + call LINE_RUN + pop ix + pop af + ld (NSPPC),a + pop hl + ld (NEWPPC),hl + pop af + ld (SUBPPC),a + pop hl + ld (PPC),hl + pop hl + ld (NXTLIN),hl + pop hl + ld (CH_ADD),hl + ENDP + pop namespace +#line 143 "/zxbasic/src/lib/arch/zxnext/stdlib/basic.bas" +_EvalBASIC__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 148 "/zxbasic/src/lib/arch/zxnext/stdlib/basic.bas" + END diff --git a/tests/functional/arch/zxnext/stdlib_basic.bas b/tests/functional/arch/zxnext/stdlib_basic.bas new file mode 100644 index 000000000..306234257 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_basic.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zxnext/stdlib_clearbox.asm b/tests/functional/arch/zxnext/stdlib_clearbox.asm new file mode 100644 index 000000000..a6a96cab5 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_clearbox.asm @@ -0,0 +1,121 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +_clearBox: + push ix + ld ix, 0 + add ix, sp +#line 37 "/zxbasic/src/lib/arch/zxnext/stdlib/clearbox.bas" + PROC + LOCAL clearbox_outer_loop, clearbox_mid_loop + LOCAL clearbox_inner_loop, clearbox_row_skip + ld b,(IX+5) + ld c,(IX+7) + ld a, c + and 24 + ld h, a + ld a, c + and 7 + rra + rra + rra + rra + add a, b + ld l, a + ld b, (IX+11) + ld c,(IX+9) +clearbox_outer_loop: + xor a + push bc + push hl + ld de, (.core.SCREEN_ADDR) + add hl, de + ld d, 8 +clearbox_mid_loop: + push hl + ld b,c +clearbox_inner_loop: + ld (hl), a + inc hl + djnz clearbox_inner_loop + pop hl + inc h + dec d + jp nz, clearbox_mid_loop + pop hl + pop bc + ld a, 32 + add a, l + ld l, a + jr nc, clearbox_row_skip + ld a, 8 + add a, h + ld h, a +clearbox_row_skip: + djnz clearbox_outer_loop + ENDP +#line 101 "/zxbasic/src/lib/arch/zxnext/stdlib/clearbox.bas" +_clearBox__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + pop bc + ex (sp), hl + exx + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 115 "/zxbasic/src/lib/arch/zxnext/stdlib/clearbox.bas" + END diff --git a/tests/functional/arch/zxnext/stdlib_clearbox.bas b/tests/functional/arch/zxnext/stdlib_clearbox.bas new file mode 100644 index 000000000..a9303731c --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_clearbox.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zxnext/stdlib_csrlin.asm b/tests/functional/arch/zxnext/stdlib_csrlin.asm new file mode 100644 index 000000000..1150414c5 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_csrlin.asm @@ -0,0 +1,214 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +_csrlin: +#line 26 "/zxbasic/src/lib/arch/zxnext/stdlib/csrlin.bas" + push namespace core + PROC + call __LOAD_S_POSN + ld a, d + ENDP + pop namespace +#line 36 "/zxbasic/src/lib/arch/zxnext/stdlib/csrlin.bas" +_csrlin__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/in_screen.asm" + push namespace core +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits + PROC + LOCAL __IN_SCREEN_ERR + ld hl, SCR_SIZE + ld a, e + cp l + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld a, d + cp h + ret c ; Return if carry (OK) +__IN_SCREEN_ERR: +__OUT_OF_SCREEN_ERR: + ; Jumps here if out of screen + ld a, ERROR_OutOfScreen + jp __STOP ; Saves error code and exits + ENDP + pop namespace +#line 7 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + push namespace core +__ATTR_ADDR: + ; calc start address in DE (as (32 * d) + e) + ; Contributed by Santiago Romero at http://www.speccy.org + ld h, 0 ; 7 T-States + ld a, d ; 4 T-States + ld d, h + add a, a ; a * 2 ; 4 T-States + add a, a ; a * 4 ; 4 T-States + ld l, a ; HL = A * 4 ; 4 T-States + add hl, hl ; HL = A * 8 ; 15 T-States + add hl, hl ; HL = A * 16 ; 15 T-States + add hl, hl ; HL = A * 32 ; 15 T-States + add hl, de + ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address + add hl, de + ; Return current screen address in HL + ret + ; Sets the attribute at a given screen coordinate (D, E). + ; The attribute is taken from the ATTR_T memory variable + ; Used by PRINT routines +SET_ATTR: + ; Checks for valid coords + call __IN_SCREEN + ret nc + call __ATTR_ADDR +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + ; HL contains the address of the ATTR cell to set + PROC +__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + ld a, d + and (hl) + ld c, a ; C = current screen color, masked + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + ret + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" + ; Printing positioning library. + push namespace core + ; Loads into DE current ROW, COL print position from S_POSN mem var. +__LOAD_S_POSN: + PROC + ld de, (S_POSN) + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + ret + ENDP + ; Saves ROW, COL from DE into S_POSN mem var. +__SAVE_S_POSN: + PROC + ld hl, SCR_SIZE + or a + sbc hl, de + ld (S_POSN), hl ; saves it again +__SET_SCR_PTR: ;; Fast + push de + call __ATTR_ADDR + ld (DFCCL), hl + pop de + ld a, d + ld c, a ; Saves it for later + and 0F8h ; Masks 3 lower bit ; zy + ld d, a + ld a, c ; Recovers it + and 07h ; MOD 7 ; y1 + rrca + rrca + rrca + or e + ld e, a + ld hl, (SCREEN_ADDR) + add hl, de ; HL = Screen address + DE + ld (DFCC), hl + ret + ENDP + pop namespace +#line 41 "/zxbasic/src/lib/arch/zxnext/stdlib/csrlin.bas" + END diff --git a/tests/functional/arch/zxnext/stdlib_csrlin.bas b/tests/functional/arch/zxnext/stdlib_csrlin.bas new file mode 100644 index 000000000..135e44c0f --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_csrlin.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zxnext/stdlib_hex.asm b/tests/functional/arch/zxnext/stdlib_hex.asm new file mode 100644 index 000000000..3237cabb7 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_hex.asm @@ -0,0 +1,847 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 4768 +.core.ZXBASIC_MEM_HEAP: + DEFS 4768 + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +_hex: +#line 30 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" + push namespace core + PROC + LOCAL SUB_CHAR + LOCAL SUB_CHAR2 + LOCAL END_CHAR + LOCAL DIGIT + push hl + push de + ld bc,10 + call __MEM_ALLOC + ld a, h + or l + pop de + pop bc + ret z + push hl + ld (hl), 8 + inc hl + ld (hl), 0 + inc hl + call DIGIT + ld d, e + call DIGIT + ld d, b + call DIGIT + ld d, c + call DIGIT + pop hl + ret +DIGIT: + ld a, d + call SUB_CHAR + ld a, d + jr SUB_CHAR2 +SUB_CHAR: + rrca + rrca + rrca + rrca +SUB_CHAR2: + and 0Fh + add a, '0' + cp '9' + 1 + jr c, END_CHAR + add a, 7 +END_CHAR: + ld (hl), a + inc hl + ret + ENDP + pop namespace +#line 91 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" +_hex__leave: + ret +_hex16: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + ld l, (ix+4) + ld h, (ix+5) + ld de, 0 + call _hex + ld d, h + ld e, l + ld bc, -2 + call .core.__PSTORE_STR2 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld hl, 4 + push hl + ld hl, 7 + push hl + xor a + call .core.__STRSLICE +_hex16__leave: + ex af, af' + exx + ld l, (ix-2) + ld h, (ix-1) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret +_hex8: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + ld a, (ix+5) + ld l, a + ld h, 0 + ld e, h + ld d, h + call _hex + ld d, h + ld e, l + ld bc, -2 + call .core.__PSTORE_STR2 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld hl, 6 + push hl + ld hl, 7 + push hl + xor a + call .core.__STRSLICE +_hex8__leave: + ex af, af' + exx + ld l, (ix-2) + ld h, (ix-1) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be freed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/heapinit.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/heapinit.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; __MEM_INIT must be called to initalize this library with the + ; standard parameters + ; --------------------------------------------------------------------- + push namespace core +__MEM_INIT: ; Initializes the library using (RAMTOP) as start, and + ld hl, ZXBASIC_MEM_HEAP ; Change this with other address of heap start + ld de, ZXBASIC_HEAP_SIZE ; Change this with your size + ; --------------------------------------------------------------------- + ; __MEM_INIT2 initalizes this library +; Parameters: +; HL : Memory address of 1st byte of the memory heap +; DE : Length in bytes of the Memory Heap + ; --------------------------------------------------------------------- +__MEM_INIT2: + ; HL as TOP + PROC + dec de + dec de + dec de + dec de ; DE = length - 4; HL = start + ; This is done, because we require 4 bytes for the empty dummy-header block + xor a + ld (hl), a + inc hl + ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 + inc hl + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block + ld (hl), c + inc hl + ld (hl), b + inc hl ; Pointer to next block + ld (hl), e + inc hl + ld (hl), d + inc hl ; Block size (should be length - 4 at start); This block contains all the available memory + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/mem/heapinit.asm" +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ; --------------------------------------------------------------------- + ; MEM_ALLOC + ; Allocates a block of memory in the heap. + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 + TEMP EQU TEMP0 + 1 + ld hl, 0 + ld (TEMP), hl +__MEM_START: + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start + inc bc + inc bc ; BC = BC + 2 ; block size needs 2 extra bytes for hidden pointer +__MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE + ld a, h ; HL = NULL (No memory available?) + or l +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ; HL = Pointer to Free block + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = Block Length + push hl ; HL = *pointer to -> next block + ex de, hl + or a ; CF = 0 + sbc hl, bc ; FREE >= BC (Length) (HL = BlockLength - Length) + jp nc, __MEM_DONE + pop hl + ld (TEMP), hl + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + jp __MEM_LOOP +__MEM_DONE: ; A free block has been found. + ; Check if at least 4 bytes remains free (HL >= 4) + push hl + exx ; exx to preserve bc + pop hl + ld bc, 4 + or a + sbc hl, bc + exx + jp nc, __MEM_SUBTRACT + ; At this point... + ; less than 4 bytes remains free. So we return this block entirely + ; We must link the previous block with the next to this one + ; (DE) => Pointer to next block + ; (TEMP) => &(previous->next) + pop hl ; Discard current block pointer + push de + ex de, hl ; DE = Previous block pointer; (HL) = Next block pointer + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex de, hl ; HL = Previous block pointer; DE = Next block pointer +TEMP0: + ld hl, 0 ; Pre-previous block pointer + ld (hl), e + inc hl + ld (hl), d ; LINKED + pop hl ; Returning block. + ret +__MEM_SUBTRACT: + ; At this point we have to store HL value (Length - BC) into (DE - 2) + ex de, hl + dec hl + ld (hl), d + dec hl + ld (hl), e ; Store new block length + add hl, de ; New length + DE => free-block start + pop de ; Remove previous HL off the stack + ld (hl), c ; Store length on its 1st word + inc hl + ld (hl), b + inc hl ; Return hl + ret + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +#line 174 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/free.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/free.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- + push namespace core +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN + ld a, h + or l + ret z ; Return if NULL pointer + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous + or a ; Clear carry flag + sbc hl, bc ; Carry if BC > HL => This block if before + add hl, bc ; Restores HL, preserving Carry flag + jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) + inc hl + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 + call __MEM_JOIN_TEST + pop hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; + push hl ; Saves it for later + add hl, bc ; Adds its length. If HL == DE now, it must be joined + or a + sbc hl, de ; If Z, then HL == DE => We must join + pop hl + ret nz +__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC + push hl ; Saves it for later + ex de, hl + ld e, (hl) ; DE -> block->next->length + inc hl + ld d, (hl) + inc hl + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next + pop hl ; Recovers Pointer to block + ld (hl), c + inc hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved + ret + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/mem/free.asm" +#line 175 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/pstorestr2.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/pstorestr2.asm" +; vim:ts=4:et:sw=4 + ; + ; Stores an string (pointer to the HEAP by DE) into the address pointed + ; by (IX + BC). No new copy of the string is created into the HEAP, since + ; it's supposed it's already created (temporary string) + ; +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storestr2.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/storestr2.asm" + ; Similar to __STORE_STR, but this one is called when + ; the value of B$ if already duplicated onto the stack. + ; So we needn't call STRASSING to create a duplication + ; HL = address of string memory variable + ; DE = address of 2n string. It just copies DE into (HL) + ; freeing (HL) previously. + push namespace core +__PISTORE_STR2: ; Indirect store temporary string at (IX + BC) + push ix + pop hl + add hl, bc +__ISTORE_STR2: + ld c, (hl) ; Dereferences HL + inc hl + ld h, (hl) + ld l, c ; HL = *HL (real string variable address) +__STORE_STR2: + push hl + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = *HL (real string address) + push de + call __MEM_FREE + pop de + pop hl + ld (hl), e + inc hl + ld (hl), d + dec hl ; HL points to mem address variable. This might be useful in the future. + ret + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/storestr2.asm" +#line 9 "/zxbasic/src/lib/arch/zx48k/runtime/pstorestr2.asm" + push namespace core +__PSTORE_STR2: + push ix + pop hl + add hl, bc + jp __STORE_STR2 + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/pstorestr2.asm" +#line 176 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/strslice.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/strslice.asm" + ; String slicing library + ; HL = Str pointer + ; DE = String start + ; BC = String character end + ; A register => 0 => the HL pointer wont' be freed from the HEAP + ; e.g. a$(5 TO 10) => HL = a$; DE = 5; BC = 10 + ; This implements a$(X to Y) being X and Y first and + ; last characters respectively. If X > Y, NULL is returned + ; Otherwise returns a pointer to a$ FROM X to Y (starting from 0) + ; if Y > len(a$), then a$ will be padded with spaces (reallocating + ; it in dynamic memory if needed). Returns pointer (HL) to resulting + ; string. NULL (0) if no memory for padding. + ; +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/strlen.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/strlen.asm" + ; Returns len if a string + ; If a string is NULL, its len is also 0 + ; Result returned in HL + push namespace core +__STRLEN: ; Direct FASTCALL entry + ld a, h + or l + ret z + ld a, (hl) + inc hl + ld h, (hl) ; LEN(str) in HL + ld l, a + ret + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/strlen.asm" +#line 18 "/zxbasic/src/lib/arch/zx48k/runtime/strslice.asm" + push namespace core +__STRSLICE: ; Callee entry + pop hl ; Return ADDRESS + pop bc ; Last char pos + pop de ; 1st char pos + ex (sp), hl ; CALLEE. -> String start +__STRSLICE_FAST: ; __FASTCALL__ Entry + PROC + LOCAL __CONT + LOCAL __EMPTY + LOCAL __FREE_ON_EXIT + push hl ; Stores original HL pointer to be recovered on exit + ex af, af' ; Saves A register for later + push hl + call __STRLEN + inc bc ; Last character position + 1 (string starts from 0) + or a + sbc hl, bc ; Compares length with last char position + jr nc, __CONT ; If Carry => We must copy to end of string + add hl, bc ; Restore back original LEN(a$) in HL + ld b, h + ld c, l ; Copy to the end of str + ccf ; Clears Carry flag for next subtraction +__CONT: + ld h, b + ld l, c ; HL = Last char position to copy (1 for char 0, 2 for char 1, etc) + sbc hl, de ; HL = LEN(a$) - DE => Number of chars to copy + jr z, __EMPTY ; 0 Chars to copy => Return HL = 0 (NULL STR) + jr c, __EMPTY ; If Carry => Nothing to return (NULL STR) + ld b, h + ld c, l ; BC = Number of chars to copy + inc bc + inc bc ; +2 bytes for string length number + push bc + push de + call __MEM_ALLOC + pop de + pop bc + ld a, h + or l + jr z, __EMPTY ; Return if NULL (no memory) + dec bc + dec bc ; Number of chars to copy (Len of slice) + ld (hl), c + inc hl + ld (hl), b + inc hl ; Stores new string length + ex (sp), hl ; Pointer to A$ now in HL; Pointer to new string chars in Stack + inc hl + inc hl ; Skip string length + add hl, de ; Were to start from A$ + pop de ; Start of new string chars + push de ; Stores it again + ldir ; Copies BC chars + pop de + dec de + dec de ; Points to String LEN start + ex de, hl ; Returns it in HL + jr __FREE_ON_EXIT +__EMPTY: ; Return NULL (empty) string + pop hl + ld hl, 0 ; Return NULL +__FREE_ON_EXIT: + ex af, af' ; Recover original A register + ex (sp), hl ; Original HL pointer + or a + call nz, __MEM_FREE + pop hl ; Recover result + ret + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/strslice.asm" +#line 177 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" + END diff --git a/tests/functional/arch/zxnext/stdlib_hex.bas b/tests/functional/arch/zxnext/stdlib_hex.bas new file mode 100644 index 000000000..605e8a9f0 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_hex.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zxnext/stdlib_memcopy.asm b/tests/functional/arch/zxnext/stdlib_memcopy.asm new file mode 100644 index 000000000..aa12195d2 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_memcopy.asm @@ -0,0 +1,123 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +_MemMove: +#line 31 "/zxbasic/src/lib/arch/zxnext/stdlib/memcopy.bas" + push namespace core + exx + pop hl + exx + pop de + pop bc + exx + push hl + exx + jp __MEMCPY + pop namespace +#line 51 "/zxbasic/src/lib/arch/zxnext/stdlib/memcopy.bas" +_MemMove__leave: + ret +_MemCopy: +#line 67 "/zxbasic/src/lib/arch/zxnext/stdlib/memcopy.bas" + push namespace core + exx + pop hl + exx + pop de + pop bc + exx + push hl + exx + ldir + pop namespace +#line 87 "/zxbasic/src/lib/arch/zxnext/stdlib/memcopy.bas" +_MemCopy__leave: + ret +_MemSet: +#line 100 "/zxbasic/src/lib/arch/zxnext/stdlib/memcopy.bas" + push namespace core + pop de + pop af + pop bc + push de + ld (hl),a + dec bc + ld a, b + or c + ret z + ld d,h + ld e,l + inc de + ldir + pop namespace +#line 122 "/zxbasic/src/lib/arch/zxnext/stdlib/memcopy.bas" +_MemSet__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/memcopy.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; + ; Use this file as a template to develop your own library file + ; ---------------------------------------------------------------- + ; Emulates both memmove and memcpy C routines + ; Block will be safely copied if they overlap + ; HL => Start of source block + ; DE => Start of destiny block + ; BC => Block length + push namespace core +__MEMCPY: + PROC + LOCAL __MEMCPY2 + push hl + add hl, bc ; addr of last source block byte + 1 + or a + sbc hl, de ; checks if DE > HL + BC + pop hl ; recovers HL. If carry => DE > HL + BC (no overlap) + jr c, __MEMCPY2 + ; Now checks if DE <= HL + sbc hl, de ; Even if overlap, if DE < HL then we can LDIR safely + add hl, de + jr nc, __MEMCPY2 + dec bc + add hl, bc + ex de, hl + add hl, bc + ex de, hl + inc bc ; HL and DE point to the last byte position + lddr ; Copies from end to beginning + ret +__MEMCPY2: + ldir + ret + ENDP + pop namespace +#line 127 "/zxbasic/src/lib/arch/zxnext/stdlib/memcopy.bas" + END diff --git a/tests/functional/arch/zxnext/stdlib_memcopy.bas b/tests/functional/arch/zxnext/stdlib_memcopy.bas new file mode 100644 index 000000000..2b2b39bbc --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_memcopy.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zxnext/stdlib_pos.asm b/tests/functional/arch/zxnext/stdlib_pos.asm new file mode 100644 index 000000000..924df1c90 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_pos.asm @@ -0,0 +1,214 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +_pos: +#line 28 "/zxbasic/src/lib/arch/zxnext/stdlib/pos.bas" + push namespace core + PROC + call __LOAD_S_POSN + ld a, e + ENDP + pop namespace +#line 38 "/zxbasic/src/lib/arch/zxnext/stdlib/pos.bas" +_pos__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/in_screen.asm" + push namespace core +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits + PROC + LOCAL __IN_SCREEN_ERR + ld hl, SCR_SIZE + ld a, e + cp l + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld a, d + cp h + ret c ; Return if carry (OK) +__IN_SCREEN_ERR: +__OUT_OF_SCREEN_ERR: + ; Jumps here if out of screen + ld a, ERROR_OutOfScreen + jp __STOP ; Saves error code and exits + ENDP + pop namespace +#line 7 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + push namespace core +__ATTR_ADDR: + ; calc start address in DE (as (32 * d) + e) + ; Contributed by Santiago Romero at http://www.speccy.org + ld h, 0 ; 7 T-States + ld a, d ; 4 T-States + ld d, h + add a, a ; a * 2 ; 4 T-States + add a, a ; a * 4 ; 4 T-States + ld l, a ; HL = A * 4 ; 4 T-States + add hl, hl ; HL = A * 8 ; 15 T-States + add hl, hl ; HL = A * 16 ; 15 T-States + add hl, hl ; HL = A * 32 ; 15 T-States + add hl, de + ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address + add hl, de + ; Return current screen address in HL + ret + ; Sets the attribute at a given screen coordinate (D, E). + ; The attribute is taken from the ATTR_T memory variable + ; Used by PRINT routines +SET_ATTR: + ; Checks for valid coords + call __IN_SCREEN + ret nc + call __ATTR_ADDR +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + ; HL contains the address of the ATTR cell to set + PROC +__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + ld a, d + and (hl) + ld c, a ; C = current screen color, masked + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + ret + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" + ; Printing positioning library. + push namespace core + ; Loads into DE current ROW, COL print position from S_POSN mem var. +__LOAD_S_POSN: + PROC + ld de, (S_POSN) + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + ret + ENDP + ; Saves ROW, COL from DE into S_POSN mem var. +__SAVE_S_POSN: + PROC + ld hl, SCR_SIZE + or a + sbc hl, de + ld (S_POSN), hl ; saves it again +__SET_SCR_PTR: ;; Fast + push de + call __ATTR_ADDR + ld (DFCCL), hl + pop de + ld a, d + ld c, a ; Saves it for later + and 0F8h ; Masks 3 lower bit ; zy + ld d, a + ld a, c ; Recovers it + and 07h ; MOD 7 ; y1 + rrca + rrca + rrca + or e + ld e, a + ld hl, (SCREEN_ADDR) + add hl, de ; HL = Screen address + DE + ld (DFCC), hl + ret + ENDP + pop namespace +#line 43 "/zxbasic/src/lib/arch/zxnext/stdlib/pos.bas" + END diff --git a/tests/functional/arch/zxnext/stdlib_pos.bas b/tests/functional/arch/zxnext/stdlib_pos.bas new file mode 100644 index 000000000..d2ab75b0f --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_pos.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zxnext/stdlib_putchars.asm b/tests/functional/arch/zxnext/stdlib_putchars.asm new file mode 100644 index 000000000..c572e9603 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_putchars.asm @@ -0,0 +1,592 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +_putChars: + push ix + ld ix, 0 + add ix, sp +#line 34 "/zxbasic/src/lib/arch/zxnext/stdlib/putchars.bas" + PROC + LOCAL BLPutChar, BLPutCharColumnLoop, BLPutCharInColumnLoop, BLPutCharSameThird + LOCAL BLPutCharNextThird, BLPutCharNextColumn, BLPutCharsEnd +BLPutChar: + ld a,(ix+5) + ld l,a + ld a,(ix+7) + ld d,a + and 24 + ld h,a + ld a,d + and 7 + rrca + rrca + rrca + or l + ld l,a + push hl + ld e,(ix+12) + ld d,(ix+13) + ld b,(ix+9) + push bc +BLPutCharColumnLoop: + ld b, (ix+11) +BLPutCharInColumnLoop: + push hl + push de + ld de, (.core.SCREEN_ADDR) + add hl, de + pop de + ld a,(de) + ld (hl),a + inc de + inc h + ld a,(de) + ld (hl),a + inc de + inc h + ld a,(de) + ld (hl),a + inc de + inc h + ld a,(de) + ld (hl),a + inc de + inc h + ld a,(de) + ld (hl),a + inc de + inc h + ld a,(de) + ld (hl),a + inc de + inc h + ld a,(de) + ld (hl),a + inc de + inc h + ld a,(de) + ld (hl),a + pop hl + inc de + dec b + jr z, BLPutCharNextColumn + push de + ld a,l + and 224 + cp 224 + jr z,BLPutCharNextThird +BLPutCharSameThird: + ld de,32 + add hl,de + pop de + jp BLPutCharInColumnLoop +BLPutCharNextThird: + ld de,1824 + add hl,de + pop de + jp BLPutCharInColumnLoop +BLPutCharNextColumn: + pop bc + pop hl + dec b + jr z, BLPutCharsEnd + inc l + push hl + push bc + jp BLPutCharColumnLoop +BLPutCharsEnd: + ENDP +#line 148 "/zxbasic/src/lib/arch/zxnext/stdlib/putchars.bas" +_putChars__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + pop bc + pop bc + ex (sp), hl + exx + ret +_getChars: + push ix + ld ix, 0 + add ix, sp +#line 167 "/zxbasic/src/lib/arch/zxnext/stdlib/putchars.bas" + PROC + LOCAL BLGetChar, BLGetCharColumnLoop, BLGetCharInColumnLoop, BLGetCharSameThird + LOCAL BLGetCharNextThird, BLGetCharNextColumn, BLGetCharsEnd +BLGetChar: + ld a,(ix+5) + ld l,a + ld a,(ix+7) + ld d,a + and 24 + ld h,a + ld a,d + and 7 + rrca + rrca + rrca + or l + ld l,a + push hl + ld e,(ix+12) + ld d,(ix+13) + ld b,(ix+9) + push bc +BLGetCharColumnLoop: + ld b, (ix+11) +BLGetCharInColumnLoop: + push hl + push de + ld de, (.core.SCREEN_ADDR) + add hl, de + pop de + ld a,(hl) + ld (de),a + inc de + inc h + ld a,(hl) + ld (de),a + inc de + inc h + ld a,(hl) + ld (de),a + inc de + inc h + ld a,(hl) + ld (de),a + inc de + inc h + ld a,(hl) + ld (de),a + inc de + inc h + ld a,(hl) + ld (de),a + inc de + inc h + ld a,(hl) + ld (de),a + inc de + inc h + ld a,(hl) + ld (de),a + pop hl + inc de + dec b + jr z, BLGetCharNextColumn + push de + ld a,l + and 224 + cp 224 + jr z,BLGetCharNextThird +BLGetCharSameThird: + ld de,32 + add hl,de + pop de + jp BLGetCharInColumnLoop +BLGetCharNextThird: + ld de,1824 + add hl,de + pop de + jp BLGetCharInColumnLoop +BLGetCharNextColumn: + pop bc + pop hl + dec b + jr z, BLGetCharsEnd + inc l + push hl + push bc + jp BLGetCharColumnLoop +BLGetCharsEnd: + ENDP +#line 281 "/zxbasic/src/lib/arch/zxnext/stdlib/putchars.bas" +_getChars__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + pop bc + pop bc + ex (sp), hl + exx + ret +_paint: + push ix + ld ix, 0 + add ix, sp +#line 300 "/zxbasic/src/lib/arch/zxnext/stdlib/putchars.bas" + PROC + LOCAL BLPaintHeightLoop, BLPaintWidthLoop, BLPaintWidthExitLoop, BLPaintHeightExitLoop + ld a,(ix+7) + rrca + rrca + rrca + ld l,a + and 3 + ld h,a + ld a,l + and 224 + ld l,a + ld a,(ix+5) + add a,l + ld l,a + ld de,(.core.SCREEN_ATTR_ADDR) + add hl, de + push hl + ld a, (ix+13) + ld de,32 + ld c,(ix+11) +BLPaintHeightLoop: + ld b,(ix+9) +BLPaintWidthLoop: + ld (hl),a + inc hl + djnz BLPaintWidthLoop +BLPaintWidthExitLoop: + pop hl + dec c + jr z, BLPaintHeightExitLoop + add hl,de + push hl + jp BLPaintHeightLoop +BLPaintHeightExitLoop: + ENDP +#line 345 "/zxbasic/src/lib/arch/zxnext/stdlib/putchars.bas" +_paint__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + pop bc + pop bc + ex (sp), hl + exx + ret +_paintData: + push ix + ld ix, 0 + add ix, sp +#line 365 "/zxbasic/src/lib/arch/zxnext/stdlib/putchars.bas" + PROC + LOCAL BLPaintDataHeightLoop, BLPaintDataWidthLoop, BLPaintDataWidthExitLoop, BLPaintDataHeightExitLoop + ld a,(ix+7) + rrca + rrca + rrca + ld l,a + and 3 + ld h,a + ld a,l + and 224 + ld l,a + ld a,(ix+5) + add a,l + ld l,a + ld de,(.core.SCREEN_ATTR_ADDR) + add hl, de + push hl + ld d,(ix+13) + ld e,(ix+12) + ld c,(ix+11) +BLPaintDataHeightLoop: + ld b,(ix+9) +BLPaintDataWidthLoop: + ld a,(de) + ld (hl),a + inc hl + inc de + djnz BLPaintDataWidthLoop +BLPaintDataWidthExitLoop: + pop hl + dec c + jr z, BLPaintDataHeightExitLoop + push de + ld de,32 + add hl,de + pop de + push hl + jp BLPaintDataHeightLoop +BLPaintDataHeightExitLoop: + ENDP +#line 414 "/zxbasic/src/lib/arch/zxnext/stdlib/putchars.bas" +_paintData__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + pop bc + pop bc + ex (sp), hl + exx + ret +_getPaintData: + push ix + ld ix, 0 + add ix, sp +#line 433 "/zxbasic/src/lib/arch/zxnext/stdlib/putchars.bas" + PROC + LOCAL BLGetPaintDataHeightLoop, BLGetPaintDataWidthLoop, BLGetPaintDataWidthExitLoop, BLGetPaintDataHeightExitLoop + ld a,(ix+7) + rrca + rrca + rrca + ld l,a + and 3 + ld h,a + ld a,l + and 224 + ld l,a + ld a,(ix+5) + add a,l + ld l,a + ld de,(.core.SCREEN_ATTR_ADDR) + add hl, de + push hl + ld d,(ix+13) + ld e,(ix+12) + ld c,(ix+11) +BLGetPaintDataHeightLoop: + ld b,(ix+9) +BLGetPaintDataWidthLoop: + ld a,(hl) + ld (de),a + inc hl + inc de + djnz BLGetPaintDataWidthLoop +BLGetPaintDataWidthExitLoop: + pop hl + dec c + jr z, BLGetPaintDataHeightExitLoop + push de + ld de,32 + add hl,de + pop de + push hl + jp BLGetPaintDataHeightLoop +BLGetPaintDataHeightExitLoop: + ENDP +#line 482 "/zxbasic/src/lib/arch/zxnext/stdlib/putchars.bas" +_getPaintData__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + pop bc + pop bc + ex (sp), hl + exx + ret +_putCharsOverMode: + push ix + ld ix, 0 + add ix, sp +#line 507 "/zxbasic/src/lib/arch/zxnext/stdlib/putchars.bas" + PROC + LOCAL BLPutChar, BLPutCharColumnLoop, BLPutCharInColumnLoop, BLPutCharSameThird + LOCAL BLPutCharNextThird, BLPutCharNextColumn, BLPutCharsEnd + LOCAL op1, op2, op3, op4, op5, op6, op7, op8, opTable, noCarry + ld a,(ix+13) + and 3 + ld hl, opTable + add a, l + jp nc, noCarry + inc h +noCarry: + ld l, a + ld a, (hl) + ld (op1), a + ld (op2), a + ld (op3), a + ld (op4), a + ld (op5), a + ld (op6), a + ld (op7), a + ld (op8), a + jp BLPutChar +opTable: + DEFB $00 + DEFB $AE + DEFB $A6 + DEFB $B6 +BLPutChar: + ld a,(ix+5) + ld l,a + ld a,(ix+7) + ld d,a + and 24 + ld h,a + ld a,d + and 7 + rrca + rrca + rrca + or l + ld l,a + push hl + ld e,(ix+14) + ld d,(ix+15) + ld b,(ix+9) + push bc +BLPutCharColumnLoop: + ld b, (ix+11) +BLPutCharInColumnLoop: + push hl + push de + ld de, (.core.SCREEN_ADDR) + add hl, de + pop de + ld a,(de) +op1: + nop + ld (hl),a + inc de + inc h + ld a,(de) +op2: + nop + ld (hl),a + inc de + inc h + ld a,(de) +op3: + nop + ld (hl),a + inc de + inc h + ld a,(de) +op4: + nop + ld (hl),a + inc de + inc h + ld a,(de) +op5: + nop + ld (hl),a + inc de + inc h + ld a,(de) +op6: + nop + ld (hl),a + inc de + inc h + ld a,(de) +op7: + nop + ld (hl),a + inc de + inc h + ld a,(de) +op8: + nop + ld (hl),a + pop hl + inc de + dec b + jr z, BLPutCharNextColumn + push de + ld a,l + and 224 + cp 224 + jr z,BLPutCharNextThird +BLPutCharSameThird: + ld de,32 + add hl,de + pop de + jp BLPutCharInColumnLoop +BLPutCharNextThird: + ld de,1824 + add hl,de + pop de + jp BLPutCharInColumnLoop +BLPutCharNextColumn: + pop bc + pop hl + dec b + jr z, BLPutCharsEnd + inc l + push hl + push bc + jp BLPutCharColumnLoop +BLPutCharsEnd: + ENDP +#line 661 "/zxbasic/src/lib/arch/zxnext/stdlib/putchars.bas" +_putCharsOverMode__leave: + exx + ld hl, 12 +__EXIT_FUNCTION: + ld sp, ix + pop ix + pop de + add hl, sp + ld sp, hl + push de + exx + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 676 "/zxbasic/src/lib/arch/zxnext/stdlib/putchars.bas" + END diff --git a/tests/functional/arch/zxnext/stdlib_putchars.bas b/tests/functional/arch/zxnext/stdlib_putchars.bas new file mode 100644 index 000000000..30371da29 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_putchars.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zxnext/stdlib_scrbuffer.asm b/tests/functional/arch/zxnext/stdlib_scrbuffer.asm new file mode 100644 index 000000000..cc001e06f --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_scrbuffer.asm @@ -0,0 +1,80 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +_GetScreenBufferAddr: +#line 26 "/zxbasic/src/lib/arch/zxnext/stdlib/scrbuffer.bas" + ld hl, (.core.SCREEN_ADDR) +#line 29 "/zxbasic/src/lib/arch/zxnext/stdlib/scrbuffer.bas" +_GetScreenBufferAddr__leave: + ret +_SetScreenBufferAddr: +#line 44 "/zxbasic/src/lib/arch/zxnext/stdlib/scrbuffer.bas" + ld (.core.SCREEN_ADDR), hl +#line 47 "/zxbasic/src/lib/arch/zxnext/stdlib/scrbuffer.bas" +_SetScreenBufferAddr__leave: + ret +_GetAttrBufferAddr: +#line 57 "/zxbasic/src/lib/arch/zxnext/stdlib/scrbuffer.bas" + ld hl, (.core.SCREEN_ATTR_ADDR) +#line 60 "/zxbasic/src/lib/arch/zxnext/stdlib/scrbuffer.bas" +_GetAttrBufferAddr__leave: + ret +_SetAttrBufferAddr: +#line 75 "/zxbasic/src/lib/arch/zxnext/stdlib/scrbuffer.bas" + ld (.core.SCREEN_ATTR_ADDR), hl +#line 78 "/zxbasic/src/lib/arch/zxnext/stdlib/scrbuffer.bas" +_SetAttrBufferAddr__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 83 "/zxbasic/src/lib/arch/zxnext/stdlib/scrbuffer.bas" + END diff --git a/tests/functional/arch/zxnext/stdlib_scrbuffer.bas b/tests/functional/arch/zxnext/stdlib_scrbuffer.bas new file mode 100644 index 000000000..4ca94c258 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_scrbuffer.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zxnext/stdlib_screen.asm b/tests/functional/arch/zxnext/stdlib_screen.asm new file mode 100644 index 000000000..05c0d3727 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_screen.asm @@ -0,0 +1,638 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 4768 +.core.ZXBASIC_MEM_HEAP: + DEFS 4768 + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +_screen: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl +#line 35 "/zxbasic/src/lib/arch/zxnext/stdlib/screen.bas" + push namespace core + PROC + LOCAL __SCREEN_END + LOCAL __S_SCRNS_BC + LOCAL STK_END + LOCAL RECLAIM2 + __S_SCRNS_BC EQU 2538h + STK_END EQU 5C65h + RECLAIM2 EQU 19E8h + ld bc, 4 + call __MEM_ALLOC + push hl + ld a, h + or l + jr z, __SCREEN_END + ld hl, (STK_END) + push hl + ld b, (ix+7) + ld c, (ix+5) + call __S_SCRNS_BC + call __FPSTACK_POP + pop hl + ld (STK_END), hl + pop hl + push hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld a, (de) + ld (hl), a + ex de, hl + call RECLAIM2 +__SCREEN_END: + pop hl + ld (ix-2), l + ld (ix-1), h + ENDP + pop namespace +#line 91 "/zxbasic/src/lib/arch/zxnext/stdlib/screen.bas" + ld l, (ix-2) + ld h, (ix-1) + call .core.__LOADSTR +_screen__leave: + ex af, af' + exx + ld l, (ix-2) + ld h, (ix-1) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/loadstr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be freed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/heapinit.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; __MEM_INIT must be called to initalize this library with the + ; standard parameters + ; --------------------------------------------------------------------- + push namespace core +__MEM_INIT: ; Initializes the library using (RAMTOP) as start, and + ld hl, ZXBASIC_MEM_HEAP ; Change this with other address of heap start + ld de, ZXBASIC_HEAP_SIZE ; Change this with your size + ; --------------------------------------------------------------------- + ; __MEM_INIT2 initalizes this library +; Parameters: +; HL : Memory address of 1st byte of the memory heap +; DE : Length in bytes of the Memory Heap + ; --------------------------------------------------------------------- +__MEM_INIT2: + ; HL as TOP + PROC + dec de + dec de + dec de + dec de ; DE = length - 4; HL = start + ; This is done, because we require 4 bytes for the empty dummy-header block + xor a + ld (hl), a + inc hl + ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 + inc hl + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block + ld (hl), c + inc hl + ld (hl), b + inc hl ; Pointer to next block + ld (hl), e + inc hl + ld (hl), d + inc hl ; Block size (should be length - 4 at start); This block contains all the available memory + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret + ENDP + pop namespace +#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" + ; --------------------------------------------------------------------- + ; MEM_ALLOC + ; Allocates a block of memory in the heap. + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 + TEMP EQU TEMP0 + 1 + ld hl, 0 + ld (TEMP), hl +__MEM_START: + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start + inc bc + inc bc ; BC = BC + 2 ; block size needs 2 extra bytes for hidden pointer +__MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE + ld a, h ; HL = NULL (No memory available?) + or l +#line 113 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" + ; HL = Pointer to Free block + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = Block Length + push hl ; HL = *pointer to -> next block + ex de, hl + or a ; CF = 0 + sbc hl, bc ; FREE >= BC (Length) (HL = BlockLength - Length) + jp nc, __MEM_DONE + pop hl + ld (TEMP), hl + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + jp __MEM_LOOP +__MEM_DONE: ; A free block has been found. + ; Check if at least 4 bytes remains free (HL >= 4) + push hl + exx ; exx to preserve bc + pop hl + ld bc, 4 + or a + sbc hl, bc + exx + jp nc, __MEM_SUBTRACT + ; At this point... + ; less than 4 bytes remains free. So we return this block entirely + ; We must link the previous block with the next to this one + ; (DE) => Pointer to next block + ; (TEMP) => &(previous->next) + pop hl ; Discard current block pointer + push de + ex de, hl ; DE = Previous block pointer; (HL) = Next block pointer + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex de, hl ; HL = Previous block pointer; DE = Next block pointer +TEMP0: + ld hl, 0 ; Pre-previous block pointer + ld (hl), e + inc hl + ld (hl), d ; LINKED + pop hl ; Returning block. + ret +__MEM_SUBTRACT: + ; At this point we have to store HL value (Length - BC) into (DE - 2) + ex de, hl + dec hl + ld (hl), d + dec hl + ld (hl), e ; Store new block length + add hl, de ; New length + DE => free-block start + pop de ; Remove previous HL off the stack + ld (hl), c ; Store length on its 1st word + inc hl + ld (hl), b + inc hl ; Return hl + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/loadstr.asm" + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + push namespace core +__ILOADSTR: ; This is the indirect pointer entry HL = (HL) + ld a, h + or l + ret z + ld a, (hl) + inc hl + ld h, (hl) + ld l, a +__LOADSTR: ; __FASTCALL__ entry + ld a, h + or l + ret z ; Return if NULL + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(a$) + inc bc + inc bc ; BC = LEN(a$) + 2 (two bytes for length) + push hl + push bc + call __MEM_ALLOC + pop bc ; Recover length + pop de ; Recover origin + ld a, h + or l + ret z ; Return if NULL (No memory) + ex de, hl ; ldir takes HL as source, DE as destiny, so SWAP HL,DE + push de ; Saves destiny start + ldir ; Copies string (length number included) + pop hl ; Recovers destiny in hl as result + ret + pop namespace +#line 113 "/zxbasic/src/lib/arch/zxnext/stdlib/screen.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/free.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- + push namespace core +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN + ld a, h + or l + ret z ; Return if NULL pointer + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous + or a ; Clear carry flag + sbc hl, bc ; Carry if BC > HL => This block if before + add hl, bc ; Restores HL, preserving Carry flag + jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) + inc hl + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 + call __MEM_JOIN_TEST + pop hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; + push hl ; Saves it for later + add hl, bc ; Adds its length. If HL == DE now, it must be joined + or a + sbc hl, de ; If Z, then HL == DE => We must join + pop hl + ret nz +__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC + push hl ; Saves it for later + ex de, hl + ld e, (hl) ; DE -> block->next->length + inc hl + ld d, (hl) + inc hl + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next + pop hl ; Recovers Pointer to block + ld (hl), c + inc hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved + ret + ENDP + pop namespace +#line 115 "/zxbasic/src/lib/arch/zxnext/stdlib/screen.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 116 "/zxbasic/src/lib/arch/zxnext/stdlib/screen.bas" + END diff --git a/tests/functional/arch/zxnext/stdlib_screen.bas b/tests/functional/arch/zxnext/stdlib_screen.bas new file mode 100644 index 000000000..7fb231070 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_screen.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zxnext/stdlib_scroll.asm b/tests/functional/arch/zxnext/stdlib_scroll.asm new file mode 100644 index 000000000..2781ea29a --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_scroll.asm @@ -0,0 +1,962 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +_ScrollRight: +#line 26 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1, LOOP2, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + ld a, h + sub b + ret c + inc a + ld e, a + ld a,c + and 7 + inc a + ld b,a + xor a +MASK1: + rra + scf + djnz MASK1 + ld (AND1+1),a + ld (AND5+1),a + cpl + ld (AND2+1),a + ld (AND7+1),a + ld a,d + and 7 + inc a + ld b,a + xor a +MASK2: + scf + rra + djnz MASK2 + ld (AND4+1),a + ld (AND8+1),a + cpl + ld (AND3+1),a + ld (AND6+1),a + ld a,c + and %11111000 + rrca + rrca + rrca + ld b,a + ld a,d + and %11111000 + rrca + rrca + rrca + sub b + ex af,af' + ld b, h + ld a, 191 + call 22ACh + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ex af,af' + jr z,NEQ1 + dec a + ld d,a +LOOP1: + push hl + ld a,(hl) +AND1: + and %11100000 + ld c,a + ld a,(hl) +AND2: + and %00011111 + rra + rl b + or c + ld (hl),a + inc hl + ld a,d + and a + jr z,COLUMNN + rr b + ld b,a +LOOP2: + rr (hl) + inc hl + djnz LOOP2 + rl b +COLUMNN: + ld a,(hl) +AND3: + and %00000011 + ld c,a + ld a,(hl) + rr b + rra +AND4: + and %11111100 + or c + ld (hl),a + pop hl + dec e + ret z + call SP.PixelDown + jp LOOP1 +NEQ1: + ld b,(hl) + ld a,b +AND5: + and %11100000 + ld c,a + ld a,b +AND6: + and %00000011 + or c + ld c,a + ld a,b +AND7: + and %00011111 + rra +AND8: + and %11111100 + or c + ld (hl),a + dec e + ret z + call SP.PixelDown + jp NEQ1 + ENDP + pop namespace +#line 187 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollRight__leave: + ret +_ScrollLeft: +#line 195 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1, LOOP2, MASK1, MASK2, COLUMN1, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + ld a, h + sub b + ret c + inc a + ld e, a + ld a,c + and 7 + inc a + ld b,a + xor a +MASK1: + rra + scf + djnz MASK1 + ld (AND3+1),a + ld (AND6+1),a + cpl + ld (AND4+1),a + ld (AND8+1),a + ld a,d + and 7 + inc a + ld b,a + xor a +MASK2: + scf + rra + djnz MASK2 + ld (AND2+1),a + ld (AND7+1),a + cpl + ld (AND1+1),a + ld (AND5+1),a + ld a,c + and %11111000 + rrca + rrca + rrca + ld b,a + ld a,d + and %11111000 + rrca + rrca + rrca + sub b + ex af,af' + ld c, d + ld b, h + ld a, 191 + call 22ACh + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ex af,af' + jr z,NEQ1 + dec a + ld d,a +LOOP1: + push hl + ld a,(hl) +AND1: + and %00000011 + ld c,a + ld a,(hl) +AND2: + and %11111100 + rla + rl b + or c + ld (hl),a + dec hl + ld a,d + and a + jr z,COLUMN1 + rr b + ld b,a +LOOP2: + rl (hl) + dec hl + djnz LOOP2 + rl b +COLUMN1: + ld a,(hl) +AND3: + and %11100000 + ld c,a + ld a,(hl) + rr b + rla +AND4: + and %00011111 + or c + ld (hl),a + pop hl + dec e + ret z + call SP.PixelDown + jp LOOP1 +NEQ1: + ld b,(hl) + ld a,b +AND5: + and %00000011 + ld c,a + ld a,b +AND6: + and %11100000 + or c + ld c,a + ld a,b +AND7: + and %11111100 + rla +AND8: + and %00011111 + or c + ld (hl),a + dec e + ret z + call SP.PixelDown + jp NEQ1 + ENDP + pop namespace +#line 357 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollLeft__leave: + ret +_ScrollUp: +#line 366 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + LOCAL EMPTYLINE + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + ld a, h + sub b + ret c + push ix + inc a + ld ixL,a + ld a,c + and 7 + inc a + ld b,a + xor a +MASK1: + rra + scf + djnz MASK1 + ld (AND1+1),a + ld (AND5+1),a + cpl + ld (AND2+1),a + ld (AND7+1),a + ld a,d + and 7 + inc a + ld b,a + xor a +MASK2: + scf + rra + djnz MASK2 + ld (AND4+1),a + ld (AND8+1),a + cpl + ld (AND3+1),a + ld (AND6+1),a + ld a,c + and %11111000 + rrca + rrca + rrca + ld b,a + ld a,d + and %11111000 + rrca + rrca + rrca + sub b + ex af,af' + ld b, h + ld a, 191 + call 22ACh + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ex af,af' + jr z,NEQ1 + dec a + ld ixH,a + ld b,0 +LOOP1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE + call nz,SP.PixelDown + inc ixL + push hl + ld a,(hl) +AND2: + and %00011111 + ld c,a + ld a,(de) +AND1: + and %11100000 + or c + ld (de),a + inc hl + inc de + ld a,ixH + and a + jr z,COLUMNN + ld c,a + ldir +COLUMNN: + ld a,(hl) +AND4: + and %11111100 + ld c,a + ld a,(de) +AND3: + and %00000011 + or c + ld (de),a + pop hl + dec ixL + jp nz,LOOP1 + pop ix + ret +NEQ1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE + call nz,SP.PixelDown + inc ixL + ld a,(hl) +AND7: + and %00011111 +AND8: + and %11111100 + ld c,a + ld a,(de) +AND5: + and %11100000 + ld b,a + ld a,(de) +AND6: + and %00000011 + or b + or c + ld (de),a + dec ixL + jp nz,NEQ1 + pop ix + ret + defs 32,0 +EMPTYLINE: + ld hl,EMPTYLINE-32 + ENDP + pop namespace +#line 537 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollUp__leave: + ret +_ScrollDown: +#line 546 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + LOCAL EMPTYLINE + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + ld a, h + sub b + ret c + push ix + inc a + ld ixL,a + ld h,b + ld a,c + and 7 + inc a + ld b,a + xor a +MASK1: + rra + scf + djnz MASK1 + ld (AND1+1),a + ld (AND5+1),a + cpl + ld (AND2+1),a + ld (AND7+1),a + ld a,d + and 7 + inc a + ld b,a + xor a +MASK2: + scf + rra + djnz MASK2 + ld (AND4+1),a + ld (AND8+1),a + cpl + ld (AND3+1),a + ld (AND6+1),a + ld a,c + and %11111000 + rrca + rrca + rrca + ld b,a + ld a,d + and %11111000 + rrca + rrca + rrca + sub b + ex af,af' + ld b, h + ld a, 191 + call 22ACh + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ex af,af' + jr z,NEQ1 + dec a + ld ixH,a + ld b,0 +LOOP1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE + call nz,SP.PixelUp + inc ixL + push hl + ld a,(hl) +AND2: + and %00011111 + ld c,a + ld a,(de) +AND1: + and %11100000 + or c + ld (de),a + inc hl + inc de + ld a,ixH + and a + jr z,COLUMNN + ld c,a + ldir +COLUMNN: + ld a,(hl) +AND4: + and %11111100 + ld c,a + ld a,(de) +AND3: + and %00000011 + or c + ld (de),a + pop hl + dec ixL + jp nz,LOOP1 + pop ix + ret +NEQ1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE + call nz,SP.PixelUp + inc ixL + ld a,(hl) +AND7: + and %00011111 +AND8: + and %11111100 + ld c,a + ld a,(de) +AND5: + and %11100000 + ld b,a + ld a,(de) +AND6: + and %00000011 + or b + or c + ld (de),a + dec ixL + jp nz,NEQ1 + pop ix + ret + defs 32,0 +EMPTYLINE: + ld hl,EMPTYLINE-32 + ENDP + pop namespace +#line 718 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollDown__leave: + ret +_ScrollRightAligned: +#line 729 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1 + LOCAL LOOP2 + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + srl a + srl a + srl a + inc a + ld e, a + ld a, h + sub b + ret c + inc a + ld d, a + ld b, h + ld a, 191 + LOCAL __PIXEL_ADDR + __PIXEL_ADDR EQU 22ACh + call __PIXEL_ADDR + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc +LOOP1: + push hl + ld b, e + or a +LOOP2: + rr (hl) + inc hl + djnz LOOP2 + pop hl + dec d + ret z + call SP.PixelDown + jp LOOP1 + ENDP + pop namespace +#line 787 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollRightAligned__leave: + ret +_ScrollLeftAligned: +#line 798 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1 + LOCAL LOOP2 + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + srl a + srl a + srl a + inc a + ld e, a + ld a, h + sub b + ret c + ld c, d + inc a + ld d, a + ld b, h + ld a, 191 + LOCAL __PIXEL_ADDR + __PIXEL_ADDR EQU 22ACh + call __PIXEL_ADDR + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc +LOOP1: + push hl + ld b, e + or a +LOOP2: + rl (hl) + dec hl + djnz LOOP2 + pop hl + dec d + ret z + call SP.PixelDown + jp LOOP1 + ENDP + pop namespace +#line 857 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollLeftAligned__leave: + ret +_ScrollUpAligned: +#line 868 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1 + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + srl a + srl a + srl a + inc a + ld e, a + ex af, af' + ld a, h + sub b + ret c + inc a + ld d, a + ld b, h + ld a, 191 + LOCAL __PIXEL_ADDR + __PIXEL_ADDR EQU 22ACh + call __PIXEL_ADDR + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ld a, d + ld b, 0 + exx + ld b, a + ex af, af' + ld c, a + jp LOOP_START +LOOP1: + exx + ld d, h + ld e, l + ld c, a + call SP.PixelDown + push hl + ldir + pop hl + exx + ld a, c + LOCAL LOOP_START +LOOP_START: + djnz LOOP1 + exx + ld (hl), 0 + ld d, h + ld e, l + inc de + ld c, a + dec c + ret z + ldir + ENDP + pop namespace +#line 945 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollUpAligned__leave: + ret +_ScrollDownAligned: +#line 956 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1 + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + srl a + srl a + srl a + inc a + ld e, a + ex af, af' + ld a, h + sub b + ret c + inc a + ld d, a + ld a, 191 + LOCAL __PIXEL_ADDR + __PIXEL_ADDR EQU 22ACh + call __PIXEL_ADDR + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ld a, d + ld b, 0 + exx + ld b, a + ex af, af' + ld c, a + jp LOOP_START +LOOP1: + exx + ld d, h + ld e, l + ld c, a + call SP.PixelUp + push hl + ldir + pop hl + exx + ld a, c + LOCAL LOOP_START +LOOP_START: + djnz LOOP1 + exx + ld (hl), 0 + ld d, h + ld e, l + inc de + ld c, a + dec c + ret z + ldir + ENDP + pop namespace +#line 1033 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +_ScrollDownAligned__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/SP/PixelDown.asm" + ; + ; PixelDown + ; Alvin Albrecht 2002 + ; + ; Pixel Down + ; + ; Adjusts screen address HL to move one pixel down in the display. + ; (0,0) is located at the top left corner of the screen. + ; +; enter: HL = valid screen address +; exit : Carry = moved off screen + ; Carry'= moved off current cell (needs ATTR update) + ; HL = moves one pixel down +; used : AF, HL + push namespace core +SP.PixelDown: + PROC + LOCAL leave + push de + ld de, (SCREEN_ADDR) + or a + sbc hl, de + inc h + ld a,h + and $07 + jr nz, leave + scf ; Sets carry on F', which flags ATTR must be updated + ex af, af' + ld a,h + sub $08 + ld h,a + ld a,l + add a,$20 + ld l,a + jr nc, leave + ld a,h + add a,$08 + ld h,a + cp $19 ; carry = 0 => Out of screen + jr c, leave ; returns if out of screen + ccf + pop de + ret +leave: + add hl, de ; This always sets Carry = 0 + pop de + ret + ENDP + pop namespace +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 58 "/zxbasic/src/lib/arch/zxnext/runtime/SP/PixelDown.asm" +#line 1038 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/SP/PixelUp.asm" + ; + ; PixelUp + ; Alvin Albrecht 2002 + ; + ; Pixel Up + ; + ; Adjusts screen address HL to move one pixel up in the display. + ; (0,0) is located at the top left corner of the screen. + ; +; enter: HL = valid screen address +; exit : Carry = moved off screen + ; HL = moves one pixel up +; used : AF, HL + push namespace core +SP.PixelUp: + PROC + LOCAL leave + push de + ld de, (SCREEN_ADDR) + or a + sbc hl, de + ld a,h + dec h + and $07 + jr nz, leave + scf ; sets C' to 1 (ATTR update needed) + ex af, af' + ld a,$08 + add a,h + ld h,a + ld a,l + sub $20 + ld l,a + jr nc, leave + ld a,h + sub $08 + ld h,a +leave: + push af + add hl, de + pop af + pop de + ret + ENDP + pop namespace +#line 1039 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" + END diff --git a/tests/functional/arch/zxnext/stdlib_scroll.bas b/tests/functional/arch/zxnext/stdlib_scroll.bas new file mode 100644 index 000000000..8c77aa059 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_scroll.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zxnext/stdlib_spectranet.asm b/tests/functional/arch/zxnext/stdlib_spectranet.asm new file mode 100644 index 000000000..739c0e1e4 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_spectranet.asm @@ -0,0 +1,4044 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + call .core.__MEM_INIT + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 4768 +.core.ZXBASIC_MEM_HEAP: + DEFS 4768 + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +_mid: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+4) + ld h, (ix+5) + push hl + ld l, (ix+6) + ld h, (ix+7) + push hl + ld l, (ix+6) + ld h, (ix+7) + push hl + ld l, (ix+8) + ld h, (ix+9) + ex de, hl + pop hl + add hl, de + dec hl + push hl + xor a + call .core.__STRSLICE +_mid__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + ex (sp), hl + exx + ret +_left: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+4) + ld h, (ix+5) + push hl + ld hl, 0 + push hl + ld l, (ix+6) + ld h, (ix+7) + dec hl + push hl + xor a + call .core.__STRSLICE +_left__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_right: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+4) + ld h, (ix+5) + push hl + ld l, (ix+4) + ld h, (ix+5) + call .core.__STRLEN + push hl + ld l, (ix+6) + ld h, (ix+7) + ex de, hl + pop hl + or a + sbc hl, de + push hl + ld hl, 65534 + push hl + xor a + call .core.__STRSLICE +_right__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_strpos2: + push ix + ld ix, 0 + add ix, sp + ld hl, -10 + add hl, sp + ld sp, hl + ld (hl), 0 + ld bc, 9 + ld d, h + ld e, l + inc de + ldir + ld h, (ix+5) + ld l, (ix+4) + ld c, (hl) + inc hl + ld h, (hl) + ld l, c + call .core.__STRLEN + ld (ix-2), l + ld (ix-1), h + ld h, (ix+7) + ld l, (ix+6) + ld c, (hl) + inc hl + ld h, (hl) + ld l, c + call .core.__STRLEN + ld (ix-4), l + ld (ix-3), h + ld l, (ix-4) + ld h, (ix-3) + push hl + ld l, (ix-2) + ld h, (ix-1) + pop de + or a + sbc hl, de + jp nc, .LABEL.__LABEL1 + ld hl, 65535 + jp _strpos2__leave +.LABEL.__LABEL1: + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-4) + ld h, (ix-3) + ex de, hl + pop hl + or a + sbc hl, de + ld (ix-6), l + ld (ix-5), h + ld l, (ix-4) + ld h, (ix-3) + dec hl + ld (ix-8), l + ld (ix-7), h + ld (ix-10), 0 + ld (ix-9), 0 + jp .LABEL.__LABEL2 +.LABEL.__LABEL5: + ld h, (ix+7) + ld l, (ix+6) + ld c, (hl) + inc hl + ld h, (hl) + ld l, c + push hl + ld h, (ix+5) + ld l, (ix+4) + ld c, (hl) + inc hl + ld h, (hl) + ld l, c + push hl + ld l, (ix-10) + ld h, (ix-9) + push hl + ld l, (ix-10) + ld h, (ix-9) + push hl + ld l, (ix-8) + ld h, (ix-7) + ex de, hl + pop hl + add hl, de + push hl + xor a + call .core.__STRSLICE + ex de, hl + pop hl + ld a, 2 + call .core.__STREQ + or a + jp z, .LABEL.__LABEL8 + ld l, (ix-10) + ld h, (ix-9) + jp _strpos2__leave +.LABEL.__LABEL8: +.LABEL.__LABEL6: + ld l, (ix-10) + ld h, (ix-9) + inc hl + ld (ix-10), l + ld (ix-9), h +.LABEL.__LABEL2: + ld l, (ix-10) + ld h, (ix-9) + push hl + ld l, (ix-6) + ld h, (ix-5) + pop de + or a + sbc hl, de + jp nc, .LABEL.__LABEL5 +.LABEL.__LABEL4: + ld hl, 65535 +_strpos2__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_strpos: + push ix + ld ix, 0 + add ix, sp + push ix + pop hl + ld de, 6 + add hl, de + push hl + push ix + pop hl + ld de, 4 + add hl, de + push hl + call _strpos2 +_strpos__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_inStr: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + push ix + pop hl + ld de, 6 + add hl, de + push hl + push ix + pop hl + ld de, 4 + add hl, de + push hl + call _strpos2 + ld (ix-2), l + ld (ix-1), h + ld l, (ix-2) + ld h, (ix-1) + push hl + ld de, 65535 + pop hl + or a + sbc hl, de + ld a, h + or l + sub 1 + sbc a, a + inc a +_inStr__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_ucase2: +#line 125 "/zxbasic/src/lib/arch/zxnext/stdlib/string.bas" + PROC + LOCAL __LOOP + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + ld a, h + or l + ret z + ld c, (hl) + inc hl + ld b, (hl) +__LOOP: + inc hl + ld a, b + or c + ret z + ld a, (hl) + dec bc + cp 'a' + jp c, __LOOP + cp 123 + jp nc, __LOOP + res 5,(hl) + jp __LOOP + ENDP +#line 166 "/zxbasic/src/lib/arch/zxnext/stdlib/string.bas" +_ucase2__leave: + ret +_ucase: + push ix + ld ix, 0 + add ix, sp + push ix + pop hl + ld de, 4 + add hl, de + ld a, 1 + call _ucase2 + ld l, (ix+4) + ld h, (ix+5) + call .core.__LOADSTR +_ucase__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret +_lcase2: +#line 186 "/zxbasic/src/lib/arch/zxnext/stdlib/string.bas" + PROC + LOCAL __LOOP + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + ld a, h + or l + ret z + ld c, (hl) + inc hl + ld b, (hl) +__LOOP: + inc hl + ld a, b + or c + ret z + ld a, (hl) + dec bc + cp 'A' + jp c, __LOOP + cp 91 + jp nc, __LOOP + set 5,(hl) + jp __LOOP + ENDP +#line 227 "/zxbasic/src/lib/arch/zxnext/stdlib/string.bas" +_lcase2__leave: + ret +_lcase: + push ix + ld ix, 0 + add ix, sp + push ix + pop hl + ld de, 4 + add hl, de + ld a, 1 + call _lcase2 + ld l, (ix+4) + ld h, (ix+5) + call .core.__LOADSTR +_lcase__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret +_ltrim: + push ix + ld ix, 0 + add ix, sp + ld hl, -8 + add hl, sp + ld sp, hl + ld (hl), 0 + ld bc, 7 + ld d, h + ld e, l + inc de + ldir + ld l, (ix+6) + ld h, (ix+7) + call .core.__STRLEN + ld (ix-8), l + ld (ix-7), h + ld l, (ix-8) + ld h, (ix-7) + ld a, h + or l + jp nz, .LABEL.__LABEL10 + ld l, (ix+4) + ld h, (ix+5) + call .core.__LOADSTR + jp _ltrim__leave +.LABEL.__LABEL10: + ld l, (ix-8) + ld h, (ix-7) + dec hl + ld (ix-4), l + ld (ix-3), h + ld l, (ix+4) + ld h, (ix+5) + call .core.__STRLEN + ld (ix-6), l + ld (ix-5), h +.LABEL.__LABEL11: + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-6) + ld h, (ix-5) + ex de, hl + pop hl + or a + sbc hl, de + sbc a, a + push af + ld l, (ix+6) + ld h, (ix+7) + push hl + ld l, (ix+4) + ld h, (ix+5) + push hl + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-4) + ld h, (ix-3) + ex de, hl + pop hl + add hl, de + push hl + xor a + call .core.__STRSLICE + ex de, hl + pop hl + ld a, 2 + call .core.__STREQ + ld h, a + pop af + or a + jr z, .LABEL.__LABEL33 + ld a, h +.LABEL.__LABEL33: + or a + jp z, .LABEL.__LABEL12 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-8) + ld h, (ix-7) + ex de, hl + pop hl + add hl, de + ld (ix-2), l + ld (ix-1), h + jp .LABEL.__LABEL11 +.LABEL.__LABEL12: + ld l, (ix+4) + ld h, (ix+5) + push hl + ld l, (ix-2) + ld h, (ix-1) + push hl + ld hl, 65534 + push hl + xor a + call .core.__STRSLICE +_ltrim__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_rtrim: + push ix + ld ix, 0 + add ix, sp + ld hl, -8 + add hl, sp + ld sp, hl + ld (hl), 0 + ld bc, 7 + ld d, h + ld e, l + inc de + ldir + ld l, (ix+6) + ld h, (ix+7) + call .core.__STRLEN + ld (ix-8), l + ld (ix-7), h + ld l, (ix+4) + ld h, (ix+5) + call .core.__STRLEN + ld (ix-6), l + ld (ix-5), h + ld l, (ix-8) + ld h, (ix-7) + ld a, h + or l + sub 1 + sbc a, a + push af + ld l, (ix-6) + ld h, (ix-5) + push hl + ld l, (ix-8) + ld h, (ix-7) + ex de, hl + pop hl + or a + sbc hl, de + sbc a, a + pop de + or d + jp z, .LABEL.__LABEL14 + ld l, (ix+4) + ld h, (ix+5) + call .core.__LOADSTR + jp _rtrim__leave +.LABEL.__LABEL14: + ld l, (ix-8) + ld h, (ix-7) + dec hl + ld (ix-4), l + ld (ix-3), h + ld l, (ix-6) + ld h, (ix-5) + dec hl + ld (ix-2), l + ld (ix-1), h +.LABEL.__LABEL15: + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-4) + ld h, (ix-3) + pop de + call .core.__LEI16 + push af + ld l, (ix+6) + ld h, (ix+7) + push hl + ld l, (ix+4) + ld h, (ix+5) + push hl + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-4) + ld h, (ix-3) + ex de, hl + pop hl + or a + sbc hl, de + push hl + ld l, (ix-2) + ld h, (ix-1) + push hl + xor a + call .core.__STRSLICE + ex de, hl + pop hl + ld a, 2 + call .core.__STREQ + ld h, a + pop af + or a + jr z, .LABEL.__LABEL34 + ld a, h +.LABEL.__LABEL34: + or a + jp z, .LABEL.__LABEL16 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-8) + ld h, (ix-7) + ex de, hl + pop hl + or a + sbc hl, de + ld (ix-2), l + ld (ix-1), h + jp .LABEL.__LABEL15 +.LABEL.__LABEL16: + ld l, (ix-2) + ld h, (ix-1) + push hl + ld de, 0 + pop hl + call .core.__LTI16 + or a + jp z, .LABEL.__LABEL18 + ld hl, .LABEL.__LABEL19 + call .core.__LOADSTR + jp _rtrim__leave +.LABEL.__LABEL18: + ld l, (ix+4) + ld h, (ix+5) + push hl + ld hl, 0 + push hl + ld l, (ix-2) + ld h, (ix-1) + push hl + xor a + call .core.__STRSLICE +_rtrim__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_trim: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+6) + ld h, (ix+7) + call .core.__LOADSTR + push hl + ld l, (ix+6) + ld h, (ix+7) + call .core.__LOADSTR + push hl + ld l, (ix+4) + ld h, (ix+5) + call .core.__LOADSTR + push hl + call _rtrim + push hl + call _ltrim +_trim__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_isdigit: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + inc sp + ld l, (ix+4) + ld h, (ix+5) + push hl + ld hl, 0 + push hl + ld hl, 0 + push hl + xor a + call .core.__STRSLICE + ld a, 1 + call .core.__ASC + ld (ix-1), a + sub 48 + ccf + sbc a, a + push af + ld a, (ix-1) + push af + ld a, 57 + pop hl + sub h + ccf + sbc a, a + ld h, a + pop af + or a + jr z, .LABEL.__LABEL35 + ld a, h +.LABEL.__LABEL35: + sub 1 + sbc a, a + inc a +_isdigit__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret +_isletter: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + inc sp + ld l, (ix+4) + ld h, (ix+5) + push hl + ld hl, 0 + push hl + ld hl, 0 + push hl + xor a + call .core.__STRSLICE + ld a, 1 + call .core.__ASC + ld (ix-1), a + sub 97 + ccf + sbc a, a + push af + ld a, (ix-1) + push af + ld a, 122 + pop hl + sub h + ccf + sbc a, a + ld h, a + pop af + or a + jr z, .LABEL.__LABEL36 + ld a, h +.LABEL.__LABEL36: + push af + ld a, (ix-1) + sub 65 + ccf + sbc a, a + push af + ld a, (ix-1) + push af + ld a, 90 + pop hl + sub h + ccf + sbc a, a + ld h, a + pop af + or a + jr z, .LABEL.__LABEL37 + ld a, h +.LABEL.__LABEL37: + pop de + or d + sub 1 + sbc a, a + inc a +_isletter__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret +_SNETsocket: +#line 59 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld c, a + ld hl, Spectranet.SOCKET + call Spectranet.HLCALL +#line 64 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETsocket__leave: + ret +_SNETbind: +#line 68 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + pop hl + ex (sp), hl + ex de, hl + ld hl, Spectranet.BIND + call Spectranet.HLCALL +#line 75 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETbind__leave: + ret +_SNETlisten: +#line 79 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld hl, Spectranet.LISTEN + call Spectranet.HLCALL +#line 83 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETlisten__leave: + ret +_SNETaccept: +#line 87 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld hl, Spectranet.ACCEPT + call Spectranet.HLCALL +#line 91 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETaccept__leave: + ret +_SNETconnect: +#line 95 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + pop hl + pop de + pop bc + push hl + ld hl, Spectranet.CONNECT + push de + inc de + inc de + call Spectranet.HLCALL + pop hl + ex af, af' + call __MEM_FREE + ex af, af' +#line 110 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETconnect__leave: + ret +_SNETclose: +#line 114 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld hl, Spectranet.CLOSE + call Spectranet.HLCALL +#line 118 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETclose__leave: + ret +_SNETrecv: +#line 122 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + pop hl + pop de + pop bc + push hl + ld hl, Spectranet.RECV + call Spectranet.HLCALL +#line 130 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETrecv__leave: + ret +_SNETsend: +#line 134 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + pop hl + pop de + pop bc + push hl + ld hl, Spectranet.SEND + call Spectranet.HLCALL +#line 142 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETsend__leave: + ret +_SNETpollfd: +#line 146 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld hl, Spectranet.POLLFD + call Spectranet.HLCALL + ld a, 0 + ret z + ld c, a +#line 153 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETpollfd__leave: + ret +_SNETpeekUinteger: +#line 160 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ex de, hl + ld hl, Spectranet.PAGEIN + call Spectranet.HLCALL + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + ld hl, Spectranet.PAGEOUT + call Spectranet.HLCALL + ex de, hl +#line 172 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETpeekUinteger__leave: + ret +_SNETmount: + push ix + ld ix, 0 + add ix, sp + ld hl, -8 + add hl, sp + ld sp, hl + ld (hl), 0 + ld bc, 7 + ld d, h + ld e, l + inc de + ldir + ld hl, -8 + ld de, .LABEL.__LABEL38 + ld bc, 10 + call .core.__ALLOC_LOCAL_ARRAY + ld l, (ix+6) + ld h, (ix+7) + push hl + ld de, .LABEL.__LABEL20 + pop hl + call .core.__ADDSTR + ld d, h + ld e, l + ld bc, 6 + call .core.__PSTORE_STR2 + ld l, (ix+8) + ld h, (ix+9) + push hl + ld de, .LABEL.__LABEL20 + pop hl + call .core.__ADDSTR + ld d, h + ld e, l + ld bc, 8 + call .core.__PSTORE_STR2 + ld l, (ix+10) + ld h, (ix+11) + push hl + ld de, .LABEL.__LABEL20 + pop hl + call .core.__ADDSTR + ld d, h + ld e, l + ld bc, 10 + call .core.__PSTORE_STR2 + ld l, (ix+12) + ld h, (ix+13) + push hl + ld de, .LABEL.__LABEL20 + pop hl + call .core.__ADDSTR + ld d, h + ld e, l + ld bc, 12 + call .core.__PSTORE_STR2 + ld l, (ix+14) + ld h, (ix+15) + push hl + ld de, .LABEL.__LABEL20 + pop hl + call .core.__ADDSTR + ld d, h + ld e, l + ld bc, 14 + call .core.__PSTORE_STR2 + ld l, (ix-6) + ld h, (ix-5) + push hl + push ix + pop hl + ld de, 6 + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + inc hl + inc hl + ex de, hl + pop hl + ld (hl), e + inc hl + ld (hl), d + ld l, (ix-6) + ld h, (ix-5) + inc hl + inc hl + push hl + push ix + pop hl + ld de, 8 + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + inc hl + inc hl + ex de, hl + pop hl + ld (hl), e + inc hl + ld (hl), d + ld l, (ix-6) + ld h, (ix-5) + ld de, 4 + add hl, de + push hl + push ix + pop hl + ld de, 10 + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + inc hl + inc hl + ex de, hl + pop hl + ld (hl), e + inc hl + ld (hl), d + ld l, (ix-6) + ld h, (ix-5) + ld de, 6 + add hl, de + push hl + push ix + pop hl + ld de, 12 + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + inc hl + inc hl + ex de, hl + pop hl + ld (hl), e + inc hl + ld (hl), d + ld l, (ix-6) + ld h, (ix-5) + ld de, 8 + add hl, de + push hl + push ix + pop hl + ld de, 14 + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + inc hl + inc hl + ex de, hl + pop hl + ld (hl), e + inc hl + ld (hl), d + ld hl, 0 + push hl + push ix + pop hl + ld de, -8 + add hl, de + call .core.__ARRAY + ld (ix-2), l + ld (ix-1), h +#line 203 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld a, (ix + 5) + push ix + push hl + pop ix + ld hl, Spectranet.MOUNT + call Spectranet.HLCALL + pop ix +#line 212 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETmount__leave: + ex af, af' + exx + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ld l, (ix+8) + ld h, (ix+9) + call .core.__MEM_FREE + ld l, (ix+10) + ld h, (ix+11) + call .core.__MEM_FREE + ld l, (ix+12) + ld h, (ix+13) + call .core.__MEM_FREE + ld l, (ix+14) + ld h, (ix+15) + call .core.__MEM_FREE + ld l, (ix-6) + ld h, (ix-5) + call .core.__MEM_FREE + ex af, af' + ld hl, 12 +__EXIT_FUNCTION: + ld sp, ix + pop ix + pop de + add hl, sp + ld sp, hl + push de + exx + ret +_SNETcurrMPoint: + push ix + ld ix, 0 + add ix, sp + call .core.COPY_ATTR + ld a, 7 + call .core.INK_TMP + ld a, 2 + call .core.PAPER_TMP + ld hl, 4097 + call _SNETpeekUinteger + ld a, l + call .core.__PRINTU8 + call .core.PRINT_EOL + ld hl, 4097 + call _SNETpeekUinteger + ld a, l +_SNETcurrMPoint__leave: + ld sp, ix + pop ix + ret +_SNETsetmountpt: +#line 228 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld hl, Spectranet.SETMOUNTPOINT + call Spectranet.HLCALL +#line 232 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETsetmountpt__leave: + ret +_SNETumount: +#line 242 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld hl, Spectranet.UMOUNT + call Spectranet.HLCALL +#line 246 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETumount__leave: + ret +_SNETopen: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + ld l, (ix+6) + ld h, (ix+7) + push hl + ld de, .LABEL.__LABEL20 + pop hl + call .core.__ADDSTR + ld d, h + ld e, l + ld bc, 6 + call .core.__PSTORE_STR2 + push ix + pop hl + ld de, 6 + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + inc hl + inc hl + ld (ix-2), l + ld (ix-1), h +#line 268 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld a, (ix + 5) + ld e, (ix + 8) + ld d, (ix + 9) + ld c, (ix + 10) + ld b, (ix + 11) + push ix + ld ix, Spectranet.OPEN + call Spectranet.IXCALL + pop ix +#line 280 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETopen__leave: + ex af, af' + exx + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + pop bc + ex (sp), hl + exx + ret +_SNETfread: +#line 292 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + pop hl + pop de + pop bc + push hl + ld hl, Spectranet.READ + call Spectranet.HLCALL + ld h, b + ld c, l + ret nc + ld (23610), a +#line 304 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETfread__leave: + ret +_SNETfwrite: +#line 316 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + pop hl + pop de + pop bc + push hl + ex de, hl + push ix + ld ix, Spectranet.WRITE + call Spectranet.IXCALL + pop ix + ld h, b + ld c, l + ret nc + ld (23610), a +#line 331 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETfwrite__leave: + ret +_SNETfopen: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + ld hl, .LABEL.__LABEL21 + call .core.__LOADSTR + push hl + ld l, (ix+8) + ld h, (ix+9) + call .core.__LOADSTR + push hl + call _inStr + or a + jp z, .LABEL.__LABEL23 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld de, 1 + pop hl + call .core.__BOR16 + ld (ix-2), l + ld (ix-1), h +.LABEL.__LABEL23: + ld hl, .LABEL.__LABEL24 + call .core.__LOADSTR + push hl + ld l, (ix+8) + ld h, (ix+9) + call .core.__LOADSTR + push hl + call _inStr + or a + jp z, .LABEL.__LABEL26 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld de, 256 + pop hl + call .core.__BOR16 + push hl + ld de, 512 + pop hl + call .core.__BOR16 + push hl + ld de, 2 + pop hl + call .core.__BOR16 + ld (ix-2), l + ld (ix-1), h +.LABEL.__LABEL26: + ld hl, .LABEL.__LABEL27 + call .core.__LOADSTR + push hl + ld l, (ix+8) + ld h, (ix+9) + call .core.__LOADSTR + push hl + call _inStr + or a + jp z, .LABEL.__LABEL29 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld de, 8 + pop hl + call .core.__BOR16 + push hl + ld de, 256 + pop hl + call .core.__BOR16 + push hl + ld de, 2 + pop hl + call .core.__BOR16 + ld (ix-2), l + ld (ix-1), h +.LABEL.__LABEL29: + ld hl, .LABEL.__LABEL30 + call .core.__LOADSTR + push hl + ld l, (ix+8) + ld h, (ix+9) + call .core.__LOADSTR + push hl + call _inStr + or a + jp z, .LABEL.__LABEL32 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld de, 3 + pop hl + call .core.__BOR16 + ld (ix-2), l + ld (ix-1), h +.LABEL.__LABEL32: + ld hl, 438 + push hl + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix+6) + ld h, (ix+7) + call .core.__LOADSTR + push hl + ld a, (ix+5) + push af + call _SNETopen +_SNETfopen__leave: + ex af, af' + exx + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ld l, (ix+8) + ld h, (ix+9) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + ex (sp), hl + exx + ret +_SNETfclose: +#line 366 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld hl, Spectranet.CLOSE + call Spectranet.HLCALL + ret c + xor a +#line 372 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETfclose__leave: + ret +_SNETfseek: +#line 383 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + pop hl + pop bc + ld c, b + pop de + ex (sp), hl + ex de, hl + push ix + ld ix, Spectranet.LSEEK + call Spectranet.IXCALL + pop ix + ret c + xor a +#line 398 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETfseek__leave: + ret +_SNETunlink: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + ld l, (ix+4) + ld h, (ix+5) + push hl + ld de, .LABEL.__LABEL20 + pop hl + call .core.__ADDSTR + ld d, h + ld e, l + ld bc, 4 + call .core.__PSTORE_STR2 + push ix + pop hl + ld de, 4 + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + inc hl + inc hl + ld (ix-2), l + ld (ix-1), h +#line 411 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + PROC + LOCAL CONT + push ix + ld ix, Spectranet.UNLINK + call Spectranet.IXCALL + pop ix + jr c, CONT + xor a +CONT: + ENDP +#line 423 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETunlink__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret +.LABEL.__LABEL19: + DEFW 0000h +.LABEL.__LABEL20: + DEFW 0001h + DEFB 00h +.LABEL.__LABEL21: + DEFW 0001h + DEFB 72h +.LABEL.__LABEL24: + DEFW 0001h + DEFB 77h +.LABEL.__LABEL27: + DEFW 0001h + DEFB 61h +.LABEL.__LABEL30: + DEFW 0001h + DEFB 2Bh + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/array/array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Multiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld a,d ; a = xh + ld d,h ; d = yh + ld h,a ; h = xh + ld c,e ; c = xl + ld b,l ; b = yl + mul d,e ; yh * yl + ex de,hl + mul d,e ; xh * yl + add hl,de ; add cross products + ld e,c + ld d,b + mul d,e ; yl * xl + ld a,l ; cross products lsb + add a,d ; add to msb final + ld h,a + ld l,e ; hl = final + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 20 "/zxbasic/src/lib/arch/zxnext/runtime/array/array.asm" +#line 24 "/zxbasic/src/lib/arch/zxnext/runtime/array/array.asm" + push namespace core +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ;; HL <-- [HL] +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL TMP_ARR_PTR ; Ptr to Array DATA region. Stored temporarily + LOCAL LBOUND_PTR, UBOUND_PTR ; LBound and UBound PTR indexes + LOCAL RET_ADDR ; Contains the return address popped from the stack + LBOUND_PTR EQU 23698 ; Uses MEMBOT as a temporary variable + UBOUND_PTR EQU LBOUND_PTR + 2 ; Next 2 bytes for UBOUND PTR + RET_ADDR EQU UBOUND_PTR + 2 ; Next 2 bytes for RET_ADDR + TMP_ARR_PTR EQU RET_ADDR + 2 ; Next 2 bytes for TMP_ARR_PTR + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE <-- PTR to Dim sizes table + ld (TMP_ARR_PTR), hl ; HL = Array __DATA__.__PTR__ + inc hl + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __LBOUND__ PTR + ld (LBOUND_PTR), bc ; Store it for later +#line 66 "/zxbasic/src/lib/arch/zxnext/runtime/array/array.asm" + ex de, hl ; HL <-- PTR to Dim sizes table, DE <-- dummy + ex (sp), hl ; Return address in HL, PTR Dim sizes table onto Stack + ld (RET_ADDR), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer to Dim sizes table + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Element Offset "accumulator" +LOOP: + ex de, hl ; DE = Element Offset + ld hl, (LBOUND_PTR) + ld a, h + or l + ld b, h + ld c, l + jr z, 1f + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (LBOUND_PTR), hl +1: + pop hl ; Get next index (Ai) from the stack + sbc hl, bc ; Subtract LBOUND +#line 116 "/zxbasic/src/lib/arch/zxnext/runtime/array/array.asm" + add hl, de ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + dec bc ; Decrements loop counter + ld e, (hl) ; Loads next dimension size into D'E' + inc hl + ld d, (hl) + inc hl + push de + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FNMUL ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/array/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 156 "/zxbasic/src/lib/arch/zxnext/runtime/array/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start + ld de, (RET_ADDR) + push de + ret + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 +__FNMUL: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + ENDP + pop namespace +#line 458 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/array/arrayalloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/calloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be freed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/heapinit.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; __MEM_INIT must be called to initalize this library with the + ; standard parameters + ; --------------------------------------------------------------------- + push namespace core +__MEM_INIT: ; Initializes the library using (RAMTOP) as start, and + ld hl, ZXBASIC_MEM_HEAP ; Change this with other address of heap start + ld de, ZXBASIC_HEAP_SIZE ; Change this with your size + ; --------------------------------------------------------------------- + ; __MEM_INIT2 initalizes this library +; Parameters: +; HL : Memory address of 1st byte of the memory heap +; DE : Length in bytes of the Memory Heap + ; --------------------------------------------------------------------- +__MEM_INIT2: + ; HL as TOP + PROC + dec de + dec de + dec de + dec de ; DE = length - 4; HL = start + ; This is done, because we require 4 bytes for the empty dummy-header block + xor a + ld (hl), a + inc hl + ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 + inc hl + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block + ld (hl), c + inc hl + ld (hl), b + inc hl ; Pointer to next block + ld (hl), e + inc hl + ld (hl), d + inc hl ; Block size (should be length - 4 at start); This block contains all the available memory + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret + ENDP + pop namespace +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ; --------------------------------------------------------------------- + ; MEM_ALLOC + ; Allocates a block of memory in the heap. + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 + TEMP EQU TEMP0 + 1 + ld hl, 0 + ld (TEMP), hl +__MEM_START: + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start + inc bc + inc bc ; BC = BC + 2 ; block size needs 2 extra bytes for hidden pointer +__MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE + ld a, h ; HL = NULL (No memory available?) + or l +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" + ; HL = Pointer to Free block + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = Block Length + push hl ; HL = *pointer to -> next block + ex de, hl + or a ; CF = 0 + sbc hl, bc ; FREE >= BC (Length) (HL = BlockLength - Length) + jp nc, __MEM_DONE + pop hl + ld (TEMP), hl + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + jp __MEM_LOOP +__MEM_DONE: ; A free block has been found. + ; Check if at least 4 bytes remains free (HL >= 4) + push hl + exx ; exx to preserve bc + pop hl + ld bc, 4 + or a + sbc hl, bc + exx + jp nc, __MEM_SUBTRACT + ; At this point... + ; less than 4 bytes remains free. So we return this block entirely + ; We must link the previous block with the next to this one + ; (DE) => Pointer to next block + ; (TEMP) => &(previous->next) + pop hl ; Discard current block pointer + push de + ex de, hl ; DE = Previous block pointer; (HL) = Next block pointer + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex de, hl ; HL = Previous block pointer; DE = Next block pointer +TEMP0: + ld hl, 0 ; Pre-previous block pointer + ld (hl), e + inc hl + ld (hl), d ; LINKED + pop hl ; Returning block. + ret +__MEM_SUBTRACT: + ; At this point we have to store HL value (Length - BC) into (DE - 2) + ex de, hl + dec hl + ld (hl), d + dec hl + ld (hl), e ; Store new block length + add hl, de ; New length + DE => free-block start + pop de ; Remove previous HL off the stack + ld (hl), c ; Store length on its 1st word + inc hl + ld (hl), b + inc hl ; Return hl + ret + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/mem/calloc.asm" + ; --------------------------------------------------------------------- + ; MEM_CALLOC + ; Allocates a block of memory in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +__MEM_CALLOC: + push bc + call __MEM_ALLOC + pop bc + ld a, h + or l + ret z ; No memory + ld (hl), 0 + dec bc + ld a, b + or c + ret z ; Already filled (1 byte-length block) + ld d, h + ld e, l + inc de + push hl + ldir + pop hl + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/array/arrayalloc.asm" + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- + push namespace core +__ALLOC_LOCAL_ARRAY: + push de + push ix + pop de + add hl, de ; hl = ix + hl + pop de + ld (hl), e + inc hl + ld (hl), d + inc hl + push hl + call __MEM_CALLOC + pop de + ex de, hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with data whose pointer (PTR) is in the stack + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] = PTR to the element area + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY: + push bc + call __ALLOC_LOCAL_ARRAY + pop bc + ;; Swaps [SP], [SP + 2] + exx + pop hl ; HL <- RET address + ex (sp), hl ; HL <- Data table, [SP] <- RET address + push hl ; [SP] <- Data table + exx + ex (sp), hl ; HL = Data table, (SP) = (IX + HL + 4) - start of array address lbound + ; HL = data table + ; BC = length + ; DE = new data area + ldir + pop hl ; HL = addr of LBound area if used + ret +#line 142 "/zxbasic/src/lib/arch/zxnext/runtime/array/arrayalloc.asm" + pop namespace +#line 459 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/asc.asm" + ; Returns the ascii code for the given str +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/mem/free.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- + push namespace core +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN + ld a, h + or l + ret z ; Return if NULL pointer + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous + or a ; Clear carry flag + sbc hl, bc ; Carry if BC > HL => This block if before + add hl, bc ; Restores HL, preserving Carry flag + jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) + inc hl + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 + call __MEM_JOIN_TEST + pop hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; + push hl ; Saves it for later + add hl, bc ; Adds its length. If HL == DE now, it must be joined + or a + sbc hl, de ; If Z, then HL == DE => We must join + pop hl + ret nz +__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC + push hl ; Saves it for later + ex de, hl + ld e, (hl) ; DE -> block->next->length + inc hl + ld d, (hl) + inc hl + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next + pop hl ; Recovers Pointer to block + ld (hl), c + inc hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved + ret + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/asc.asm" + push namespace core +__ASC: + PROC + LOCAL __ASC_END + ex af, af' ; Saves free_mem flag + ld a, h + or l + ret z ; NULL? return + ld c, (hl) + inc hl + ld b, (hl) + ld a, b + or c + jr z, __ASC_END ; No length? return + inc hl + ld a, (hl) + dec hl +__ASC_END: + dec hl + ex af, af' + or a + call nz, __MEM_FREE ; Free memory if needed + ex af, af' ; Recover result + ret + ENDP + pop namespace +#line 460 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bitwise/bor16.asm" +; vim:ts=4:et: + ; FASTCALL bitwise or 16 version. + ; result in HL +; __FASTCALL__ version (operands: A, H) + ; Performs 16bit or 16bit and returns the boolean +; Input: HL, DE +; Output: HL <- HL OR DE + push namespace core +__BOR16: + ld a, h + or d + ld h, a + ld a, l + or e + ld l, a + ret + pop namespace +#line 461 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lei16.asm" + push namespace core +__LEI16: + PROC + LOCAL checkParity + or a + sbc hl, de + ld a, 1 + ret z + jp po, checkParity + ld a, h + xor 0x80 +checkParity: + ld a, 0 ; False + ret p + inc a ; True + ret + ENDP + pop namespace +#line 462 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lti16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lei8.asm" + push namespace core +__LEI8: ; Signed <= comparison for 8bit int + ; A <= H (registers) + PROC + LOCAL checkParity + sub h + jr nz, __LTI + inc a + ret +__LTI8: ; Test 8 bit values A < H + sub h +__LTI: ; Generic signed comparison + jp po, checkParity + xor 0x80 +checkParity: + ld a, 0 ; False + ret p + inc a ; True + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/cmp/lti16.asm" + push namespace core +__LTI16: ; Test 8 bit values HL < DE + ; Returns result in A: 0 = False, !0 = True + PROC + LOCAL checkParity + or a + sbc hl, de + jp po, checkParity + ld a, h + xor 0x80 +checkParity: + ld a, 0 ; False + ret p + inc a ; True + ret + ENDP + pop namespace +#line 463 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +; vim:ts=4:sw=4:et: + ; PRINT command routine + ; Does not print attribute. Use PRINT_STR or PRINT_NUM for that +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/in_screen.asm" + push namespace core +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits + PROC + LOCAL __IN_SCREEN_ERR + ld hl, SCR_SIZE + ld a, e + cp l + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld a, d + cp h + ret c ; Return if carry (OK) +__IN_SCREEN_ERR: +__OUT_OF_SCREEN_ERR: + ; Jumps here if out of screen + ld a, ERROR_OutOfScreen + jp __STOP ; Saves error code and exits + ENDP + pop namespace +#line 7 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + push namespace core +__ATTR_ADDR: + ; calc start address in DE (as (32 * d) + e) + ; Contributed by Santiago Romero at http://www.speccy.org + ld h, 0 ; 7 T-States + ld a, d ; 4 T-States + ld d, h + add a, a ; a * 2 ; 4 T-States + add a, a ; a * 4 ; 4 T-States + ld l, a ; HL = A * 4 ; 4 T-States + add hl, hl ; HL = A * 8 ; 15 T-States + add hl, hl ; HL = A * 16 ; 15 T-States + add hl, hl ; HL = A * 32 ; 15 T-States + add hl, de + ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address + add hl, de + ; Return current screen address in HL + ret + ; Sets the attribute at a given screen coordinate (D, E). + ; The attribute is taken from the ATTR_T memory variable + ; Used by PRINT routines +SET_ATTR: + ; Checks for valid coords + call __IN_SCREEN + ret nc + call __ATTR_ADDR +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + ; HL contains the address of the ATTR cell to set + PROC +__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + ld a, d + and (hl) + ld c, a ; C = current screen color, masked + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + ret + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" + ; Printing positioning library. + push namespace core + ; Loads into DE current ROW, COL print position from S_POSN mem var. +__LOAD_S_POSN: + PROC + ld de, (S_POSN) + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + ret + ENDP + ; Saves ROW, COL from DE into S_POSN mem var. +__SAVE_S_POSN: + PROC + ld hl, SCR_SIZE + or a + sbc hl, de + ld (S_POSN), hl ; saves it again +__SET_SCR_PTR: ;; Fast + push de + call __ATTR_ADDR + ld (DFCCL), hl + pop de + ld a, d + ld c, a ; Saves it for later + and 0F8h ; Masks 3 lower bit ; zy + ld d, a + ld a, c ; Recovers it + and 07h ; MOD 7 ; y1 + rrca + rrca + rrca + or e + ld e, a + ld hl, (SCREEN_ADDR) + add hl, de ; HL = Screen address + DE + ld (DFCC), hl + ret + ENDP + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/table_jump.asm" + push namespace core +JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A + add a, a +JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE + ld e, a + ld d, 0 +JUMP_HL_PLUS_DE: ; Does JP (HL + DE) + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl +CALL_HL: + jp (hl) + pop namespace +#line 8 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/ink.asm" + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 + ld de, ATTR_P +__SET_INK: + cp 8 + jr nz, __SET_INK2 + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + or 7 ; Set bits 0,1,2 to enable transparency + ld (de), a + ret +__SET_INK2: + ; Another entry. This will set the ink color at location pointer by DE + and 7 ; # Gets color mod 8 + ld b, a ; Saves the color + ld a, (de) + and 0F8h ; Clears previous value + or b + ld (de), a + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + and 0F8h ; Reset bits 0,1,2 sign to disable transparency + ld (de), a ; Store new attr + ret + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + ld de, ATTR_P +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret + ; Another entry. This will set the paper color at location pointer by DE +__SET_PAPER2: + and 7 ; # Remove + rlca + rlca + rlca ; a *= 8 + ld b, a ; Saves the color + ld a, (de) + and 0C7h ; Clears previous value + or b + ld (de), a + inc de ; Points to MASK_T or MASK_P accordingly + ld a, (de) + and 0C7h ; Resets bits 3,4,5 + ld (de), a + ret + ; Sets the PAPER color passed in A register in the ATTR_T variable +PAPER_TMP: + ld de, ATTR_T + jp __SET_PAPER + ENDP + pop namespace +#line 10 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/flash.asm" + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +FLASH: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x80 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 07Fh ; Clears previous value + or b + ld (hl), a + inc hl + res 7, (hl) ;Reset bit 7 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 7, (hl) ;Set bit 7 to enable transparency + ret + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld hl, ATTR_T + jr __SET_FLASH + ENDP + pop namespace +#line 11 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bright.asm" + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +BRIGHT: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x40 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 0BFh ; Clears previous value + or b + ld (hl), a + inc hl + res 6, (hl) ;Reset bit 6 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 6, (hl) ;Set bit 6 to enable transparency + ret + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld hl, ATTR_T + jr __SET_BRIGHT + ENDP + pop namespace +#line 12 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/over.asm" + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register + push namespace core +OVER: + PROC + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + ret + ; Sets OVER flag in P_FLAG temporarily +OVER_TMP: + ld c, a ; saves it for later + and 2 ; gets bit 1; clears carry + rra + ld hl, FLAGS2 + res 0, (hl) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + push namespace core +INVERSE: + PROC + and 1 ; # Convert to 0/1 + add a, a; # Shift left 3 bits for permanent + add a, a + add a, a + ld hl, P_FLAG + res 3, (hl) + or (hl) + ld (hl), a + ret + ; Sets INVERSE flag in P_FLAG temporarily +INVERSE_TMP: + and 1 + add a, a + add a, a; # Shift left 2 bits for temporary + ld hl, P_FLAG + res 2, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bold.asm" + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + push namespace core +BOLD: + PROC + and 1 + rlca + rlca + rlca + ld hl, FLAGS2 + res 3, (HL) + or (hl) + ld (hl), a + ret + ; Sets BOLD flag in P_FLAG temporarily +BOLD_TMP: + and 1 + rlca + rlca + ld hl, FLAGS2 + res 2, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 15 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/italic.asm" + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + push namespace core +ITALIC: + PROC + and 1 + rrca + rrca + rrca + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + ret + ; Sets ITALIC flag in P_FLAG temporarily +ITALIC_TMP: + and 1 + rrca + rrca + rrca + rrca + ld hl, FLAGS2 + res 4, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 16 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + push namespace core +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + ;; Clears ATTR2 flags (OVER 2, etc) + xor a + ld (FLAGS2), a + ld hl, TV_FLAG + res 0, (hl) + LOCAL SET_SCR_ADDR + call __LOAD_S_POSN + jp __SET_SCR_PTR + ;; Receives HL = future value of S_POSN + ;; Stores it at (S_POSN) and refresh screen pointers (ATTR, SCR) +SET_SCR_ADDR: + ld (S_POSN), hl + ex de, hl + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + dec e + jp __SET_SCR_PTR +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A + LOCAL PO_GR_1 + LOCAL __PRCHAR + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + PRINT_JUMP_STATE EQU __PRINT_JUMP + 2 +__PRINT_JUMP: + exx ; Switch to alternative registers + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively +__PRINT_START: +__PRINT_CHR: + cp ' ' + jr c, __PRINT_SPECIAL ; Characters below ' ' are special ones + ex af, af' ; Saves a value (char to print) for later + ld hl, (S_POSN) + dec l + jr nz, 1f + ld l, SCR_COLS - 1 + dec h + jr nz, 2f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 94 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +2: + call SET_SCR_ADDR + jr 4f +1: + ld (S_POSN), hl +4: + ex af, af' + cp 80h ; Is it a "normal" (printable) char + jr c, __SRCADDR + cp 90h ; Is it an UDG? + jr nc, __PRINT_UDG + ; Print an 8 bit pattern (80h to 8Fh) + ld b, a + call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 + ld hl, MEM0 + jp __PRGRAPH + PO_GR_1 EQU 0B38h +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jr __PRGRAPH0 + __SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source +__SRCADDR: + ld bc, (CHARS) +__PRGRAPH0: + add a, a ; A = a * 2 (since a < 80h) ; Thanks to Metalbrain at http://foro.speccy.org + ld l, a + ld h, 0 ; HL = a * 2 (accumulator) + add hl, hl + add hl, hl ; HL = a * 8 + add hl, bc ; HL = CHARS address +__PRGRAPH: + ex de, hl ; HL = Write Address, DE = CHARS address + bit 2, (iy + $47) + call nz, __BOLD +#line 141 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + bit 4, (iy + $47) + call nz, __ITALIC +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ld hl, (DFCC) + push hl + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be source, and HL destiny +PRINT_MODE: ; Which operation is used to write on the screen + ; Set it with: + ; LD A, + ; LD (PRINT_MODE), A + ; + ; Available operations: + ; NORMAL : 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; Set to one of the values above +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + ld (hl), a + inc de + inc h ; Next line + djnz __PRCHAR + pop hl + inc hl + ld (DFCC), hl + ld hl, (DFCCL) ; current ATTR Pos + inc hl + ld (DFCCL), hl + dec hl + call __SET_ATTR + exx + ret + ; ------------- SPECIAL CHARS (< 32) ----------------- +__PRINT_SPECIAL: ; Jumps here if it is a special char + ld hl, __PRINT_TABLE + jp JUMP_HL_PLUS_2A +PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence + exx +__PRINT_0Dh: ; Called WHEN printing CHR$(13) + ld hl, (S_POSN) + dec l + jr nz, 1f + dec h + jr nz, 1f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 211 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +1: + ld l, 1 +__PRINT_EOL_END: + call SET_SCR_ADDR + exx + ret +__PRINT_COM: + exx + push hl + push de + push bc + call PRINT_COMMA + pop bc + pop de + pop hl + ret +__PRINT_TAB: + ld hl, __PRINT_TAB1 + jr __PRINT_SET_STATE +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + jr __PRINT_SET_STATE +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + exx + push hl + push bc + push de + call PRINT_TAB + pop de + pop bc + pop hl + ret +__PRINT_AT: + ld hl, __PRINT_AT1 + jr __PRINT_SET_STATE +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + ld hl, (S_POSN) + ld h, a + ld a, SCR_ROWS + sub h + ld (S_POSN + 1), a + ld hl, __PRINT_AT2 + jr __PRINT_SET_STATE +__PRINT_AT2: + call __LOAD_S_POSN + ld e, a + call __SAVE_S_POSN + jr __PRINT_RESTART +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jr nz, 3f + ld e, SCR_COLS - 2 + dec d + cp d + jr nz, 3f + ld d, SCR_ROWS - 1 +3: + call __SAVE_S_POSN + exx + ret +__PRINT_INK: + ld hl, __PRINT_INK2 + jr __PRINT_SET_STATE +__PRINT_INK2: + call INK_TMP + jr __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jr __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jr __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jr __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jr __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jr __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 355 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 365 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __BOLD +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +1: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz 1b + pop hl + ld de, MEM0 + ret +#line 386 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __ITALIC +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) + inc hl + srl (hl) + inc hl + srl (hl) + inc hl + inc hl + inc hl + sla (hl) + inc hl + sla (hl) + inc hl + sla (hl) + pop hl + ld de, MEM0 + ret +#line 414 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __SCROLL_SCR +#line 488 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + __SCROLL_SCR EQU 0DFEh ; Use ROM SCROLL +#line 490 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 491 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 +PRINT_TAB: + ; Tabulates the number of spaces in A register + ; If the current cursor position is already A, does nothing + PROC + LOCAL LOOP + call __LOAD_S_POSN ; e = current row + sub e + and 31 + ret z + ld b, a +LOOP: + ld a, ' ' + call __PRINTCHAR + djnz LOOP + ret + ENDP +PRINT_AT: ; Changes cursor to ROW, COL + ; COL in A register + ; ROW in stack + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl + call __IN_SCREEN + ret nc ; Return if out of screen + jp __SAVE_S_POSN + LOCAL __PRINT_COM + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_BOLD + LOCAL __PRINT_ITA + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + LOCAL __PRINT_ITA2 +#line 547 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 553 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_TABLE: ; Jump table for 0 .. 22 codes + DW __PRINT_NOP ; 0 + DW __PRINT_NOP ; 1 + DW __PRINT_NOP ; 2 + DW __PRINT_NOP ; 3 + DW __PRINT_NOP ; 4 + DW __PRINT_NOP ; 5 + DW __PRINT_COM ; 6 COMMA + DW __PRINT_NOP ; 7 + DW __PRINT_DEL ; 8 DEL + DW __PRINT_NOP ; 9 + DW __PRINT_NOP ; 10 + DW __PRINT_NOP ; 11 + DW __PRINT_NOP ; 12 + DW __PRINT_0Dh ; 13 + DW __PRINT_BOLD ; 14 + DW __PRINT_ITA ; 15 + DW __PRINT_INK ; 16 + DW __PRINT_PAP ; 17 + DW __PRINT_FLA ; 18 + DW __PRINT_BRI ; 19 + DW __PRINT_INV ; 20 + DW __PRINT_OVR ; 21 + DW __PRINT_AT ; 22 AT + DW __PRINT_TAB ; 23 TAB + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" + push namespace core +COPY_ATTR: + ; Just copies current permanent attribs into temporal attribs + ; and sets print mode + PROC + LOCAL INVERSE1 + LOCAL __REFRESH_TMP + INVERSE1 EQU 02Fh + ld hl, (ATTR_P) + ld (ATTR_T), hl + ld hl, FLAGS2 + call __REFRESH_TMP + ld hl, P_FLAG + call __REFRESH_TMP +__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) + LOCAL TABLE + LOCAL CONT2 + rra ; Over bit to carry + ld a, (FLAGS2) + rla ; Over bit in bit 1, Over2 bit in bit 2 + and 3 ; Only bit 0 and 1 (OVER flag) + ld c, a + ld b, 0 + ld hl, TABLE + add hl, bc + ld a, (hl) + ld (PRINT_MODE), a + ld hl, (P_FLAG) + xor a ; NOP -> INVERSE0 + bit 2, l + jr z, CONT2 + ld a, INVERSE1 ; CPL -> INVERSE1 +CONT2: + ld (INVERSE_MODE), a + ret +TABLE: + nop ; NORMAL MODE + xor (hl) ; OVER 1 MODE + and (hl) ; OVER 2 MODE + or (hl) ; OVER 3 MODE +#line 67 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +__REFRESH_TMP: + ld a, (hl) + and 0b10101010 + ld c, a + rra + or c + ld (hl), a + ret + ENDP + pop namespace +#line 464 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/loadstr.asm" + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + push namespace core +__ILOADSTR: ; This is the indirect pointer entry HL = (HL) + ld a, h + or l + ret z + ld a, (hl) + inc hl + ld h, (hl) + ld l, a +__LOADSTR: ; __FASTCALL__ entry + ld a, h + or l + ret z ; Return if NULL + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(a$) + inc bc + inc bc ; BC = LEN(a$) + 2 (two bytes for length) + push hl + push bc + call __MEM_ALLOC + pop bc ; Recover length + pop de ; Recover origin + ld a, h + or l + ret z ; Return if NULL (No memory) + ex de, hl ; ldir takes HL as source, DE as destiny, so SWAP HL,DE + push de ; Saves destiny start + ldir ; Copies string (length number included) + pop hl ; Recovers destiny in hl as result + ret + pop namespace +#line 466 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printu8.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printi8.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printnum.asm" + push namespace core +__PRINTU_START: + PROC + LOCAL __PRINTU_CONT + ld a, b + or a + jp nz, __PRINTU_CONT + ld a, '0' + jp __PRINT_DIGIT +__PRINTU_CONT: + pop af + push bc + call __PRINT_DIGIT + pop bc + djnz __PRINTU_CONT + ret + ENDP +__PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER must preserve registers + ld a, '-' + jp __PRINT_DIGIT + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printi8.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" + ; -------------------------------- + push namespace core +__DIVU8: ; 8 bit unsigned integer division + ; Divides (Top of stack, High Byte) / A + pop hl ; -------------------------------- + ex (sp), hl ; CALLEE +__DIVU8_FAST: ; Does A / H + ld l, h + ld h, a ; At this point do H / L + ld b, 8 + xor a ; A = 0, Carry Flag = 0 +__DIV8LOOP: + sla h + rla + cp l + jr c, __DIV8NOSUB + sub l + inc h +__DIV8NOSUB: + djnz __DIV8LOOP + ld l, a ; save remainder + ld a, h ; + ret ; a = Quotient, + ; -------------------------------- +__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A + pop hl ; -------------------------------- + ex (sp), hl +__DIVI8_FAST: + ld e, a ; store operands for later + ld c, h + or a ; negative? + jp p, __DIV8A + neg ; Make it positive +__DIV8A: + ex af, af' + ld a, h + or a + jp p, __DIV8B + neg + ld h, a ; make it positive +__DIV8B: + ex af, af' + call __DIVU8_FAST + ld a, c + xor l ; bit 7 of A = 1 if result is negative + ld a, h ; Quotient + ret p ; return if positive + neg + ret +__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) + pop hl + ex (sp), hl ; CALLEE +__MODU8_FAST: ; __FASTCALL__ entry + call __DIVU8_FAST + ld a, l ; Remainder + ret ; a = Modulus +__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) + pop hl + ex (sp), hl ; CALLEE +__MODI8_FAST: ; __FASTCALL__ entry + call __DIVI8_FAST + ld a, l ; remainder + ret ; a = Modulus + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/printi8.asm" + push namespace core +__PRINTI8: ; Prints an 8 bits number in Accumulator (A) + ; Converts 8 to 32 bits + or a + jp p, __PRINTU8 + push af + call __PRINT_MINUS + pop af + neg +__PRINTU8: + PROC + LOCAL __PRINTU_LOOP + ld b, 0 ; Counter +__PRINTU_LOOP: + or a + jp z, __PRINTU_START + push bc + ld h, 10 + call __DIVU8_FAST ; Divides by 10. D'E'H'L' contains modulo (L' since < 10) + pop bc + ld a, l + or '0' ; Stores ASCII digit (must be print in reversed order) + push af + ld a, h + inc b + jp __PRINTU_LOOP ; Uses JP in loops + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printu8.asm" +#line 470 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/pstorestr2.asm" +; vim:ts=4:et:sw=4 + ; + ; Stores an string (pointer to the HEAP by DE) into the address pointed + ; by (IX + BC). No new copy of the string is created into the HEAP, since + ; it's supposed it's already created (temporary string) + ; +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storestr2.asm" + ; Similar to __STORE_STR, but this one is called when + ; the value of B$ if already duplicated onto the stack. + ; So we needn't call STRASSING to create a duplication + ; HL = address of string memory variable + ; DE = address of 2n string. It just copies DE into (HL) + ; freeing (HL) previously. + push namespace core +__PISTORE_STR2: ; Indirect store temporary string at (IX + BC) + push ix + pop hl + add hl, bc +__ISTORE_STR2: + ld c, (hl) ; Dereferences HL + inc hl + ld h, (hl) + ld l, c ; HL = *HL (real string variable address) +__STORE_STR2: + push hl + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = *HL (real string address) + push de + call __MEM_FREE + pop de + pop hl + ld (hl), e + inc hl + ld (hl), d + dec hl ; HL points to mem address variable. This might be useful in the future. + ret + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/pstorestr2.asm" + push namespace core +__PSTORE_STR2: + push ix + pop hl + add hl, bc + jp __STORE_STR2 + pop namespace +#line 471 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/spectranet.inc" + ;The MIT License + ; + ;Copyright (c) 2008 Dylan Smith + ; + ;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. + ; This file can be included in assembly language programs to give + ; symbolic access to the public jump table entry points. + ; Avoid double inclusion + NAMESPACE Spectranet ; NAME PREFIX to avoid naming clash + ; Hardware page-in entry points + MODULECALL equ $3FF8 + MODULECALL_NOPAGE equ $28 + PAGEIN equ $3FF9 + PAGEOUT equ $007C + HLCALL equ $3FFA + IXCALL equ $3FFD + ; Port defines + CTRLREG equ $033B + CPLDINFO equ $023B + ; Jump table entry points + SOCKET equ $3E00 ; Allocate a socket + CLOSE equ $3E03 ; Close a socket + LISTEN equ $3E06 ; Listen for incoming connections + ACCEPT equ $3E09 ; Accept an incoming connection + BIND equ $3E0C ; Bind a local address to a socket + CONNECT equ $3E0F ; Connect to a remote host + SEND equ $3E12 ; Send data + RECV equ $3E15 ; Receive data + SENDTO equ $3E18 ; Send data to an address + RECVFROM equ $3E1B ; Receive data from an address + POLL equ $3E1E ; Poll a list of sockets + POLLALL equ $3E21 ; Poll all open sockets + POLLFD equ $3E24 ; Poll a single socket + GETHOSTBYNAME equ $3E27 ; Look up a hostname + PUTCHAR42 equ $3E2A ; 42 column print write a character + PRINT42 equ $3E2D ; 42 column print a null terminated string + CLEAR42 equ $3E30 ; Clear the screen and reset 42-col print + SETPAGEA equ $3E33 ; Sets page area A + SETPAGEB equ $3E36 ; Sets page area B + LONG2IPSTRING equ $3E39 ; Convert a 4 byte big endian long to an IP + IPSTRING2LONG equ $3E3C ; Convert an IP to a 4 byte big endian long + ITOA8 equ $3E3F ; Convert a byte to ascii + RAND16 equ $3E42 ; 16 bit PRNG + REMOTEADDRESS equ $3E45 ; Fill struct sockaddr_in + IFCONFIG_INET equ $3E48 ; Set IPv4 address + IFCONFIG_NETMASK equ $3E4B ; Set netmask + IFCONFIG_GW equ $3E4E ; Set gateway + INITHW equ $3E51 ; Set the MAC address and initial hw registers + GETHWADDR equ $3E54 ; Read the MAC address + DECONFIG equ $3E57 ; Deconfigure inet, netmask and gateway + MAC2STRING equ $3E5A ; Convert 6 byte MAC address to a string + STRING2MAC equ $3E5D ; Convert a hex string to a 6 byte MAC address + ITOH8 equ $3E60 ; Convert accumulator to hex string + HTOI8 equ $3E63 ; Convert hex string to byte in A + GETKEY equ $3E66 ; Get a key from the keyboard, and put it in A + KEYUP equ $3E69 ; Wait for key release + INPUTSTRING equ $3E6C ; Read a string into buffer at DE + GET_IFCONFIG_INET equ $3E6F ; Gets the current IPv4 address + GET_IFCONFIG_NETMASK equ $3E72 ; Gets the current netmask + GET_IFCONFIG_GW equ $3E75 ; Gets the current gateway address + SETTRAP equ $3E78 ; Sets the programmable trap + DISABLETRAP equ $3E7B ; Disables the programmable trap + ENABLETRAP equ $3E7E ; Enables the programmable trap + PUSHPAGEA equ $3E81 ; Pages a page into area A, pushing the old one + POPPAGEA equ $3E84 ; Restores the previous page in area A + PUSHPAGEB equ $3E87 ; Pages into area B pushing the old one + POPPAGEB equ $3E8A ; Restores the previous page in area B + PAGETRAPRETURN equ $3E8D ; Returns from a trap to page area B + TRAPRETURN equ $3E90 ; Returns from a trap that didn't page area B + ADDBASICEXT equ $3E93 ; Adds a BASIC command + STATEMENT_END equ $3E96 ; Check for statement end, exit at syntax time + EXIT_SUCCESS equ $3E99 ; Use this to exit successfully after cmd + PARSE_ERROR equ $3E9C ; Use this to exit to BASIC with a parse error + RESERVEPAGE equ $3E9F ; Reserve a page of static RAM + FREEPAGE equ $3EA2 ; Free a page of static RAM + REPORTERR equ $3EA5 ; report an error via BASIC + ; Filesystem functions + MOUNT equ $3EA8 + UMOUNT equ $3EAB + OPENDIR equ $3EAE + OPEN equ $3EB1 + UNLINK equ $3EB4 + MKDIR equ $3EB7 + RMDIR equ $3EBA + SIZE equ $3EBD + FREE equ $3EC0 + STAT equ $3EC3 + CHMOD equ $3EC6 + READ equ $3EC9 + WRITE equ $3ECC + LSEEK equ $3ECF + VCLOSE equ $3ED2 + VPOLL equ $3ED5 + READDIR equ $3ED8 + CLOSEDIR equ $3EDB + CHDIR equ $3EDE + GETCWD equ $3EE1 + RENAME equ $3EE4 + SETMOUNTPOINT equ $3EE7 + FREEMOUNTPOINT equ $3EEA + RESALLOC equ $3EED + ; Definitions + ALLOCFD equ 1 + FREEFD equ 0 + ALLOCDIRHND equ 3 + FREEDIRHND equ 2 + ; POLL status bits + BIT_RECV equ 2 + BIT_DISCON equ 1 + BIT_CONN equ 0 + NAMESPACE DEFAULT ; Clears namespace +#line 143 "/zxbasic/src/lib/arch/zxnext/runtime/spectranet.inc" +#line 472 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/strcat.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/strlen.asm" + ; Returns len if a string + ; If a string is NULL, its len is also 0 + ; Result returned in HL + push namespace core +__STRLEN: ; Direct FASTCALL entry + ld a, h + or l + ret z + ld a, (hl) + inc hl + ld h, (hl) ; LEN(str) in HL + ld l, a + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/strcat.asm" + push namespace core +__ADDSTR: ; Implements c$ = a$ + b$ + ; hl = &a$, de = &b$ (pointers) +__STRCAT2: ; This routine creates a new string in dynamic space + ; making room for it. Then copies a$ + b$ into it. + ; HL = a$, DE = b$ + PROC + LOCAL __STR_CONT + LOCAL __STRCATEND + push hl + call __STRLEN + ld c, l + ld b, h ; BC = LEN(a$) + ex (sp), hl ; (SP) = LEN (a$), HL = a$ + push hl ; Saves pointer to a$ + inc bc + inc bc ; +2 bytes to store length + ex de, hl + push hl + call __STRLEN + ; HL = len(b$) + add hl, bc ; Total str length => 2 + len(a$) + len(b$) + ld c, l + ld b, h ; BC = Total str length + 2 + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ + ex (sp), hl ; HL = a$, (SP) = b$ + exx + pop de ; D'E' = b$ + exx + pop bc ; LEN(a$) + ld a, d + or e + ret z ; If no memory: RETURN +__STR_CONT: + push de ; Address of c$ + ld a, h + or l + jr nz, __STR_CONT1 ; If len(a$) != 0 do copy + ; a$ is NULL => uses HL = DE for transfer + ld h, d + ld l, e + ld (hl), a ; This will copy 00 00 at (DE) location + inc de ; + dec bc ; Ensure BC will be set to 1 in the next step +__STR_CONT1: ; Copies a$ (HL) into c$ (DE) + inc bc + inc bc ; BC = BC + 2 + ldir ; MEMCOPY: c$ = a$ + pop hl ; HL = c$ + exx + push de ; Recovers b$; A ex hl,hl' would be very handy + exx + pop de ; DE = b$ +__STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ + ; NOTE: Both DE, BC and AF are modified and lost + ; Returns HL (pointer to a$) + ; a$ Must be NOT NULL + ld a, d + or e + ret z ; Returns if de is NULL (nothing to copy) + push hl ; Saves HL to return it later + ld c, (hl) + inc hl + ld b, (hl) + inc hl + add hl, bc ; HL = end of (a$) string ; bc = len(a$) + push bc ; Saves LEN(a$) for later + ex de, hl ; DE = end of string (Begin of copy addr) + ld c, (hl) + inc hl + ld b, (hl) ; BC = len(b$) + ld a, b + or c + jr z, __STRCATEND; Return if len(b$) == 0 + push bc ; Save LEN(b$) + inc hl ; Skip 2nd byte of len(b$) + ldir ; Concatenate b$ + pop bc ; Recovers length (b$) + pop hl ; Recovers length (a$) + add hl, bc ; HL = LEN(a$) + LEN(b$) = LEN(a$+b$) + ex de, hl ; DE = LEN(a$+b$) + pop hl + ld (hl), e ; Updates new LEN and return + inc hl + ld (hl), d + dec hl + ret +__STRCATEND: + pop hl ; Removes Len(a$) + pop hl ; Restores original HL, so HL = a$ + ret + ENDP + pop namespace +#line 473 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/string.asm" + ; String library + push namespace core +__STR_ISNULL: ; Returns A = FF if HL is 0, 0 otherwise + ld a, h + or l + sub 1 ; Only CARRY if HL is NULL + sbc a, a ; Only FF if HL is NULL (0 otherwise) + ret +__STRCMP: ; Compares strings at HL (a$), DE (b$) + ; Returns 0 if EQual, -1 if HL < DE, +1 if HL > DE + ; A register is preserved and returned in A' + PROC ; __FASTCALL__ + LOCAL __STRCMPZERO + LOCAL __STRCMPEXIT + LOCAL __STRCMPLOOP + LOCAL __EQULEN1 + LOCAL __HLZERO + ex af, af' ; Saves current A register in A' (it's used by STRXX comparison functions) + push hl + call __STRLEN + ld a, h + or l + pop hl + jr z, __HLZERO ; if HL == "", go to __HLZERO + push de + ex de, hl + call __STRLEN + ld a, h + or l + ld a, 1 + ex de, hl ; Recovers HL + pop de + ret z ; Returns +1 if HL != "" AND DE == "" + ld c, (hl) + inc hl + ld b, (hl) + inc hl ; BC = LEN(a$) + push hl ; HL = &a$, saves it + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ex de, hl ; HL = LEN(b$), de = &b$ + ; At this point Carry is cleared, and A reg. = 1 + sbc hl, bc ; Carry if len(a$)[BC] > len(b$)[HL] + ld a, 0 + jr z, __EQULEN1 ; Jumps if len(a$)[BC] = len(b$)[HL] : A = 0 + dec a + jr nc, __EQULEN1 ; Jumps if len(a$)[BC] < len(b$)[HL] : A = 1 + adc hl, bc ; Restores HL + ld a, 1 ; Signals len(a$)[BC] > len(b$)[HL] : A = 1 + ld b, h + ld c, l +__EQULEN1: + pop hl ; Recovers A$ pointer + push af ; Saves A for later (Value to return if strings reach the end) + ld a, b + or c + jr z, __STRCMPZERO ; empty string being compared + ; At this point: BC = lesser length, DE and HL points to b$ and a$ chars respectively +__STRCMPLOOP: + ld a, (de) + cpi + jr nz, __STRCMPEXIT ; (HL) != (DE). Examine carry + jp po, __STRCMPZERO ; END of string (both are equal) + inc de + jp __STRCMPLOOP +__STRCMPZERO: + pop af ; This is -1 if len(a$) < len(b$), +1 if len(b$) > len(a$), 0 otherwise + ret +__STRCMPEXIT: ; Sets A with the following value + dec hl ; Get back to the last char + cp (hl) + sbc a, a ; A = -1 if carry => (DE) < (HL); 0 otherwise (DE) > (HL) + cpl ; A = -1 if (HL) < (DE), 0 otherwise + add a, a ; A = A * 2 (thus -2 or 0) + inc a ; A = A + 1 (thus -1 or 1) + pop bc ; Discard top of the stack + ret +__HLZERO: + ex de, hl + call __STRLEN + ld a, h + or l + ret z ; Returns 0 (EQ) if HL == DE == "" + ld a, -1 + ret ; Returns -1 if HL == "" and DE != "" + ENDP + ; The following routines perform string comparison operations (<, >, ==, etc...) + ; On return, A will contain 0 for False, other value for True + ; Register A' will determine whether the incoming strings (HL, DE) will be freed + ; from dynamic memory on exit: + ; Bit 0 => 1 means HL will be freed. + ; Bit 1 => 1 means DE will be freed. +__STREQ: ; Compares a$ == b$ (HL = ptr a$, DE = ptr b$). Returns FF (True) or 0 (False) + push hl + push de + call __STRCMP + pop de + pop hl + sub 1 + sbc a, a + jp __FREE_STR +__STRNE: ; Compares a$ != b$ (HL = ptr a$, DE = ptr b$). Returns FF (True) or 0 (False) + push hl + push de + call __STRCMP + pop de + pop hl + jp __FREE_STR +__STRLT: ; Compares a$ < b$ (HL = ptr a$, DE = ptr b$). Returns FF (True) or 0 (False) + push hl + push de + call __STRCMP + pop de + pop hl + or a + jp z, __FREE_STR ; Returns 0 if A == B + dec a ; Returns 0 if A == 1 => a$ > b$ + jp __FREE_STR +__STRLE: ; Compares a$ <= b$ (HL = ptr a$, DE = ptr b$). Returns FF (True) or 0 (False) + push hl + push de + call __STRCMP + pop de + pop hl + dec a ; Returns 0 if A == 1 => a$ < b$ + jp __FREE_STR +__STRGT: ; Compares a$ > b$ (HL = ptr a$, DE = ptr b$). Returns FF (True) or 0 (False) + push hl + push de + call __STRCMP + pop de + pop hl + or a + jp z, __FREE_STR ; Returns 0 if A == B + inc a ; Returns 0 if A == -1 => a$ < b$ + jp __FREE_STR +__STRGE: ; Compares a$ >= b$ (HL = ptr a$, DE = ptr b$). Returns FF (True) or 0 (False) + push hl + push de + call __STRCMP + pop de + pop hl + inc a ; Returns 0 if A == -1 => a$ < b$ +__FREE_STR: ; This exit point will test A' for bits 0 and 1 + ; If bit 0 is 1 => Free memory from HL pointer + ; If bit 1 is 1 => Free memory from DE pointer + ; Finally recovers A, to return the result + PROC + LOCAL __FREE_STR2 + LOCAL __FREE_END + ex af, af' + bit 0, a + jr z, __FREE_STR2 + push af + push de + call __MEM_FREE + pop de + pop af +__FREE_STR2: + bit 1, a + jr z, __FREE_END + ex de, hl + call __MEM_FREE +__FREE_END: + ex af, af' + ret + ENDP + pop namespace +#line 474 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/strslice.asm" + ; String slicing library + ; HL = Str pointer + ; DE = String start + ; BC = String character end + ; A register => 0 => the HL pointer wont' be freed from the HEAP + ; e.g. a$(5 TO 10) => HL = a$; DE = 5; BC = 10 + ; This implements a$(X to Y) being X and Y first and + ; last characters respectively. If X > Y, NULL is returned + ; Otherwise returns a pointer to a$ FROM X to Y (starting from 0) + ; if Y > len(a$), then a$ will be padded with spaces (reallocating + ; it in dynamic memory if needed). Returns pointer (HL) to resulting + ; string. NULL (0) if no memory for padding. + ; + push namespace core +__STRSLICE: ; Callee entry + pop hl ; Return ADDRESS + pop bc ; Last char pos + pop de ; 1st char pos + ex (sp), hl ; CALLEE. -> String start +__STRSLICE_FAST: ; __FASTCALL__ Entry + PROC + LOCAL __CONT + LOCAL __EMPTY + LOCAL __FREE_ON_EXIT + push hl ; Stores original HL pointer to be recovered on exit + ex af, af' ; Saves A register for later + push hl + call __STRLEN + inc bc ; Last character position + 1 (string starts from 0) + or a + sbc hl, bc ; Compares length with last char position + jr nc, __CONT ; If Carry => We must copy to end of string + add hl, bc ; Restore back original LEN(a$) in HL + ld b, h + ld c, l ; Copy to the end of str + ccf ; Clears Carry flag for next subtraction +__CONT: + ld h, b + ld l, c ; HL = Last char position to copy (1 for char 0, 2 for char 1, etc) + sbc hl, de ; HL = LEN(a$) - DE => Number of chars to copy + jr z, __EMPTY ; 0 Chars to copy => Return HL = 0 (NULL STR) + jr c, __EMPTY ; If Carry => Nothing to return (NULL STR) + ld b, h + ld c, l ; BC = Number of chars to copy + inc bc + inc bc ; +2 bytes for string length number + push bc + push de + call __MEM_ALLOC + pop de + pop bc + ld a, h + or l + jr z, __EMPTY ; Return if NULL (no memory) + dec bc + dec bc ; Number of chars to copy (Len of slice) + ld (hl), c + inc hl + ld (hl), b + inc hl ; Stores new string length + ex (sp), hl ; Pointer to A$ now in HL; Pointer to new string chars in Stack + inc hl + inc hl ; Skip string length + add hl, de ; Were to start from A$ + pop de ; Start of new string chars + push de ; Stores it again + ldir ; Copies BC chars + pop de + dec de + dec de ; Points to String LEN start + ex de, hl ; Returns it in HL + jr __FREE_ON_EXIT +__EMPTY: ; Return NULL (empty) string + pop hl + ld hl, 0 ; Return NULL +__FREE_ON_EXIT: + ex af, af' ; Recover original A register + ex (sp), hl ; Original HL pointer + or a + call nz, __MEM_FREE + pop hl ; Recover result + ret + ENDP + pop namespace +#line 476 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +.LABEL.__LABEL38: + DEFB 00h + DEFB 00h + DEFB 02h + END diff --git a/tests/functional/arch/zxnext/stdlib_spectranet.bas b/tests/functional/arch/zxnext/stdlib_spectranet.bas new file mode 100644 index 000000000..8a52f0b32 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_spectranet.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zxnext/stdlib_winscroll.asm b/tests/functional/arch/zxnext/stdlib_winscroll.asm new file mode 100644 index 000000000..de8d9584a --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_winscroll.asm @@ -0,0 +1,505 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +_WinScrollRight: +#line 27 "/zxbasic/src/lib/arch/zxnext/stdlib/winscroll.bas" + push namespace core + PROC + LOCAL BucleRows, BucleScans, AfterLDDR, AfterLDDR2, AfterLDDR3 + ld b, a + pop hl + pop de + ld a, d + pop de + add a, d + dec a + ld c, a + ex (sp), hl + ld e, h + ld a, e + or a + ret z + ld a, d + or a + ret z + sub 2 + inc a + ex af,af' + push ix + ld ixL,e + ld l,b + ld h,0 + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl + ld a,l + add a,c + ld l,a + ld de,(SCREEN_ATTR_ADDR) + add hl,de +BucleRows: + push bc + push hl + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + ld b,0 + ex af,af' + ld ixH,7 +BucleScans: + push hl + jr c,AfterLDDR + ld d,h + ld e,l + dec hl + ld c,a + lddr + ex de,hl +AfterLDDR: + ld (hl),b + pop hl + inc h + dec ixH + jp nz,BucleScans + jr c,AfterLDDR2 + ld d,h + ld e,l + dec hl + ld c,a + lddr + ex de,hl +AfterLDDR2: + ld (hl),b + pop hl + jr c,AfterLDDR3 + ld d,h + ld e,l + dec hl + ld c,a + push de + lddr + pop hl +AfterLDDR3: + ex af,af' + ld de,32 + add hl,de + pop bc + inc b + dec ixL + jp nz,BucleRows + pop ix + ENDP + pop namespace +#line 139 "/zxbasic/src/lib/arch/zxnext/stdlib/winscroll.bas" +_WinScrollRight__leave: + ret +_WinScrollLeft: +#line 149 "/zxbasic/src/lib/arch/zxnext/stdlib/winscroll.bas" + push namespace core + PROC + LOCAL BucleRows, BucleScans, AfterLDIR, AfterLDIR2, AfterLDIR3 + ld b, a + pop hl + pop de + ld c, d + pop de + ex (sp), hl + ld e, h + ld a, e + or a + ret z + ld a, d + or a + ret z + sub 2 + inc a + ex af,af' + push ix + ld ixL,e + ld l,b + ld h,0 + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl + ld a,l + add a,c + ld l,a + ld de,(SCREEN_ATTR_ADDR) + add hl,de +BucleRows: + push bc + push hl + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + ld b,0 + ex af,af' + ld ixH,7 +BucleScans: + push hl + jr c,AfterLDIR + ld d,h + ld e,l + inc hl + ld c,a + ldir + ex de,hl +AfterLDIR: + ld (hl),b + pop hl + inc h + dec ixH + jp nz,BucleScans + jr c,AfterLDIR2 + ld d,h + ld e,l + inc hl + ld c,a + ldir + ex de,hl +AfterLDIR2: + ld (hl),b + pop hl + jr c,AfterLDIR3 + ld d,h + ld e,l + inc hl + ld c,a + push de + ldir + pop hl +AfterLDIR3: + ex af,af' + ld de,32 + add hl,de + pop bc + inc b + dec ixL + jp nz,BucleRows + pop ix + ENDP + pop namespace +#line 258 "/zxbasic/src/lib/arch/zxnext/stdlib/winscroll.bas" +_WinScrollLeft__leave: + ret +_WinScrollUp: +#line 268 "/zxbasic/src/lib/arch/zxnext/stdlib/winscroll.bas" + push namespace core + PROC + LOCAL BucleRows, BucleScans, AttrAddress + LOCAL CleanBottomRow, CleanBottomScans, AfterLDIR + ld b, a + pop hl + pop de + ld c, d + pop de + ex (sp), hl + ld e, h + ld a, e + or a + ret z + ld a, d + or a + ret z + ex af,af' + push ix + ld ixL,e + ld l,b + ld h,0 + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl + ld a,l + add a,c + ld l,a + ld de,(SCREEN_ATTR_ADDR) + add hl,de + ld (AttrAddress+1),hl + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + push hl +BucleRows: + dec ixL + jr z,CleanBottomRow + inc b + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + pop de + push hl + push bc + ld b,0 + ex af,af' + ld ixH,7 +BucleScans: + ld c,a + push de + push hl + ldir + pop hl + pop de + inc h + inc d + dec ixH + jp nz,BucleScans + ld c,a + ldir +AttrAddress: + ld hl,AttrAddress + ld d,h + ld e,l + ld c,32 + add hl,bc + ld (AttrAddress+1),hl + ld c,a + ldir + ex af,af' + pop bc + jp BucleRows +CleanBottomRow: + ld b,0 + ex af,af' + ld ixH,8 + pop hl +CleanBottomScans: + ld (hl),b + ld c,a + dec c + jr z,AfterLDIR + push hl + ld d,h + ld e,l + inc de + ldir + pop hl +AfterLDIR: + inc h + dec ixH + jp nz,CleanBottomScans + pop ix + ENDP + pop namespace +#line 402 "/zxbasic/src/lib/arch/zxnext/stdlib/winscroll.bas" +_WinScrollUp__leave: + ret +_WinScrollDown: +#line 412 "/zxbasic/src/lib/arch/zxnext/stdlib/winscroll.bas" + push namespace core + PROC + LOCAL BucleRows, BucleScans, AttrAddress + LOCAL CleanTopRow, CleanTopScans, AfterLDIR + ld b, a + pop hl + pop de + ld c, d + pop de + ex (sp), hl + ld e, h + ld a, b + add a, e + dec a + ld b, a + ld a, e + or a + ret z + ld a, d + or a + ret z + ex af,af' + push ix + ld ixL,e + ld l,b + ld h,0 + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl + ld a,l + add a,c + ld l,a + ld de,(SCREEN_ATTR_ADDR) + add hl,de + ld (AttrAddress+1),hl + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + push hl +BucleRows: + dec ixL + jr z,CleanTopRow + dec b + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + pop de + push hl + push bc + ld b,0 + ex af,af' + ld ixH,7 +BucleScans: + ld c,a + push de + push hl + ldir + pop hl + pop de + inc h + inc d + dec ixH + jp nz,BucleScans + ld c,a + ldir +AttrAddress: + ld hl,AttrAddress + ld d,h + ld e,l + ld c,32 + sbc hl,bc + ld (AttrAddress+1),hl + ld c,a + ldir + ex af,af' + pop bc + jp BucleRows +CleanTopRow: + ld b,0 + ex af,af' + ld ixH,8 + pop hl +CleanTopScans: + ld (hl),b + ld c,a + dec c + jr z,AfterLDIR + push hl + ld d,h + ld e,l + inc de + ldir + pop hl +AfterLDIR: + inc h + dec ixH + jp nz,CleanTopScans + pop ix + ENDP + pop namespace +#line 550 "/zxbasic/src/lib/arch/zxnext/stdlib/winscroll.bas" +_WinScrollDown__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 555 "/zxbasic/src/lib/arch/zxnext/stdlib/winscroll.bas" + END diff --git a/tests/functional/arch/zxnext/stdlib_winscroll.bas b/tests/functional/arch/zxnext/stdlib_winscroll.bas new file mode 100644 index 000000000..35234d39d --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_winscroll.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zxnext/subf00.asm b/tests/functional/arch/zxnext/subf00.asm index 94f097455..f02386b45 100644 --- a/tests/functional/arch/zxnext/subf00.asm +++ b/tests/functional/arch/zxnext/subf00.asm @@ -47,6 +47,13 @@ _b: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/subf.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/subf.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC @@ -86,7 +93,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/subf.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/subf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -105,6 +112,7 @@ __SUBF: ; Subtraction defb 38h; ; END CALC jp __FPSTACK_POP pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/subf.asm" #line 21 "arch/zxnext/subf00.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/pushf.asm" ; Routine to push Float pointed by HL diff --git a/tests/functional/arch/zxnext/subf01.asm b/tests/functional/arch/zxnext/subf01.asm index 0a144d5df..fcbf3310d 100644 --- a/tests/functional/arch/zxnext/subf01.asm +++ b/tests/functional/arch/zxnext/subf01.asm @@ -51,6 +51,13 @@ _b: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/subf.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/subf.asm" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC @@ -90,7 +97,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/subf.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/subf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -109,6 +116,7 @@ __SUBF: ; Subtraction defb 38h; ; END CALC jp __FPSTACK_POP pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/subf.asm" #line 25 "arch/zxnext/subf01.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storef.asm" push namespace core diff --git a/tests/functional/arch/zxnext/subf16c.asm b/tests/functional/arch/zxnext/subf16c.asm index b48b19068..e553a3e4a 100644 --- a/tests/functional/arch/zxnext/subf16c.asm +++ b/tests/functional/arch/zxnext/subf16c.asm @@ -67,6 +67,13 @@ _l: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" ; SUB32 ; Perform TOP of the stack - DEHL ; Pops operand out of the stack (CALLEE) @@ -92,6 +99,7 @@ __SUB32: exx ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" #line 41 "arch/zxnext/subf16c.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" ; Exchanges current DE HL with the diff --git a/tests/functional/arch/zxnext/subi32c.asm b/tests/functional/arch/zxnext/subi32c.asm index 46f18ba50..a2f710ad6 100644 --- a/tests/functional/arch/zxnext/subi32c.asm +++ b/tests/functional/arch/zxnext/subi32c.asm @@ -76,6 +76,13 @@ _l: ret ;; --- end of user code --- #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; ---------------------------------------------------------------- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" ; SUB32 ; Perform TOP of the stack - DEHL ; Pops operand out of the stack (CALLEE) @@ -101,6 +108,7 @@ __SUB32: exx ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" #line 50 "arch/zxnext/subi32c.bas" #line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" ; Exchanges current DE HL with the diff --git a/tests/functional/asm/ld_hl_expr.asm b/tests/functional/asm/ld_hl_expr.asm new file mode 100644 index 000000000..8a155e769 --- /dev/null +++ b/tests/functional/asm/ld_hl_expr.asm @@ -0,0 +1,5 @@ +ld hl, (_Fuente.__DATA__ + 0) - (256) +ld hl, ((_Fuente.__DATA__ + 0)) - (256) +ld hl, ((_Fuente.__DATA__ + 0) - (256)) + +_Fuente.__DATA__: diff --git a/tests/functional/asm/ld_hl_expr.bin b/tests/functional/asm/ld_hl_expr.bin new file mode 100644 index 000000000..d2d775313 --- /dev/null +++ b/tests/functional/asm/ld_hl_expr.bin @@ -0,0 +1 @@ +! ! * \ No newline at end of file diff --git a/tests/functional/asm/rel_include.asm b/tests/functional/asm/rel_include.asm new file mode 100644 index 000000000..ce250e7ce --- /dev/null +++ b/tests/functional/asm/rel_include.asm @@ -0,0 +1,3 @@ +#include once "../asm/rel_include.asm" + +NOP diff --git a/tests/functional/asm/rel_include.bin b/tests/functional/asm/rel_include.bin new file mode 100644 index 000000000..09f370e38 Binary files /dev/null and b/tests/functional/asm/rel_include.bin differ diff --git a/tests/functional/cmdline/test_cmdline.txt b/tests/functional/cmdline/test_cmdline.txt index 3933a993d..14241bd61 100644 --- a/tests/functional/cmdline/test_cmdline.txt +++ b/tests/functional/cmdline/test_cmdline.txt @@ -3,9 +3,9 @@ >>> os.environ['COLUMNS'] = '80' >>> process_file('arch/zx48k/arrbase1.bas', ['-q', '-S', '-O --mmap arrbase1.map']) -usage: zxbc.py [-h] [-d] [-O OPTIMIZE] [-o OUTPUT_FILE] - [-T | -t | -A | -E | --parse-only | -f {asm,bin,ir,sna,tap,tzx,z80}] - [-B] [-a] [-S ORG] [-e STDERR] [--array-base ARRAY_BASE] +usage: zxbc.py [-h] [-d] [-O OPTIMIZE] [-o OUTPUT_FILE] [-T | -t | -A | -E | + --parse-only | -f {asm,bin,ir,sna,tap,tzx,z80}] [-B] [-a] + [-S ORG] [-e STDERR] [--array-base ARRAY_BASE] [--string-base STRING_BASE] [-Z] [-H HEAP_SIZE] [--heap-address HEAP_ADDRESS] [--debug-memory] [--debug-array] [--strict-bool] [--enable-break] [--explicit] [-D DEFINES] @@ -26,11 +26,20 @@ zxbc.py: error: Option --asm and --mmap cannot be used together >>> process_file('asm/no_zxnext.asm') no_zxnext.asm:8: error: Syntax error. Unexpected token 'D' [D] no_zxnext.asm:9: error: Syntax error. Unexpected token 'A' [A] +no_zxnext.asm:10: error: Syntax error. Unexpected token 'DE' [DE] +no_zxnext.asm:11: error: Syntax error. Unexpected token 'BC' [BC] no_zxnext.asm:12: error: Syntax error. Unexpected token '513' [INTEGER] +no_zxnext.asm:13: error: Syntax error. Unexpected token 'DE' [DE] no_zxnext.asm:14: error: Syntax error. Unexpected token 'BC' [BC] no_zxnext.asm:17: error: Syntax error. Unexpected token '17185' [INTEGER] +no_zxnext.asm:18: error: Syntax error. Unexpected token '55' [INTEGER] no_zxnext.asm:19: error: Syntax error. Unexpected token '51' [INTEGER] no_zxnext.asm:23: error: Syntax error. Unexpected token '119' [INTEGER] +no_zxnext.asm:24: error: Syntax error. Unexpected token 'DE' [DE] +no_zxnext.asm:25: error: Syntax error. Unexpected token 'DE' [DE] +no_zxnext.asm:26: error: Syntax error. Unexpected token 'DE' [DE] +no_zxnext.asm:27: error: Syntax error. Unexpected token 'DE' [DE] +no_zxnext.asm:28: error: Syntax error. Unexpected token 'DE' [DE] no_zxnext.asm:29: error: Syntax error. Unexpected token 'C' [C] >>> process_file('asm/no_zxnext.asm', ['-q', '-S', '-O --zxnext']) diff --git a/tests/functional/cmdline/test_errmsg.txt b/tests/functional/cmdline/test_errmsg.txt index 04c984727..7d8ff5458 100644 --- a/tests/functional/cmdline/test_errmsg.txt +++ b/tests/functional/cmdline/test_errmsg.txt @@ -5,6 +5,7 @@ subcall1.bas:6: error: 'test' is a SUB not a FUNCTION >>> process_file('arch/zx48k/subcall2.bas') subcall2.bas:6: error: 'test' is a SUB, not a FUNCTION >>> process_file('arch/zx48k/param3.bas') +param3.bas:5: error: Invalid $ suffix (or sigil) for numeric variable param3.bas:5: error: Function 'test' (previously declared at 3) type mismatch param3.bas:6: error: Type Error: Function must return a numeric value, not a string >>> process_file('arch/zx48k/typecast1.bas') @@ -53,7 +54,7 @@ ifempty0.bas:3: warning: Useless empty IF ignored >>> process_file('arch/zx48k/llb.bas') llb.bas:3: error: Undeclared function "f$" >>> process_file('arch/zx48k/substr_expr_err.bas') -substr_expr_err.bas:3: error: Expected a string type expression. Got byte type instead +substr_expr_err.bas:2: error: Expected a string type expression. Got byte type instead >>> process_file('arch/zx48k/dup_func_decl.bas') dup_func_decl.bas:5: error: duplicated declaration for function 'f' >>> process_file('arch/zx48k/def_func_inline.bas') diff --git a/tests/functional/cmdline/test_warning.txt b/tests/functional/cmdline/test_warning.txt index 18d5f2697..d8aa00f86 100644 --- a/tests/functional/cmdline/test_warning.txt +++ b/tests/functional/cmdline/test_warning.txt @@ -12,3 +12,6 @@ doloop2.bas:4: warning: [W150] Variable 'a' is never used >>> process_file('arch/zx48k/warn_brk.bas', ['-S', '-q', '-O --enable-break']) >>> process_file('arch/zx48k/warn_lbl.bas', ['-S', '-q']) + +>>> process_file('arch/zx48k/letsubstr_undecl.bas', ['-S', '-q']) +letsubstr_undecl.bas:1: warning: [W101] Accessing uninitialized string variable 'S' diff --git a/tests/functional/test.py b/tests/functional/test.py index 086a2ff0b..930f29041 100755 --- a/tests/functional/test.py +++ b/tests/functional/test.py @@ -1,5 +1,11 @@ #!/usr/bin/env python3 -# vim:ts=4:et:ai: + +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import argparse import difflib @@ -94,7 +100,7 @@ def __exit__(self, type_, value, traceback): if self.error_level or not self.keep_file: # command failure or remove file? try: os.unlink(self.fname) - except (OSError, FileNotFoundError): + except OSError, FileNotFoundError: pass # Ok. It might be that it wasn't created @@ -549,7 +555,7 @@ def normalizeDiff(diff: list[str]) -> list[str]: if ext != "bas": continue - options, tfname, ext = _get_testbas_options(fname) + _, tfname, ext = _get_testbas_options(fname) if testBAS(fname, keep_tmp_file=True): try: os.unlink(tfname) diff --git a/tests/functional/test_.py b/tests/functional/test_.py index ea879fa57..95049ddd2 100644 --- a/tests/functional/test_.py +++ b/tests/functional/test_.py @@ -1,4 +1,9 @@ -# vim:ts=4:et:ai +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import os import sys diff --git a/tests/functional/test_asm.py b/tests/functional/test_asm.py index af6eb8a14..39309ebfa 100644 --- a/tests/functional/test_asm.py +++ b/tests/functional/test_asm.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import os import sys diff --git a/tests/functional/test_basic.py b/tests/functional/test_basic.py index 0a8029817..a24c0015c 100644 --- a/tests/functional/test_basic.py +++ b/tests/functional/test_basic.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import glob import os import sys diff --git a/tests/functional/test_prepro.py b/tests/functional/test_prepro.py index c5e2f06eb..b4cb09bce 100644 --- a/tests/functional/test_prepro.py +++ b/tests/functional/test_prepro.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import os import sys diff --git a/tests/functional/zxbpp/builtin.out b/tests/functional/zxbpp/builtin.out index d0ceec53c..3f41ec510 100644 --- a/tests/functional/zxbpp/builtin.out +++ b/tests/functional/zxbpp/builtin.out @@ -1,28 +1,28 @@ -#line 1 "builtin.bi" - - - -#line 5 "builtin.bi" -#line 6 "builtin.bi" - -"builtin.bi" + "a" 7 + 5 -"builtin.bi" + "a" 8 + 5 - -5 + 1 - -"builtin.bi" -13 - - -#line 17 "builtin.bi" -#line 18 "builtin.bi" - -0 -"a" - -5 + 1 - -#line 25 "builtin.bi" -5 + 1 - -0 + "a" "a" + 5 +#line 1 "builtin.bi" + + + +#line 5 "builtin.bi" +#line 6 "builtin.bi" + +"builtin.bi" + "a" 7 + 5 +"builtin.bi" + "a" 8 + 5 + +5 + 1 + +"builtin.bi" +13 + + +#line 17 "builtin.bi" +#line 18 "builtin.bi" + +0 +"a" + +5 + 1 + +#line 25 "builtin.bi" +5 + 1 + +0 + "a" "a" + 5 diff --git a/tests/functional/zxbpp/emook0.out b/tests/functional/zxbpp/emook0.out index b04b111e6..a297f8dae 100644 --- a/tests/functional/zxbpp/emook0.out +++ b/tests/functional/zxbpp/emook0.out @@ -1,9 +1,9 @@ -#line 1 "emook0.bi" - - -ASM - - LD A,A - LD BC,254 -#line 7 -end Asm +#line 1 "emook0.bi" + + +ASM + + LD A,A + LD BC,254 +#line 7 +end Asm diff --git a/tests/functional/zxbpp/iflogic.out b/tests/functional/zxbpp/iflogic.out index e81ffc7be..7e1427ea9 100644 --- a/tests/functional/zxbpp/iflogic.out +++ b/tests/functional/zxbpp/iflogic.out @@ -1,18 +1,18 @@ -#line 1 "iflogic.bi" - -#line 3 "iflogic.bi" -#line 4 "iflogic.bi" - - -PRINT "Or works" -#line 8 "iflogic.bi" - - -PRINT "And works" -#line 12 "iflogic.bi" - - -PRINT "Parenthesis works" -#line 16 "iflogic.bi" - -#line 20 "iflogic.bi" +#line 1 "iflogic.bi" + +#line 3 "iflogic.bi" +#line 4 "iflogic.bi" + + +PRINT "Or works" +#line 8 "iflogic.bi" + + +PRINT "And works" +#line 12 "iflogic.bi" + + +PRINT "Parenthesis works" +#line 16 "iflogic.bi" + +#line 20 "iflogic.bi" diff --git a/tests/functional/zxbpp/init_dot.out b/tests/functional/zxbpp/init_dot.out index 422b07b01..a30ea1f9a 100644 --- a/tests/functional/zxbpp/init_dot.out +++ b/tests/functional/zxbpp/init_dot.out @@ -1,11 +1,11 @@ -#line 1 "init_dot.bi" - -#init "XX.main" - -jp XX.main -push namespace XX -main: - nop - -pop namespace -ret +#line 1 "init_dot.bi" + +#init "XX.main" + +jp XX.main +push namespace XX +main: + nop + +pop namespace +ret diff --git a/tests/functional/zxbpp/line_asm.out b/tests/functional/zxbpp/line_asm.out index bd9d6ec71..f3e4d721e 100644 --- a/tests/functional/zxbpp/line_asm.out +++ b/tests/functional/zxbpp/line_asm.out @@ -1,4 +1,4 @@ -#line 1 "line_asm.bi" -ASM -NOP -END ASM +#line 1 "line_asm.bi" +ASM +NOP +END ASM diff --git a/tests/functional/zxbpp/once.out b/tests/functional/zxbpp/once.out index ddfee5fe1..d639f4c4a 100644 --- a/tests/functional/zxbpp/once.out +++ b/tests/functional/zxbpp/once.out @@ -1,8 +1,8 @@ -#line 1 "once.bi" - - -DIM a as Ubyte - - - -DIM b as Ubyte +#line 1 "once.bi" + + +DIM a as Ubyte + + + +DIM b as Ubyte diff --git a/tests/functional/zxbpp/once_base.out b/tests/functional/zxbpp/once_base.out index 9f0d4c4ab..cbdb6a01b 100644 --- a/tests/functional/zxbpp/once_base.out +++ b/tests/functional/zxbpp/once_base.out @@ -1,12 +1,12 @@ -#line 1 "once_base.bi" - -#line 1 "once.bi" - - -DIM a as Ubyte - - - -DIM b as Ubyte -#line 3 "once_base.bi" - +#line 1 "once_base.bi" + +#line 1 "once.bi" + + +DIM a as Ubyte + + + +DIM b as Ubyte +#line 3 "once_base.bi" + diff --git a/tests/functional/zxbpp/other_arch.bi b/tests/functional/zxbpp/other_arch.bi new file mode 100644 index 000000000..591bf4b20 --- /dev/null +++ b/tests/functional/zxbpp/other_arch.bi @@ -0,0 +1,2 @@ +#include once [arch:zx48k] +#include once [arch:zxnext] diff --git a/tests/functional/zxbpp/other_arch.out b/tests/functional/zxbpp/other_arch.out new file mode 100644 index 000000000..5b6695f33 --- /dev/null +++ b/tests/functional/zxbpp/other_arch.out @@ -0,0 +1,255 @@ +#line 1 "other_arch.bi" +#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" + + + + + + + + + + + +#line 13 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" + +#pragma push(case_insensitive) +#pragma case_insensitive = TRUE + + + + + + + + + + + +function attr(byval row as ubyte, byval col as ubyte) as ubyte + asm + push namespace core + + PROC + LOCAL __ATTR_END + + ld e, (ix+7) + ld d, (ix+5) + + + call __IN_SCREEN + jr nc, __ATTR_END + + call __ATTR_ADDR + ld a, (hl) + +__ATTR_END: + ENDP + + pop namespace + end asm + +end function + + + + + + + + + + + + + + +sub setattr(byval row as ubyte, byval col as ubyte, byval value as ubyte) + asm + push namespace core + + PROC + LOCAL __ATTR_END + + ld e, (ix+7) + ld d, (ix+5) + + + call __IN_SCREEN + jr nc, __ATTR_END + + call __ATTR_ADDR + ld a, (ix+9) + ld (hl), a + +__ATTR_END: + ENDP + + pop namespace + end asm + +end sub + + + + + + + + + + + +function fastcall attraddr(byval row as ubyte, byval col as ubyte) as uinteger + asm + push namespace core + + pop hl + ex (sp), hl + ld d, a + ld e, h + jp __ATTR_ADDR + pop namespace + end asm +end function + + + +#pragma pop(case_insensitive) + + +#require "attr.asm" + + +#require "in_screen.asm" + +#line 125 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" +#line 2 "other_arch.bi" +#line 1 "/zxbasic/src/lib/arch/zxnext/stdlib/hex.bas" + + + + + + + +#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" + + + + + + + + + + + +#line 13 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" + +#pragma push(case_insensitive) +#pragma case_insensitive = TRUE + +#pragma push(string_base) +#pragma string_base = 0 + + + + + + + + + + +function FASTCALL hex(num as ULong) as String + asm + push namespace core + PROC + LOCAL SUB_CHAR + LOCAL SUB_CHAR2 + LOCAL END_CHAR + LOCAL DIGIT + + push hl + push de + ld bc,10 + call __MEM_ALLOC + ld a, h + or l + pop de + pop bc + ret z + + push hl + ld (hl), 8 + inc hl + ld (hl), 0 + inc hl + + call DIGIT + ld d, e + call DIGIT + ld d, b + call DIGIT + ld d, c + call DIGIT + pop hl + ret + +DIGIT: + ld a, d + call SUB_CHAR + ld a, d + jr SUB_CHAR2 + +SUB_CHAR: + rrca + rrca + rrca + rrca + +SUB_CHAR2: + and 0Fh + add a, '0' + cp '9' + 1 + jr c, END_CHAR + add a, 7 + +END_CHAR: + ld (hl), a + inc hl + ret + + ENDP + pop namespace + end asm +end function + + + +function hex16(n as UInteger) as String + Dim a$ as String + a$ = hex(n) + return a$(4 TO 7) +end function + + + +Function hex8 (n as UByte) as String + Dim res$ as String + + res$ = hex(n) + return res$(6 TO 7) +end function + + +#pragma pop(string_base) +#pragma pop(case_insensitive) + + +#require "mem/alloc.asm" + +#line 118 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" +#line 9 "/zxbasic/src/lib/arch/zxnext/stdlib/hex.bas" +#line 3 "other_arch.bi" diff --git a/tests/functional/zxbpp/prepro00.out b/tests/functional/zxbpp/prepro00.out index 7b2942837..af0893179 100644 --- a/tests/functional/zxbpp/prepro00.out +++ b/tests/functional/zxbpp/prepro00.out @@ -1,128 +1,128 @@ -#line 1 "prepro00.bi" -#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" - - - - - - - - - - - -#line 13 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" - -#pragma push(case_insensitive) -#pragma case_insensitive = TRUE - - - - - - - - - - - -function attr(byval row as ubyte, byval col as ubyte) as ubyte - asm - push namespace core - - PROC - LOCAL __ATTR_END - - ld e, (ix+7) - ld d, (ix+5) - - - call __IN_SCREEN - jr nc, __ATTR_END - - call __ATTR_ADDR - ld a, (hl) - -__ATTR_END: - ENDP - - pop namespace - end asm - -end function - - - - - - - - - - - - - - -sub setattr(byval row as ubyte, byval col as ubyte, byval value as ubyte) - asm - push namespace core - - PROC - LOCAL __ATTR_END - - ld e, (ix+7) - ld d, (ix+5) - - - call __IN_SCREEN - jr nc, __ATTR_END - - call __ATTR_ADDR - ld a, (ix+9) - ld (hl), a - -__ATTR_END: - ENDP - - pop namespace - end asm - -end sub - - - - - - - - - - - -function fastcall attraddr(byval row as ubyte, byval col as ubyte) as uinteger - asm - push namespace core - - pop hl - ex (sp), hl - ld d, a - ld e, h - jp __ATTR_ADDR - pop namespace - end asm -end function - - - -#pragma pop(case_insensitive) - - -#require "attr.asm" - - -#require "in_screen.asm" - -#line 125 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" -#line 2 "prepro00.bi" -PRINT "HELLO" +#line 1 "prepro00.bi" +#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" + + + + + + + + + + + +#line 13 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" + +#pragma push(case_insensitive) +#pragma case_insensitive = TRUE + + + + + + + + + + + +function attr(byval row as ubyte, byval col as ubyte) as ubyte + asm + push namespace core + + PROC + LOCAL __ATTR_END + + ld e, (ix+7) + ld d, (ix+5) + + + call __IN_SCREEN + jr nc, __ATTR_END + + call __ATTR_ADDR + ld a, (hl) + +__ATTR_END: + ENDP + + pop namespace + end asm + +end function + + + + + + + + + + + + + + +sub setattr(byval row as ubyte, byval col as ubyte, byval value as ubyte) + asm + push namespace core + + PROC + LOCAL __ATTR_END + + ld e, (ix+7) + ld d, (ix+5) + + + call __IN_SCREEN + jr nc, __ATTR_END + + call __ATTR_ADDR + ld a, (ix+9) + ld (hl), a + +__ATTR_END: + ENDP + + pop namespace + end asm + +end sub + + + + + + + + + + + +function fastcall attraddr(byval row as ubyte, byval col as ubyte) as uinteger + asm + push namespace core + + pop hl + ex (sp), hl + ld d, a + ld e, h + jp __ATTR_ADDR + pop namespace + end asm +end function + + + +#pragma pop(case_insensitive) + + +#require "attr.asm" + + +#require "in_screen.asm" + +#line 125 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" +#line 2 "prepro00.bi" +PRINT "HELLO" diff --git a/tests/functional/zxbpp/prepro01.out b/tests/functional/zxbpp/prepro01.out index 38fe92ced..bcc03a728 100644 --- a/tests/functional/zxbpp/prepro01.out +++ b/tests/functional/zxbpp/prepro01.out @@ -1,129 +1,129 @@ -#line 1 "prepro01.bi" -#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" - - - - - - - - - - - -#line 13 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" - -#pragma push(case_insensitive) -#pragma case_insensitive = TRUE - - - - - - - - - - - -function attr(byval row as ubyte, byval col as ubyte) as ubyte - asm - push namespace core - - PROC - LOCAL __ATTR_END - - ld e, (ix+7) - ld d, (ix+5) - - - call __IN_SCREEN - jr nc, __ATTR_END - - call __ATTR_ADDR - ld a, (hl) - -__ATTR_END: - ENDP - - pop namespace - end asm - -end function - - - - - - - - - - - - - - -sub setattr(byval row as ubyte, byval col as ubyte, byval value as ubyte) - asm - push namespace core - - PROC - LOCAL __ATTR_END - - ld e, (ix+7) - ld d, (ix+5) - - - call __IN_SCREEN - jr nc, __ATTR_END - - call __ATTR_ADDR - ld a, (ix+9) - ld (hl), a - -__ATTR_END: - ENDP - - pop namespace - end asm - -end sub - - - - - - - - - - - -function fastcall attraddr(byval row as ubyte, byval col as ubyte) as uinteger - asm - push namespace core - - pop hl - ex (sp), hl - ld d, a - ld e, h - jp __ATTR_ADDR - pop namespace - end asm -end function - - - -#pragma pop(case_insensitive) - - -#require "attr.asm" - - -#require "in_screen.asm" - -#line 125 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" -#line 2 "prepro01.bi" - -PRINT "HOLA" +#line 1 "prepro01.bi" +#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" + + + + + + + + + + + +#line 13 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" + +#pragma push(case_insensitive) +#pragma case_insensitive = TRUE + + + + + + + + + + + +function attr(byval row as ubyte, byval col as ubyte) as ubyte + asm + push namespace core + + PROC + LOCAL __ATTR_END + + ld e, (ix+7) + ld d, (ix+5) + + + call __IN_SCREEN + jr nc, __ATTR_END + + call __ATTR_ADDR + ld a, (hl) + +__ATTR_END: + ENDP + + pop namespace + end asm + +end function + + + + + + + + + + + + + + +sub setattr(byval row as ubyte, byval col as ubyte, byval value as ubyte) + asm + push namespace core + + PROC + LOCAL __ATTR_END + + ld e, (ix+7) + ld d, (ix+5) + + + call __IN_SCREEN + jr nc, __ATTR_END + + call __ATTR_ADDR + ld a, (ix+9) + ld (hl), a + +__ATTR_END: + ENDP + + pop namespace + end asm + +end sub + + + + + + + + + + + +function fastcall attraddr(byval row as ubyte, byval col as ubyte) as uinteger + asm + push namespace core + + pop hl + ex (sp), hl + ld d, a + ld e, h + jp __ATTR_ADDR + pop namespace + end asm +end function + + + +#pragma pop(case_insensitive) + + +#require "attr.asm" + + +#require "in_screen.asm" + +#line 125 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" +#line 2 "prepro01.bi" + +PRINT "HOLA" diff --git a/tests/functional/zxbpp/prepro02.out b/tests/functional/zxbpp/prepro02.out index 68aa48ce2..3c3d5c7b9 100644 --- a/tests/functional/zxbpp/prepro02.out +++ b/tests/functional/zxbpp/prepro02.out @@ -1,8 +1,8 @@ -#line 1 "prepro02.bi" - - - - -#line 6 "prepro02.bi" - - +#line 1 "prepro02.bi" + + + + +#line 6 "prepro02.bi" + + diff --git a/tests/functional/zxbpp/prepro03.out b/tests/functional/zxbpp/prepro03.out index 29c0d0931..c4b2fe796 100644 --- a/tests/functional/zxbpp/prepro03.out +++ b/tests/functional/zxbpp/prepro03.out @@ -1,9 +1,9 @@ -#line 1 "prepro03.bi" - -#line 4 "prepro03.bi" -#line 5 "prepro03.bi" - -x - this line must be compiled -#line 7 -y +#line 1 "prepro03.bi" + +#line 4 "prepro03.bi" +#line 5 "prepro03.bi" + +x + this line must be compiled +#line 7 +y diff --git a/tests/functional/zxbpp/prepro04.out b/tests/functional/zxbpp/prepro04.out index 99b4448a6..ac005c346 100644 --- a/tests/functional/zxbpp/prepro04.out +++ b/tests/functional/zxbpp/prepro04.out @@ -1,6 +1,6 @@ -#line 1 "prepro04.bi" - -#line 5 "prepro04.bi" - -#line 7 "prepro04.bi" - +#line 1 "prepro04.bi" + +#line 5 "prepro04.bi" + +#line 7 "prepro04.bi" + diff --git a/tests/functional/zxbpp/prepro05.out b/tests/functional/zxbpp/prepro05.out index 31c4c4012..002842a64 100644 --- a/tests/functional/zxbpp/prepro05.out +++ b/tests/functional/zxbpp/prepro05.out @@ -1,7 +1,7 @@ -#line 1 "prepro05.bi" - -#line 3 "prepro05.bi" -#line 4 "prepro05.bi" - -y -test2 +#line 1 "prepro05.bi" + +#line 3 "prepro05.bi" +#line 4 "prepro05.bi" + +y +test2 diff --git a/tests/functional/zxbpp/prepro06.out b/tests/functional/zxbpp/prepro06.out index ec1f8f52d..a1adfc2fd 100644 --- a/tests/functional/zxbpp/prepro06.out +++ b/tests/functional/zxbpp/prepro06.out @@ -1,6 +1,6 @@ -#line 1 "prepro06.bi" - -#line 3 "prepro06.bi" - - -func () +#line 1 "prepro06.bi" + +#line 3 "prepro06.bi" + + +func () diff --git a/tests/functional/zxbpp/prepro09.out b/tests/functional/zxbpp/prepro09.out index b39bdec97..5eed85abe 100644 --- a/tests/functional/zxbpp/prepro09.out +++ b/tests/functional/zxbpp/prepro09.out @@ -1,4 +1,4 @@ -#line 1 "prepro09.bi" - - - +#line 1 "prepro09.bi" + + + diff --git a/tests/functional/zxbpp/prepro10.out b/tests/functional/zxbpp/prepro10.out index b5f7a2435..2ca0d7a86 100644 --- a/tests/functional/zxbpp/prepro10.out +++ b/tests/functional/zxbpp/prepro10.out @@ -1,6 +1,6 @@ -#line 1 "prepro10.bi" - - -function test(byval a as byte) - let test = 0 -end function +#line 1 "prepro10.bi" + + +function test(byval a as byte) + let test = 0 +end function diff --git a/tests/functional/zxbpp/prepro11.out b/tests/functional/zxbpp/prepro11.out index 58a87d63f..16d1cda9f 100644 --- a/tests/functional/zxbpp/prepro11.out +++ b/tests/functional/zxbpp/prepro11.out @@ -1,8 +1,8 @@ -#line 1 "prepro11.bi" - - - -#line 5 "prepro11.bi" -#line 6 "prepro11.bi" - -PRINT "HELLO WORLD" +#line 1 "prepro11.bi" + + + +#line 5 "prepro11.bi" +#line 6 "prepro11.bi" + +PRINT "HELLO WORLD" diff --git a/tests/functional/zxbpp/prepro12.out b/tests/functional/zxbpp/prepro12.out index 8a9dc287a..1847f6a1a 100644 --- a/tests/functional/zxbpp/prepro12.out +++ b/tests/functional/zxbpp/prepro12.out @@ -1,8 +1,8 @@ -#line 1 "prepro12.bi" - - - -#line 5 "prepro12.bi" -#line 6 "prepro12.bi" - -)PRINT "HELLO WORLD" +#line 1 "prepro12.bi" + + + +#line 5 "prepro12.bi" +#line 6 "prepro12.bi" + +)PRINT "HELLO WORLD" diff --git a/tests/functional/zxbpp/prepro13.out b/tests/functional/zxbpp/prepro13.out index 149ca4c6f..0ed5de8bc 100644 --- a/tests/functional/zxbpp/prepro13.out +++ b/tests/functional/zxbpp/prepro13.out @@ -1,8 +1,8 @@ -#line 1 "prepro13.bi" - - - -#line 5 "prepro13.bi" -#line 6 "prepro13.bi" - -PRINT "HELLO WORLD" +#line 1 "prepro13.bi" + + + +#line 5 "prepro13.bi" +#line 6 "prepro13.bi" + +PRINT "HELLO WORLD" diff --git a/tests/functional/zxbpp/prepro14.out b/tests/functional/zxbpp/prepro14.out index 6b2b9e78d..d7e474e95 100644 --- a/tests/functional/zxbpp/prepro14.out +++ b/tests/functional/zxbpp/prepro14.out @@ -1,9 +1,9 @@ -#line 1 "prepro14.bi" - - - -#line 5 "prepro14.bi" -#line 6 "prepro14.bi" -#line 7 "prepro14.bi" - -PRINT "HELLO WORLD" +#line 1 "prepro14.bi" + + + +#line 5 "prepro14.bi" +#line 6 "prepro14.bi" +#line 7 "prepro14.bi" + +PRINT "HELLO WORLD" diff --git a/tests/functional/zxbpp/prepro15.out b/tests/functional/zxbpp/prepro15.out index a57b86312..76f4d4747 100644 --- a/tests/functional/zxbpp/prepro15.out +++ b/tests/functional/zxbpp/prepro15.out @@ -1,12 +1,12 @@ -#line 1 "prepro15.bi" - - - - - - -#line 8 "prepro15.bi" - -PRINT 10; -PRINT "Hello"; -macro +#line 1 "prepro15.bi" + + + + + + +#line 8 "prepro15.bi" + +PRINT 10; +PRINT "Hello"; +macro diff --git a/tests/functional/zxbpp/prepro16.out b/tests/functional/zxbpp/prepro16.out index 8da46a07b..1e616d55f 100644 --- a/tests/functional/zxbpp/prepro16.out +++ b/tests/functional/zxbpp/prepro16.out @@ -1,11 +1,11 @@ -#line 1 "prepro16.bi" - - - - - -#line 7 "prepro16.bi" -#line 8 "prepro16.bi" - -PRINT 20; -PRINT 10 + 20; +#line 1 "prepro16.bi" + + + + + +#line 7 "prepro16.bi" +#line 8 "prepro16.bi" + +PRINT 20; +PRINT 10 + 20; diff --git a/tests/functional/zxbpp/prepro17.out b/tests/functional/zxbpp/prepro17.out index b3ad6e2ed..80349359a 100644 --- a/tests/functional/zxbpp/prepro17.out +++ b/tests/functional/zxbpp/prepro17.out @@ -1,8 +1,8 @@ -#line 1 "prepro17.bi" - - - - -#line 6 "prepro17.bi" - -PRINT PRINT PRINT 10;;; +#line 1 "prepro17.bi" + + + + +#line 6 "prepro17.bi" + +PRINT PRINT PRINT 10;;; diff --git a/tests/functional/zxbpp/prepro18.out b/tests/functional/zxbpp/prepro18.out index 7fced6439..268595dbb 100644 --- a/tests/functional/zxbpp/prepro18.out +++ b/tests/functional/zxbpp/prepro18.out @@ -1,7 +1,7 @@ -#line 1 "prepro18.bi" - - - -#line 5 "prepro18.bi" - -PRINT (10, 20); +#line 1 "prepro18.bi" + + + +#line 5 "prepro18.bi" + +PRINT (10, 20); diff --git a/tests/functional/zxbpp/prepro19.out b/tests/functional/zxbpp/prepro19.out index 7dde492a3..9dcd02cf3 100644 --- a/tests/functional/zxbpp/prepro19.out +++ b/tests/functional/zxbpp/prepro19.out @@ -1,7 +1,7 @@ -#line 1 "prepro19.bi" - - - -#line 5 "prepro19.bi" - -PRINT (10 + PRINT 20;); +#line 1 "prepro19.bi" + + + +#line 5 "prepro19.bi" + +PRINT (10 + PRINT 20;); diff --git a/tests/functional/zxbpp/prepro20.out b/tests/functional/zxbpp/prepro20.out index ea97c3727..1d74d1c0a 100644 --- a/tests/functional/zxbpp/prepro20.out +++ b/tests/functional/zxbpp/prepro20.out @@ -1,7 +1,7 @@ -#line 1 "prepro20.bi" - - - -#line 5 "prepro20.bi" - -PRINT ;) +#line 1 "prepro20.bi" + + + +#line 5 "prepro20.bi" + +PRINT ;) diff --git a/tests/functional/zxbpp/prepro21.out b/tests/functional/zxbpp/prepro21.out index dda7d1be9..094ff345e 100644 --- a/tests/functional/zxbpp/prepro21.out +++ b/tests/functional/zxbpp/prepro21.out @@ -1,8 +1,8 @@ -#line 1 "prepro21.bi" - - - -#line 5 "prepro21.bi" -#line 6 "prepro21.bi" - -10 +#line 1 "prepro21.bi" + + + +#line 5 "prepro21.bi" +#line 6 "prepro21.bi" + +10 diff --git a/tests/functional/zxbpp/prepro23.out b/tests/functional/zxbpp/prepro23.out index 2ae9cd2cf..f091e4e73 100644 --- a/tests/functional/zxbpp/prepro23.out +++ b/tests/functional/zxbpp/prepro23.out @@ -1,9 +1,9 @@ -#line 1 "prepro23.bi" - - - -#line 5 "prepro23.bi" -#line 6 "prepro23.bi" -#line 7 "prepro23.bi" - -PRINT 10; +#line 1 "prepro23.bi" + + + +#line 5 "prepro23.bi" +#line 6 "prepro23.bi" +#line 7 "prepro23.bi" + +PRINT 10; diff --git a/tests/functional/zxbpp/prepro24.out b/tests/functional/zxbpp/prepro24.out index d24b609e0..3d82bdadd 100644 --- a/tests/functional/zxbpp/prepro24.out +++ b/tests/functional/zxbpp/prepro24.out @@ -1,11 +1,11 @@ -#line 1 "prepro24.bi" - - -#line 6 "prepro24.bi" - - -FOR xxx = 1 TO 10 - PRINT xxx - NEXT -#line 9 -PRINT "Line 9" +#line 1 "prepro24.bi" + + +#line 6 "prepro24.bi" + + +FOR xxx = 1 TO 10 + PRINT xxx + NEXT +#line 9 +PRINT "Line 9" diff --git a/tests/functional/zxbpp/prepro25.out b/tests/functional/zxbpp/prepro25.out index c3026c9cf..f8f76e628 100644 --- a/tests/functional/zxbpp/prepro25.out +++ b/tests/functional/zxbpp/prepro25.out @@ -1,7 +1,7 @@ -#line 1 "prepro25.bi" - - - -#line 5 "prepro25.bi" - -(x + y) * 2 +#line 1 "prepro25.bi" + + + +#line 5 "prepro25.bi" + +(x + y) * 2 diff --git a/tests/functional/zxbpp/prepro26.out b/tests/functional/zxbpp/prepro26.out index efb28cc9e..0065c88eb 100644 --- a/tests/functional/zxbpp/prepro26.out +++ b/tests/functional/zxbpp/prepro26.out @@ -1,7 +1,7 @@ -#line 1 "prepro26.bi" - - - -#line 5 "prepro26.bi" - -(x + y) + 4 * 2 +#line 1 "prepro26.bi" + + + +#line 5 "prepro26.bi" + +(x + y) + 4 * 2 diff --git a/tests/functional/zxbpp/prepro27.out b/tests/functional/zxbpp/prepro27.out index 2d9cbb470..79c511a1a 100644 --- a/tests/functional/zxbpp/prepro27.out +++ b/tests/functional/zxbpp/prepro27.out @@ -1,14 +1,14 @@ -#line 1 "prepro27.bi" -Sub x -End Sub - - - - -#line 1 "prepro27.bi" -Sub x -End Sub - -#line 9 "prepro27.bi" -#line 8 "prepro27.bi" -#line 9 "prepro27.bi" +#line 1 "prepro27.bi" +Sub x +End Sub + + + + +#line 1 "prepro27.bi" +Sub x +End Sub + +#line 9 "prepro27.bi" +#line 8 "prepro27.bi" +#line 9 "prepro27.bi" diff --git a/tests/functional/zxbpp/prepro29.out b/tests/functional/zxbpp/prepro29.out index 0dd7ce49b..5f43a94c2 100644 --- a/tests/functional/zxbpp/prepro29.out +++ b/tests/functional/zxbpp/prepro29.out @@ -1,4 +1,4 @@ -#line 1 "prepro29.bi" - - -ZZ +#line 1 "prepro29.bi" + + +ZZ diff --git a/tests/functional/zxbpp/prepro30.out b/tests/functional/zxbpp/prepro30.out index 4de9e4752..02c39216c 100644 --- a/tests/functional/zxbpp/prepro30.out +++ b/tests/functional/zxbpp/prepro30.out @@ -1,128 +1,128 @@ -#line 1 "prepro30.bi" -#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" - - - - - - - - - - - -#line 13 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" - -#pragma push(case_insensitive) -#pragma case_insensitive = TRUE - - - - - - - - - - - -function attr(byval row as ubyte, byval col as ubyte) as ubyte - asm - push namespace core - - PROC - LOCAL __ATTR_END - - ld e, (ix+7) - ld d, (ix+5) - - - call __IN_SCREEN - jr nc, __ATTR_END - - call __ATTR_ADDR - ld a, (hl) - -__ATTR_END: - ENDP - - pop namespace - end asm - -end function - - - - - - - - - - - - - - -sub setattr(byval row as ubyte, byval col as ubyte, byval value as ubyte) - asm - push namespace core - - PROC - LOCAL __ATTR_END - - ld e, (ix+7) - ld d, (ix+5) - - - call __IN_SCREEN - jr nc, __ATTR_END - - call __ATTR_ADDR - ld a, (ix+9) - ld (hl), a - -__ATTR_END: - ENDP - - pop namespace - end asm - -end sub - - - - - - - - - - - -function fastcall attraddr(byval row as ubyte, byval col as ubyte) as uinteger - asm - push namespace core - - pop hl - ex (sp), hl - ld d, a - ld e, h - jp __ATTR_ADDR - pop namespace - end asm -end function - - - -#pragma pop(case_insensitive) - - -#require "attr.asm" - - -#require "in_screen.asm" - -#line 125 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" -#line 2 "prepro30.bi" - +#line 1 "prepro30.bi" +#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" + + + + + + + + + + + +#line 13 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" + +#pragma push(case_insensitive) +#pragma case_insensitive = TRUE + + + + + + + + + + + +function attr(byval row as ubyte, byval col as ubyte) as ubyte + asm + push namespace core + + PROC + LOCAL __ATTR_END + + ld e, (ix+7) + ld d, (ix+5) + + + call __IN_SCREEN + jr nc, __ATTR_END + + call __ATTR_ADDR + ld a, (hl) + +__ATTR_END: + ENDP + + pop namespace + end asm + +end function + + + + + + + + + + + + + + +sub setattr(byval row as ubyte, byval col as ubyte, byval value as ubyte) + asm + push namespace core + + PROC + LOCAL __ATTR_END + + ld e, (ix+7) + ld d, (ix+5) + + + call __IN_SCREEN + jr nc, __ATTR_END + + call __ATTR_ADDR + ld a, (ix+9) + ld (hl), a + +__ATTR_END: + ENDP + + pop namespace + end asm + +end sub + + + + + + + + + + + +function fastcall attraddr(byval row as ubyte, byval col as ubyte) as uinteger + asm + push namespace core + + pop hl + ex (sp), hl + ld d, a + ld e, h + jp __ATTR_ADDR + pop namespace + end asm +end function + + + +#pragma pop(case_insensitive) + + +#require "attr.asm" + + +#require "in_screen.asm" + +#line 125 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" +#line 2 "prepro30.bi" + diff --git a/tests/functional/zxbpp/prepro31.out b/tests/functional/zxbpp/prepro31.out index 69152e43d..b3aca512a 100644 --- a/tests/functional/zxbpp/prepro31.out +++ b/tests/functional/zxbpp/prepro31.out @@ -1,4 +1,4 @@ -#line 1 "prepro31.bi" - - -t(x, (y - 1)) +#line 1 "prepro31.bi" + + +t(x, (y - 1)) diff --git a/tests/functional/zxbpp/prepro32.out b/tests/functional/zxbpp/prepro32.out index 075b19cb5..ac0fc82c3 100644 --- a/tests/functional/zxbpp/prepro32.out +++ b/tests/functional/zxbpp/prepro32.out @@ -1,14 +1,14 @@ -#line 1 "prepro32.bi" -DIM a As Ubyte = 1 - -#line 10 "prepro32.bi" - -PRINT "HELLO WORLD" - - PRINT a; " MACRO START" - ASM - ld hl, _a - inc (hl) - END ASM - PRINT a; " MACRO END" -#line 13 +#line 1 "prepro32.bi" +DIM a As Ubyte = 1 + +#line 10 "prepro32.bi" + +PRINT "HELLO WORLD" + + PRINT a; " MACRO START" + ASM + ld hl, _a + inc (hl) + END ASM + PRINT a; " MACRO END" +#line 13 diff --git a/tests/functional/zxbpp/prepro33.out b/tests/functional/zxbpp/prepro33.out index 8de3545f3..3617a96f7 100644 --- a/tests/functional/zxbpp/prepro33.out +++ b/tests/functional/zxbpp/prepro33.out @@ -1,35 +1,35 @@ -#line 1 "prepro33.bi" -asm - EX DE,HL - PUSH DE - - LD L,(IX+8) - LD H,(IX+9) - EX AF,AF' - LD A,4 - EX AF,AF' - BLPutCharLoop: - LD B,8 - - BLPutCharOneCharLoop: - XOR A - LD C,A - - - LDI - LDI - LDI - LDI - LD A,C - ADD A,E - LD E,A - INC D - LD A,B - OR A - JP NZ,BLPutCharOneCharLoop - - EX AF,AF' - DEC A - JR Z, BLPutCharsEnd - EX AF,AF' -end asm +#line 1 "prepro33.bi" +asm + EX DE,HL + PUSH DE + + LD L,(IX+8) + LD H,(IX+9) + EX AF,AF' + LD A,4 + EX AF,AF' + BLPutCharLoop: + LD B,8 + + BLPutCharOneCharLoop: + XOR A + LD C,A + + + LDI + LDI + LDI + LDI + LD A,C + ADD A,E + LD E,A + INC D + LD A,B + OR A + JP NZ,BLPutCharOneCharLoop + + EX AF,AF' + DEC A + JR Z, BLPutCharsEnd + EX AF,AF' +end asm diff --git a/tests/functional/zxbpp/prepro34.out b/tests/functional/zxbpp/prepro34.out index 645a33509..2108234f0 100644 --- a/tests/functional/zxbpp/prepro34.out +++ b/tests/functional/zxbpp/prepro34.out @@ -1,5 +1,5 @@ -#line 1 "prepro34.bi" - - -LET a = _ - 5 +#line 1 "prepro34.bi" + + +LET a = _ + 5 diff --git a/tests/functional/zxbpp/prepro36.out b/tests/functional/zxbpp/prepro36.out index ad9a29896..c9c27cc18 100644 --- a/tests/functional/zxbpp/prepro36.out +++ b/tests/functional/zxbpp/prepro36.out @@ -1,4 +1,4 @@ -#line 1 "prepro36.bi" - - - +#line 1 "prepro36.bi" + + + diff --git a/tests/functional/zxbpp/prepro37.out b/tests/functional/zxbpp/prepro37.out index 9412f442d..fcfa1ffab 100644 --- a/tests/functional/zxbpp/prepro37.out +++ b/tests/functional/zxbpp/prepro37.out @@ -1,6 +1,6 @@ -#line 1 "prepro37.bi" - - - - -PRINT +#line 1 "prepro37.bi" + + + + +PRINT diff --git a/tests/functional/zxbpp/prepro38.out b/tests/functional/zxbpp/prepro38.out index 21858f01a..df07706e2 100644 --- a/tests/functional/zxbpp/prepro38.out +++ b/tests/functional/zxbpp/prepro38.out @@ -1,7 +1,7 @@ -#line 1 "prepro38.bi" - -#line 4 "prepro38.bi" - - - Test -#line 6 +#line 1 "prepro38.bi" + +#line 4 "prepro38.bi" + + + Test +#line 6 diff --git a/tests/functional/zxbpp/prepro39.out b/tests/functional/zxbpp/prepro39.out index 42c310b70..2723ffcb1 100644 --- a/tests/functional/zxbpp/prepro39.out +++ b/tests/functional/zxbpp/prepro39.out @@ -1,9 +1,9 @@ -#line 1 "prepro39.bi" - - - -#line 6 "prepro39.bi" - - - PRINT , -#line 8 +#line 1 "prepro39.bi" + + + +#line 6 "prepro39.bi" + + + PRINT , +#line 8 diff --git a/tests/functional/zxbpp/prepro40.out b/tests/functional/zxbpp/prepro40.out index 611dbd187..987bc779c 100644 --- a/tests/functional/zxbpp/prepro40.out +++ b/tests/functional/zxbpp/prepro40.out @@ -1,4 +1,4 @@ -#line 1 "prepro40.bi" - - -macro +#line 1 "prepro40.bi" + + +macro diff --git a/tests/functional/zxbpp/prepro41.out b/tests/functional/zxbpp/prepro41.out index 02670ced4..76c952b31 100644 --- a/tests/functional/zxbpp/prepro41.out +++ b/tests/functional/zxbpp/prepro41.out @@ -1,8 +1,8 @@ -#line 1 "prepro41.bi" - - -10 PAUSE 0 : - asm - call 65012 - end asm : PAUSE 0 -#line 7 +#line 1 "prepro41.bi" + + +10 PAUSE 0 : + asm + call 65012 + end asm : PAUSE 0 +#line 7 diff --git a/tests/functional/zxbpp/prepro42.out b/tests/functional/zxbpp/prepro42.out index bda8844f4..932a27240 100644 --- a/tests/functional/zxbpp/prepro42.out +++ b/tests/functional/zxbpp/prepro42.out @@ -1,4 +1,4 @@ -#line 1 "prepro42.bi" - - -#line 6 "prepro42.bi" +#line 1 "prepro42.bi" + + +#line 6 "prepro42.bi" diff --git a/tests/functional/zxbpp/prepro43.out b/tests/functional/zxbpp/prepro43.out index c77d10e4d..1d5bc3876 100644 --- a/tests/functional/zxbpp/prepro43.out +++ b/tests/functional/zxbpp/prepro43.out @@ -1,6 +1,6 @@ -#line 1 "prepro43.bi" - - - -print 1 -#line 6 "prepro43.bi" +#line 1 "prepro43.bi" + + + +print 1 +#line 6 "prepro43.bi" diff --git a/tests/functional/zxbpp/prepro44.out b/tests/functional/zxbpp/prepro44.out index 62908a59e..12b7f6558 100644 --- a/tests/functional/zxbpp/prepro44.out +++ b/tests/functional/zxbpp/prepro44.out @@ -1,4 +1,4 @@ -#line 1 "prepro44.bi" - - -#line 6 "prepro44.bi" +#line 1 "prepro44.bi" + + +#line 6 "prepro44.bi" diff --git a/tests/functional/zxbpp/prepro45.out b/tests/functional/zxbpp/prepro45.out index f23f903a2..61af4f5a4 100644 --- a/tests/functional/zxbpp/prepro45.out +++ b/tests/functional/zxbpp/prepro45.out @@ -1,4 +1,4 @@ -#line 1 "prepro45.bi" - - -#line 7 "prepro45.bi" +#line 1 "prepro45.bi" + + +#line 7 "prepro45.bi" diff --git a/tests/functional/zxbpp/prepro46.out b/tests/functional/zxbpp/prepro46.out index 0cecdef91..1ba95a53b 100644 --- a/tests/functional/zxbpp/prepro46.out +++ b/tests/functional/zxbpp/prepro46.out @@ -1,6 +1,6 @@ -#line 1 "prepro46.bi" - - - -print 1 -#line 6 "prepro46.bi" +#line 1 "prepro46.bi" + + + +print 1 +#line 6 "prepro46.bi" diff --git a/tests/functional/zxbpp/prepro47.out b/tests/functional/zxbpp/prepro47.out index 3a4b99af7..813aaa1e9 100644 --- a/tests/functional/zxbpp/prepro47.out +++ b/tests/functional/zxbpp/prepro47.out @@ -1,5 +1,5 @@ -#line 1 "prepro47.bi" - -#line 3 "prepro47.bi" - -#line 8 "prepro47.bi" +#line 1 "prepro47.bi" + +#line 3 "prepro47.bi" + +#line 8 "prepro47.bi" diff --git a/tests/functional/zxbpp/prepro48.out b/tests/functional/zxbpp/prepro48.out index 976bbb3f7..bfc8f8d82 100644 --- a/tests/functional/zxbpp/prepro48.out +++ b/tests/functional/zxbpp/prepro48.out @@ -1,5 +1,5 @@ -#line 1 "prepro48.bi" - -#line 3 "prepro48.bi" - -#line 8 "prepro48.bi" +#line 1 "prepro48.bi" + +#line 3 "prepro48.bi" + +#line 8 "prepro48.bi" diff --git a/tests/functional/zxbpp/prepro49.out b/tests/functional/zxbpp/prepro49.out index 7b358facf..87aa9583f 100644 --- a/tests/functional/zxbpp/prepro49.out +++ b/tests/functional/zxbpp/prepro49.out @@ -1,5 +1,5 @@ -#line 1 "prepro49.bi" - -#line 3 "prepro49.bi" - -#line 8 "prepro49.bi" +#line 1 "prepro49.bi" + +#line 3 "prepro49.bi" + +#line 8 "prepro49.bi" diff --git a/tests/functional/zxbpp/prepro50.out b/tests/functional/zxbpp/prepro50.out index e4560671c..76e000824 100644 --- a/tests/functional/zxbpp/prepro50.out +++ b/tests/functional/zxbpp/prepro50.out @@ -1,8 +1,8 @@ -#line 1 "prepro50.bi" - -#line 3 "prepro50.bi" - - -print 1 - -#line 8 "prepro50.bi" +#line 1 "prepro50.bi" + +#line 3 "prepro50.bi" + + +print 1 + +#line 8 "prepro50.bi" diff --git a/tests/functional/zxbpp/prepro51.out b/tests/functional/zxbpp/prepro51.out index 67281624b..f09c1f080 100644 --- a/tests/functional/zxbpp/prepro51.out +++ b/tests/functional/zxbpp/prepro51.out @@ -1,4 +1,4 @@ -#line 1 "prepro51.bi" - - -A(1) +#line 1 "prepro51.bi" + + +A(1) diff --git a/tests/functional/zxbpp/prepro52.out b/tests/functional/zxbpp/prepro52.out index e2c023bc4..b22576dca 100644 --- a/tests/functional/zxbpp/prepro52.out +++ b/tests/functional/zxbpp/prepro52.out @@ -1,5 +1,5 @@ -#line 1 "prepro52.bi" - -#line 3 "prepro52.bi" - -1 +#line 1 "prepro52.bi" + +#line 3 "prepro52.bi" + +1 diff --git a/tests/functional/zxbpp/prepro53.out b/tests/functional/zxbpp/prepro53.out index bacaf8d97..051b7c171 100644 --- a/tests/functional/zxbpp/prepro53.out +++ b/tests/functional/zxbpp/prepro53.out @@ -1,5 +1,5 @@ -#line 1 "prepro53.bi" - -#line 3 "prepro53.bi" - -1 +#line 1 "prepro53.bi" + +#line 3 "prepro53.bi" + +1 diff --git a/tests/functional/zxbpp/prepro54.out b/tests/functional/zxbpp/prepro54.out index dc8ee0c6d..5796d5e88 100644 --- a/tests/functional/zxbpp/prepro54.out +++ b/tests/functional/zxbpp/prepro54.out @@ -1,5 +1,5 @@ -#line 1 "prepro54.bi" - -#line 3 "prepro54.bi" - -#line 7 "prepro54.bi" +#line 1 "prepro54.bi" + +#line 3 "prepro54.bi" + +#line 7 "prepro54.bi" diff --git a/tests/functional/zxbpp/prepro55.out b/tests/functional/zxbpp/prepro55.out index 57bc0cf34..627797956 100644 --- a/tests/functional/zxbpp/prepro55.out +++ b/tests/functional/zxbpp/prepro55.out @@ -1,7 +1,7 @@ -#line 1 "prepro55.bi" - -#line 3 "prepro55.bi" - - -ok -#line 7 "prepro55.bi" +#line 1 "prepro55.bi" + +#line 3 "prepro55.bi" + + +ok +#line 7 "prepro55.bi" diff --git a/tests/functional/zxbpp/prepro56.out b/tests/functional/zxbpp/prepro56.out index 814a6b692..b829f00c2 100644 --- a/tests/functional/zxbpp/prepro56.out +++ b/tests/functional/zxbpp/prepro56.out @@ -1,5 +1,5 @@ -#line 1 "prepro56.bi" - -#line 3 "prepro56.bi" - -#line 7 "prepro56.bi" +#line 1 "prepro56.bi" + +#line 3 "prepro56.bi" + +#line 7 "prepro56.bi" diff --git a/tests/functional/zxbpp/prepro57.out b/tests/functional/zxbpp/prepro57.out index a6d0ba11a..d62e8580b 100644 --- a/tests/functional/zxbpp/prepro57.out +++ b/tests/functional/zxbpp/prepro57.out @@ -1,5 +1,5 @@ -#line 1 "prepro57.bi" - - -PRINT "LANG = es" -#line 5 "prepro57.bi" +#line 1 "prepro57.bi" + + +PRINT "LANG = es" +#line 5 "prepro57.bi" diff --git a/tests/functional/zxbpp/prepro58.out b/tests/functional/zxbpp/prepro58.out index 0d224895e..3ae83cfa0 100644 --- a/tests/functional/zxbpp/prepro58.out +++ b/tests/functional/zxbpp/prepro58.out @@ -1,6 +1,6 @@ -#line 1 "prepro58.bi" -sub test() - asm - ex af,af' - end asm -end sub +#line 1 "prepro58.bi" +sub test() + asm + ex af,af' + end asm +end sub diff --git a/tests/functional/zxbpp/prepro59.out b/tests/functional/zxbpp/prepro59.out index 0fb6b4e16..65a8a93a7 100644 --- a/tests/functional/zxbpp/prepro59.out +++ b/tests/functional/zxbpp/prepro59.out @@ -1,5 +1,5 @@ -#line 1 "prepro59.bi" - -#line 3 "prepro59.bi" - -LET a$ = (a$ + CHR$(0)) +#line 1 "prepro59.bi" + +#line 3 "prepro59.bi" + +LET a$ = (a$ + CHR$(0)) diff --git a/tests/functional/zxbpp/prepro60.out b/tests/functional/zxbpp/prepro60.out index 1835bd294..eae521eb5 100644 --- a/tests/functional/zxbpp/prepro60.out +++ b/tests/functional/zxbpp/prepro60.out @@ -1,6 +1,6 @@ -#line 1 "prepro60.bi" -Function FASTCALL ESXDOSWrite(ByVal handle as Byte, _ - ByVal buffer as UInteger, _ - ByVal nbytes as UInteger) as Uinteger -#line 4 -End Function +#line 1 "prepro60.bi" +Function FASTCALL ESXDOSWrite(ByVal handle as Byte, _ + ByVal buffer as UInteger, _ + ByVal nbytes as UInteger) as Uinteger +#line 4 +End Function diff --git a/tests/functional/zxbpp/prepro61.out b/tests/functional/zxbpp/prepro61.out index 8c6ed5188..c057071d0 100644 --- a/tests/functional/zxbpp/prepro61.out +++ b/tests/functional/zxbpp/prepro61.out @@ -1,6 +1,6 @@ -#line 1 "prepro61.bi" - - -#line 4 "prepro61.bi" - -xx 1 aa 1 x(1) +#line 1 "prepro61.bi" + + +#line 4 "prepro61.bi" + +xx 1 aa 1 x(1) diff --git a/tests/functional/zxbpp/prepro62.out b/tests/functional/zxbpp/prepro62.out index f424ae130..39bb10aaf 100644 --- a/tests/functional/zxbpp/prepro62.out +++ b/tests/functional/zxbpp/prepro62.out @@ -1,7 +1,7 @@ -#line 1 "prepro62.bi" - -#line 3 "prepro62.bi" -#line 4 "prepro62.bi" - -(1 + 1, 2 + 2) -(3 + 1, 4 + 2) +#line 1 "prepro62.bi" + +#line 3 "prepro62.bi" +#line 4 "prepro62.bi" + +(1 + 1, 2 + 2) +(3 + 1, 4 + 2) diff --git a/tests/functional/zxbpp/prepro63.out b/tests/functional/zxbpp/prepro63.out index 51b9f7222..8d9d72c5a 100644 --- a/tests/functional/zxbpp/prepro63.out +++ b/tests/functional/zxbpp/prepro63.out @@ -1,6 +1,6 @@ -#line 1 "prepro63.bi" - - -#line 4 "prepro63.bi" - -XX5 +#line 1 "prepro63.bi" + + +#line 4 "prepro63.bi" + +XX5 diff --git a/tests/functional/zxbpp/prepro64.out b/tests/functional/zxbpp/prepro64.out index 15a3d3114..835aabb03 100644 --- a/tests/functional/zxbpp/prepro64.out +++ b/tests/functional/zxbpp/prepro64.out @@ -1,50 +1,50 @@ -#line 1 "prepro64.bi" - -#line 3 "prepro64.bi" - -#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" - - - - - - - - - - - - - -#line 15 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" - -#pragma push(case_insensitive) -#pragma case_insensitive = true - - - - - - - - - -function FASTCALL pos as ubyte - asm - push namespace core - PROC - - call __LOAD_S_POSN - ld , e - - ENDP - pop namespace - end asm -end function - -#pragma pop(case_insensitive) -#require "sposn.asm" - - -#line 45 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" -#line 5 "prepro64.bi" +#line 1 "prepro64.bi" + +#line 3 "prepro64.bi" + +#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" + + + + + + + + + + + + + +#line 15 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" + +#pragma push(case_insensitive) +#pragma case_insensitive = true + + + + + + + + + +function FASTCALL pos as ubyte + asm + push namespace core + PROC + + call __LOAD_S_POSN + ld , e + + ENDP + pop namespace + end asm +end function + +#pragma pop(case_insensitive) +#require "sposn.asm" + + +#line 45 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" +#line 5 "prepro64.bi" diff --git a/tests/functional/zxbpp/prepro70.out b/tests/functional/zxbpp/prepro70.out index 599230c43..dd55790ed 100644 --- a/tests/functional/zxbpp/prepro70.out +++ b/tests/functional/zxbpp/prepro70.out @@ -1,13 +1,13 @@ -#line 1 "prepro70.bi" -DIM b - - - -#line 1 "./prepro70.bi" -DIM b -#line 10 "./prepro70.bi" -#line 6 "prepro70.bi" - -DIM a - -#line 10 "prepro70.bi" +#line 1 "prepro70.bi" +DIM b + + + +#line 1 "prepro70.bi" +DIM b +#line 10 "prepro70.bi" +#line 6 "prepro70.bi" + +DIM a + +#line 10 "prepro70.bi" diff --git a/tests/functional/zxbpp/prepro71.out b/tests/functional/zxbpp/prepro71.out index 9d1274edd..98e08c6fb 100644 --- a/tests/functional/zxbpp/prepro71.out +++ b/tests/functional/zxbpp/prepro71.out @@ -1,243 +1,243 @@ -#line 1 "prepro71.bi" -#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" - - - - - - - - - - - -#line 13 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" - -#pragma push(case_insensitive) -#pragma case_insensitive = True - - - - - - - - - - - - - - - -function FASTCALL allocate(byval n as uinteger) as uinteger - - - - - asm - push namespace core - ld b, h - ld c, l - jp __MEM_ALLOC - pop namespace - end asm -end function - - - - - - - - - - - - - - - - - -function FASTCALL callocate(byval n as uinteger) as uinteger - - - - - asm - push namespace core - ld b, h - ld c, l - jp __MEM_CALLOC - pop namespace - end asm -end function - - - - - - - - -sub FASTCALL deallocate(byval addr as integer) - - - - asm - push namespace core - jp __MEM_FREE - pop namespace - end asm -end sub - - - - - - - - - - - - - - - - -function FASTCALL reallocate(byval addr as uinteger, byval n as uinteger) as uinteger - - - - - - asm - push namespace core - ex de, hl - pop hl - ex (sp), hl - ld b, h - ld c, l - ex de, hl - jp __REALLOC - pop namespace - end asm -end function - - - - - - - - -function FASTCALL memavail as uInteger - asm - push namespace core - PROC - - LOCAL LOOP - - ld hl, ZXBASIC_MEM_HEAP - ld de, 0 - -LOOP: - - ld c, (hl) - inc hl - ld b, (hl) - inc hl - - - ld a, (hl) - inc hl - ld h, (hl) - ld l, a - - - ex de, hl - add hl, bc - ex de, hl - - - ld a, h - or l - jr nz, LOOP - - ex de, hl - - ENDP - pop namespace - end asm -end function - - - - - - - -function FASTCALL maxavail as uInteger - asm - push namespace core - PROC - - LOCAL LOOP, CONT - - ld hl, ZXBASIC_MEM_HEAP - ld de, 0 - -LOOP: - - ld c, (hl) - inc hl - ld b, (hl) - inc hl - - - ld a, (hl) - inc hl - ld h, (hl) - ld l, a - - - - ex de, hl - or a - sbc hl, bc - add hl, bc - ex de, hl - - - jr nc, CONT - - ld d, b - ld e, c - -CONT: - - ld a, h - or l - jr nz, LOOP - - ex de, hl - - ENDP - pop namespace - end asm -end function - - -#pragma pop(case_insensitive) - -#require "mem/alloc.asm" -#require "mem/free.asm" -#require "mem/realloc.asm" -#require "mem/calloc.asm" - -#line 239 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" -#line 2 "prepro71.bi" - -PRINT "HOLA" +#line 1 "prepro71.bi" +#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" + + + + + + + + + + + +#line 13 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" + +#pragma push(case_insensitive) +#pragma case_insensitive = True + + + + + + + + + + + + + + + +function FASTCALL allocate(byval n as uinteger) as uinteger + + + + + asm + push namespace core + ld b, h + ld c, l + jp __MEM_ALLOC + pop namespace + end asm +end function + + + + + + + + + + + + + + + + + +function FASTCALL callocate(byval n as uinteger) as uinteger + + + + + asm + push namespace core + ld b, h + ld c, l + jp __MEM_CALLOC + pop namespace + end asm +end function + + + + + + + + +sub FASTCALL deallocate(byval addr as integer) + + + + asm + push namespace core + jp __MEM_FREE + pop namespace + end asm +end sub + + + + + + + + + + + + + + + + +function FASTCALL reallocate(byval addr as uinteger, byval n as uinteger) as uinteger + + + + + + asm + push namespace core + ex de, hl + pop hl + ex (sp), hl + ld b, h + ld c, l + ex de, hl + jp __REALLOC + pop namespace + end asm +end function + + + + + + + + +function FASTCALL memavail as uInteger + asm + push namespace core + PROC + + LOCAL LOOP + + ld hl, ZXBASIC_MEM_HEAP + ld de, 0 + +LOOP: + + ld c, (hl) + inc hl + ld b, (hl) + inc hl + + + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + + + ex de, hl + add hl, bc + ex de, hl + + + ld a, h + or l + jr nz, LOOP + + ex de, hl + + ENDP + pop namespace + end asm +end function + + + + + + + +function FASTCALL maxavail as uInteger + asm + push namespace core + PROC + + LOCAL LOOP, CONT + + ld hl, ZXBASIC_MEM_HEAP + ld de, 0 + +LOOP: + + ld c, (hl) + inc hl + ld b, (hl) + inc hl + + + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + + + + ex de, hl + or a + sbc hl, bc + add hl, bc + ex de, hl + + + jr nc, CONT + + ld d, b + ld e, c + +CONT: + + ld a, h + or l + jr nz, LOOP + + ex de, hl + + ENDP + pop namespace + end asm +end function + + +#pragma pop(case_insensitive) + +#require "mem/alloc.asm" +#require "mem/free.asm" +#require "mem/realloc.asm" +#require "mem/calloc.asm" + +#line 239 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" +#line 2 "prepro71.bi" + +PRINT "HOLA" diff --git a/tests/functional/zxbpp/prepro72.out b/tests/functional/zxbpp/prepro72.out index 3ba0b5f2a..6da2cd04e 100644 --- a/tests/functional/zxbpp/prepro72.out +++ b/tests/functional/zxbpp/prepro72.out @@ -1,13 +1,13 @@ -#line 1 "prepro72.bi" -DIM b - - - -#line 1 "./prepro70.bi" -DIM b -#line 10 "./prepro70.bi" -#line 6 "prepro72.bi" - -DIM a - -#line 10 "prepro72.bi" +#line 1 "prepro72.bi" +DIM b + + + +#line 1 "prepro70.bi" +DIM b +#line 10 "prepro70.bi" +#line 6 "prepro72.bi" + +DIM a + +#line 10 "prepro72.bi" diff --git a/tests/functional/zxbpp/prepro73.out b/tests/functional/zxbpp/prepro73.out index e3de2597c..75a2c16aa 100644 --- a/tests/functional/zxbpp/prepro73.out +++ b/tests/functional/zxbpp/prepro73.out @@ -1,67 +1,67 @@ -#line 1 "prepro73.bi" -#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" - - - - - - - - - - - - - -#line 15 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" - -#pragma push(case_insensitive) -#pragma case_insensitive = true - - - - - - - - - -function FASTCALL pos as ubyte - asm - push namespace core - PROC - - call __LOAD_S_POSN - ld a, e - - ENDP - pop namespace - end asm -end function - -#pragma pop(case_insensitive) -#require "sposn.asm" - - -#line 45 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" -#line 2 "prepro73.bi" - - - -#line 1 "prepro73.bi" -#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" - - - - - - - - - -#line 45 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" -#line 2 "prepro73.bi" - -#line 7 "prepro73.bi" -#line 6 "prepro73.bi" -#line 7 "prepro73.bi" +#line 1 "prepro73.bi" +#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" + + + + + + + + + + + + + +#line 15 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" + +#pragma push(case_insensitive) +#pragma case_insensitive = true + + + + + + + + + +function FASTCALL pos as ubyte + asm + push namespace core + PROC + + call __LOAD_S_POSN + ld a, e + + ENDP + pop namespace + end asm +end function + +#pragma pop(case_insensitive) +#require "sposn.asm" + + +#line 45 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" +#line 2 "prepro73.bi" + + + +#line 1 "prepro73.bi" +#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" + + + + + + + + + +#line 45 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" +#line 2 "prepro73.bi" + +#line 7 "prepro73.bi" +#line 6 "prepro73.bi" +#line 7 "prepro73.bi" diff --git a/tests/functional/zxbpp/prepro74.out b/tests/functional/zxbpp/prepro74.out index 72e9bfc05..f1b5a1fd5 100644 --- a/tests/functional/zxbpp/prepro74.out +++ b/tests/functional/zxbpp/prepro74.out @@ -1,60 +1,60 @@ -#line 1 "prepro74.bi" -#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" - - - - - - - - - - - - - -#line 15 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" - -#pragma push(case_insensitive) -#pragma case_insensitive = true - - - - - - - - - -function FASTCALL pos as ubyte - asm - push namespace core - PROC - - call __LOAD_S_POSN - ld a, e - - ENDP - pop namespace - end asm -end function - -#pragma pop(case_insensitive) -#require "sposn.asm" - - -#line 45 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" -#line 2 "prepro74.bi" - - - -#line 1 "prepro74.bi" - - -#line 7 "prepro74.bi" - -glibberish -#line 6 "prepro74.bi" -#line 7 "prepro74.bi" - -glibberish +#line 1 "prepro74.bi" +#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" + + + + + + + + + + + + + +#line 15 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" + +#pragma push(case_insensitive) +#pragma case_insensitive = true + + + + + + + + + +function FASTCALL pos as ubyte + asm + push namespace core + PROC + + call __LOAD_S_POSN + ld a, e + + ENDP + pop namespace + end asm +end function + +#pragma pop(case_insensitive) +#require "sposn.asm" + + +#line 45 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" +#line 2 "prepro74.bi" + + + +#line 1 "prepro74.bi" + + +#line 7 "prepro74.bi" + +glibberish +#line 6 "prepro74.bi" +#line 7 "prepro74.bi" + +glibberish diff --git a/tests/functional/zxbpp/prepro75.out b/tests/functional/zxbpp/prepro75.out index 3d2e89be8..60d28b684 100644 --- a/tests/functional/zxbpp/prepro75.out +++ b/tests/functional/zxbpp/prepro75.out @@ -1,9 +1,9 @@ -#line 1 "prepro75.bi" -A = 0 - - -#line 1 "prepro75.bi" -A = 0 -#line 6 "prepro75.bi" -#line 5 "prepro75.bi" -#line 6 "prepro75.bi" +#line 1 "prepro75.bi" +A = 0 + + +#line 1 "prepro75.bi" +A = 0 +#line 6 "prepro75.bi" +#line 5 "prepro75.bi" +#line 6 "prepro75.bi" diff --git a/tests/functional/zxbpp/prepro77.out b/tests/functional/zxbpp/prepro77.out index 05c22b9ac..302836d38 100644 --- a/tests/functional/zxbpp/prepro77.out +++ b/tests/functional/zxbpp/prepro77.out @@ -1,2 +1,2 @@ -#line 1 "prepro77.bi" - +#line 1 "prepro77.bi" + diff --git a/tests/functional/zxbpp/prepro80.out b/tests/functional/zxbpp/prepro80.out index c5f085d99..a543816d3 100644 --- a/tests/functional/zxbpp/prepro80.out +++ b/tests/functional/zxbpp/prepro80.out @@ -1,20 +1,20 @@ -#line 1 "prepro80.bi" - - - - - - - asm - halt - halt - end asm -#line 11 - - asm - halt - halt - end asm -#line 12 - -#line 14 "prepro80.bi" +#line 1 "prepro80.bi" + + + + + + + asm + halt + halt + end asm +#line 11 + + asm + halt + halt + end asm +#line 12 + +#line 14 "prepro80.bi" diff --git a/tests/functional/zxbpp/prepro81.out b/tests/functional/zxbpp/prepro81.out index ff19e067d..b53925261 100644 --- a/tests/functional/zxbpp/prepro81.out +++ b/tests/functional/zxbpp/prepro81.out @@ -1,4 +1,4 @@ -#line 1 "prepro81.bi" - - -#line 6 "prepro81.bi" +#line 1 "prepro81.bi" + + +#line 6 "prepro81.bi" diff --git a/tests/functional/zxbpp/prepro82.out b/tests/functional/zxbpp/prepro82.out index 8387751de..2ce50d3fa 100644 --- a/tests/functional/zxbpp/prepro82.out +++ b/tests/functional/zxbpp/prepro82.out @@ -1,7 +1,7 @@ -#line 1 "prepro82.bi" - - - - - PRINT "test" -#line 7 "prepro82.bi" +#line 1 "prepro82.bi" + + + + + PRINT "test" +#line 7 "prepro82.bi" diff --git a/tests/functional/zxbpp/prepro85.bi b/tests/functional/zxbpp/prepro85.bi new file mode 100644 index 000000000..7094a3468 --- /dev/null +++ b/tests/functional/zxbpp/prepro85.bi @@ -0,0 +1,3 @@ +REM relative import + +#include "../zxbpp/prepro00.bi" diff --git a/tests/functional/zxbpp/prepro85.out b/tests/functional/zxbpp/prepro85.out new file mode 100644 index 000000000..8207f63be --- /dev/null +++ b/tests/functional/zxbpp/prepro85.out @@ -0,0 +1,132 @@ +#line 1 "prepro85.bi" + + +#line 1 "prepro00.bi" +#line 1 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" + + + + + + + + + + + +#line 13 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" + +#pragma push(case_insensitive) +#pragma case_insensitive = TRUE + + + + + + + + + + + +function attr(byval row as ubyte, byval col as ubyte) as ubyte + asm + push namespace core + + PROC + LOCAL __ATTR_END + + ld e, (ix+7) + ld d, (ix+5) + + + call __IN_SCREEN + jr nc, __ATTR_END + + call __ATTR_ADDR + ld a, (hl) + +__ATTR_END: + ENDP + + pop namespace + end asm + +end function + + + + + + + + + + + + + + +sub setattr(byval row as ubyte, byval col as ubyte, byval value as ubyte) + asm + push namespace core + + PROC + LOCAL __ATTR_END + + ld e, (ix+7) + ld d, (ix+5) + + + call __IN_SCREEN + jr nc, __ATTR_END + + call __ATTR_ADDR + ld a, (ix+9) + ld (hl), a + +__ATTR_END: + ENDP + + pop namespace + end asm + +end sub + + + + + + + + + + + +function fastcall attraddr(byval row as ubyte, byval col as ubyte) as uinteger + asm + push namespace core + + pop hl + ex (sp), hl + ld d, a + ld e, h + jp __ATTR_ADDR + pop namespace + end asm +end function + + + +#pragma pop(case_insensitive) + + +#require "attr.asm" + + +#require "in_screen.asm" + +#line 125 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" +#line 2 "prepro00.bi" +PRINT "HELLO" +#line 4 "prepro85.bi" diff --git a/tests/functional/zxbpp/prepro90.out b/tests/functional/zxbpp/prepro90.out index fa3b6bb6a..5ab94ed12 100644 --- a/tests/functional/zxbpp/prepro90.out +++ b/tests/functional/zxbpp/prepro90.out @@ -1,7 +1,7 @@ -#line 1 "prepro90.bi" - -#line 3 "prepro90.bi" - -#line 7 "prepro90.bi" - OK -#line 9 "prepro90.bi" +#line 1 "prepro90.bi" + +#line 3 "prepro90.bi" + +#line 7 "prepro90.bi" + OK +#line 9 "prepro90.bi" diff --git a/tests/functional/zxbpp/prepro91.out b/tests/functional/zxbpp/prepro91.out index 98e31fcb2..8de0031a7 100644 --- a/tests/functional/zxbpp/prepro91.out +++ b/tests/functional/zxbpp/prepro91.out @@ -1,5 +1,5 @@ -#line 1 "prepro91.bi" - -#line 7 "prepro91.bi" - OK -#line 9 "prepro91.bi" +#line 1 "prepro91.bi" + +#line 7 "prepro91.bi" + OK +#line 9 "prepro91.bi" diff --git a/tests/functional/zxbpp/prepro92.out b/tests/functional/zxbpp/prepro92.out index bcf57a7a8..ddb01a2ed 100644 --- a/tests/functional/zxbpp/prepro92.out +++ b/tests/functional/zxbpp/prepro92.out @@ -1,7 +1,7 @@ -#line 1 "prepro92.bi" - - - -#line 7 "prepro92.bi" -OK -#line 9 "prepro92.bi" +#line 1 "prepro92.bi" + + + +#line 7 "prepro92.bi" +OK +#line 9 "prepro92.bi" diff --git a/tests/functional/zxbpp/prepro93.out b/tests/functional/zxbpp/prepro93.out index 5be344977..b00ab178a 100644 --- a/tests/functional/zxbpp/prepro93.out +++ b/tests/functional/zxbpp/prepro93.out @@ -1,7 +1,7 @@ -#line 1 "prepro93.bi" - - - -#line 9 "prepro93.bi" -OK -#line 11 "prepro93.bi" +#line 1 "prepro93.bi" + + + +#line 9 "prepro93.bi" +OK +#line 11 "prepro93.bi" diff --git a/tests/functional/zxbpp/prepro94.out b/tests/functional/zxbpp/prepro94.out index f712fd64e..9a631f91b 100644 --- a/tests/functional/zxbpp/prepro94.out +++ b/tests/functional/zxbpp/prepro94.out @@ -1,6 +1,6 @@ -#line 1 "prepro94.bi" - - -#line 8 "prepro94.bi" - OK -#line 10 "prepro94.bi" +#line 1 "prepro94.bi" + + +#line 8 "prepro94.bi" + OK +#line 10 "prepro94.bi" diff --git a/tests/functional/zxbpp/prepro95.out b/tests/functional/zxbpp/prepro95.out index 233206023..5a7dd76ff 100644 --- a/tests/functional/zxbpp/prepro95.out +++ b/tests/functional/zxbpp/prepro95.out @@ -1,5 +1,5 @@ -#line 1 "prepro95.bi" - -#line 11 "prepro95.bi" - OK -#line 13 "prepro95.bi" +#line 1 "prepro95.bi" + +#line 11 "prepro95.bi" + OK +#line 13 "prepro95.bi" diff --git a/tests/functional/zxbpp/spectrum.out b/tests/functional/zxbpp/spectrum.out index 81886253d..752c23498 100644 --- a/tests/functional/zxbpp/spectrum.out +++ b/tests/functional/zxbpp/spectrum.out @@ -1,5 +1,5 @@ -#line 1 "spectrum.bi" - - - -PRINT "HELLO WORLD!" +#line 1 "spectrum.bi" + + + +PRINT "HELLO WORLD!" diff --git a/tests/functional/zxbpp/stringizing0.out b/tests/functional/zxbpp/stringizing0.out index dda07c45b..a8d4304b7 100644 --- a/tests/functional/zxbpp/stringizing0.out +++ b/tests/functional/zxbpp/stringizing0.out @@ -1,6 +1,6 @@ -#line 1 "stringizing0.bi" - - -#line 4 "stringizing0.bi" - -"1" +#line 1 "stringizing0.bi" + + +#line 4 "stringizing0.bi" + +"1" diff --git a/tests/functional/zxbpp/stringizing1.out b/tests/functional/zxbpp/stringizing1.out index 17de860a1..41500b96a 100644 --- a/tests/functional/zxbpp/stringizing1.out +++ b/tests/functional/zxbpp/stringizing1.out @@ -1,8 +1,8 @@ -#line 1 "stringizing1.bi" - - -#line 4 "stringizing1.bi" -#line 5 "stringizing1.bi" - -"ab" -"5" +#line 1 "stringizing1.bi" + + +#line 4 "stringizing1.bi" +#line 5 "stringizing1.bi" + +"ab" +"5" diff --git a/tests/functional/zxbpp/stringizing2.out b/tests/functional/zxbpp/stringizing2.out index 84d7d3d01..d6689b993 100644 --- a/tests/functional/zxbpp/stringizing2.out +++ b/tests/functional/zxbpp/stringizing2.out @@ -1,6 +1,6 @@ -#line 1 "stringizing2.bi" - - -#line 4 "stringizing2.bi" - -"""a """" string""" +#line 1 "stringizing2.bi" + + +#line 4 "stringizing2.bi" + +"""a """" string""" diff --git a/tests/functional/zxbpp/token-paste0.out b/tests/functional/zxbpp/token-paste0.out index b623d4d94..366812a66 100644 --- a/tests/functional/zxbpp/token-paste0.out +++ b/tests/functional/zxbpp/token-paste0.out @@ -1,9 +1,9 @@ -#line 1 "token-paste0.bi" - - -#line 4 "token-paste0.bi" - -AB - - -A ## B +#line 1 "token-paste0.bi" + + +#line 4 "token-paste0.bi" + +AB + + +A ## B diff --git a/tests/functional/zxbpp/token-paste1.out b/tests/functional/zxbpp/token-paste1.out index 9ecbbc591..cce8bdbd6 100644 --- a/tests/functional/zxbpp/token-paste1.out +++ b/tests/functional/zxbpp/token-paste1.out @@ -1,8 +1,8 @@ -#line 1 "token-paste1.bi" - - -#line 4 "token-paste1.bi" - -#line 6 "token-paste1.bi" - -(AA 1) +#line 1 "token-paste1.bi" + + +#line 4 "token-paste1.bi" + +#line 6 "token-paste1.bi" + +(AA 1) diff --git a/tests/zxbc/__init__.py b/tests/outfmt/__init__.py similarity index 100% rename from tests/zxbc/__init__.py rename to tests/outfmt/__init__.py diff --git a/tests/outfmt/test_binary.py b/tests/outfmt/test_binary.py new file mode 100644 index 000000000..ce4ccfcb3 --- /dev/null +++ b/tests/outfmt/test_binary.py @@ -0,0 +1,27 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + +from src.outfmt.binary import BinaryEmitter + + +def test_binary_emitter(tmp_path): + output_file = tmp_path / "test.bin" + emitter = BinaryEmitter() + program_bytes = b"\x01\x02\x03\x04" + + emitter.emit( + output_filename=str(output_file), + program_name="test", + loader_bytes=None, + entry_point=16384, + program_bytes=program_bytes, + aux_bin_blocks=[], + aux_headless_bin_blocks=[], + ) + + assert output_file.exists() + assert output_file.read_bytes() == program_bytes diff --git a/tests/outfmt/test_sna.py b/tests/outfmt/test_sna.py new file mode 100644 index 000000000..4ec327052 --- /dev/null +++ b/tests/outfmt/test_sna.py @@ -0,0 +1,57 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + +from src.outfmt.sna import SnaEmitter + + +def test_sna_emitter_generate(): + emitter = SnaEmitter() + program_bytes = b"\x00" * 100 + entry_point = 0x8000 + + # generate(self, loader_bytes, clear_addr, entry_point, program_bytes) + # SnaEmitter.emit calls generate(None, entry_point - 1, entry_point, program_bytes) + sna_data = emitter.generate(None, entry_point - 1, entry_point, program_bytes) + + assert len(sna_data) == 27 + 49152 + + # Check some header values + # snapshot.I is initialized to 0x3F in GenSnapshot + assert sna_data[0] == 0x3F + + # Border color (snapshot.outFE & 7) + # GenSnapshot.outFE = 0x0F, so 0x0F & 7 = 7 + assert sna_data[26] == 7 + + # Check SP + # GenSnapshot: SP = clear_addr - 3 = (0x8000 - 1) - 3 = 0x7FFC = 32764 + # SnaEmitter: SP = snapshot.SP - 2 = 32764 - 2 = 32762 (0x7FFA) + # sna_data[23] = 0xFA, sna_data[24] = 0x7F + assert sna_data[23] == 0xFA + assert sna_data[24] == 0x7F + + # Check PC patched on stack + # snapshot.PCL = 0x9E, snapshot.PCH = 0x1B + # Index in sna_data = 27 + 32762 - 16384 = 16405 + assert sna_data[27 + 32762 - 16384] == 0x9E + assert sna_data[27 + 32762 - 16384 + 1] == 0x1B + + +def test_sna_emitter_emit(tmp_path): + output_file = tmp_path / "test.sna" + emitter = SnaEmitter() + emitter.emit( + output_filename=str(output_file), + program_name="test", + loader_bytes=None, + entry_point=0x8000, + program_bytes=b"\x00" * 100, + aux_bin_blocks=[], + aux_headless_bin_blocks=[], + ) + assert output_file.exists() + assert len(output_file.read_bytes()) == 27 + 49152 diff --git a/tests/outfmt/test_tap.py b/tests/outfmt/test_tap.py new file mode 100644 index 000000000..e6914a6ac --- /dev/null +++ b/tests/outfmt/test_tap.py @@ -0,0 +1,63 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + +from src.outfmt.tap import TAP + + +def test_tap_init(): + tap = TAP() + assert tap.output == b"" + + +def test_tap_standard_block(): + tap = TAP() + tap.standard_block(b"\x01\x02") + # Length (3 -> [0x03, 0x00]) + # Data: \x01\x02 + # Checksum: 0x01 ^ 0x02 = 0x03 + assert tap.output == b"\x03\x00\x01\x02\x03" + + +def test_tap_emit(tmp_path): + output_file = tmp_path / "test.tap" + tap = TAP() + program_bytes = b"\x00\x01" + tap.emit( + output_filename=str(output_file), + program_name="test", + loader_bytes=None, + entry_point=16384, + program_bytes=program_bytes, + aux_bin_blocks=[], + aux_headless_bin_blocks=[], + ) + assert output_file.exists() + content = output_file.read_bytes() + + # Header block + # 0x13 0x00 (Length) + # 0x00 (BLOCK_TYPE_HEADER) + # 0x03 (HEADER_TYPE_CODE) + # "test " (10 bytes) + # 0x02 0x00 (Length 2) + # 0x00 0x40 (Address 16384) + # 0x00 0x80 (32768) + # Checksum + expected_header_content = b"\x00\x03test \x02\x00\x00\x40\x00\x80" + checksum = 0 + for b in expected_header_content: + checksum ^= b + expected_header = b"\x13\x00" + expected_header_content + bytes([checksum]) + assert content.startswith(expected_header) + + # Data block + # 0x04 0x00 (Length) + # 0xFF (BLOCK_TYPE_DATA) + # 0x00 0x01 (data) + # Checksum: 0xFF ^ 0x00 ^ 0x01 = 0xFE + expected_data = b"\x04\x00\xff\x00\x01\xfe" + assert expected_data in content diff --git a/tests/outfmt/test_tzx.py b/tests/outfmt/test_tzx.py new file mode 100644 index 000000000..6bc0f8210 --- /dev/null +++ b/tests/outfmt/test_tzx.py @@ -0,0 +1,77 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + +from src.outfmt.tzx import TZX + + +def test_tzx_init(): + tzx = TZX() + assert tzx.output == b"ZXTape!\x1a\x01\x15" + + +def test_tzx_lh(): + tzx = TZX() + assert tzx.LH(0x1234) == [0x34, 0x12] + assert tzx.LH(0x00FF) == [0xFF, 0x00] + + +def test_tzx_standard_block(): + tzx = TZX() + tzx.output = bytearray() + tzx.standard_block(b"\x01\x02") + # BLOCK_STANDARD (0x10) + # Pause (1000ms -> [0xE8, 0x03]) + # Length (3 -> [0x03, 0x00]) - length of data + 1 for checksum + # Data: \x01\x02 + # Checksum: 0x01 ^ 0x02 = 0x03 + assert tzx.output == b"\x10\xe8\x03\x03\x00\x01\x02\x03" + + +def test_tzx_emit(tmp_path): + output_file = tmp_path / "test.tzx" + tzx = TZX() + program_bytes = b"\x00\x01" + tzx.emit( + output_filename=str(output_file), + program_name="test", + loader_bytes=None, + entry_point=16384, + program_bytes=program_bytes, + aux_bin_blocks=[], + aux_headless_bin_blocks=[], + ) + assert output_file.exists() + content = output_file.read_bytes() + assert content.startswith(b"ZXTape!\x1a\x01\x15") + + # Header block for "test" + # 0x10 (Standard block ID) + # 0xE8 0x03 (1000ms pause) + # 0x13 0x00 (Length of header: 1 + 1 + 10 + 2 + 2 + 2 + 1 = 19 -> 0x13) + # 0x00 (BLOCK_TYPE_HEADER) + # 0x03 (HEADER_TYPE_CODE) + # "test " (10 bytes) + # 0x02 0x00 (Length 2) + # 0x00 0x40 (Address 16384) + # 0x00 0x80 (32768) + # Checksum (XOR of all bytes in block) + expected_header_content = b"\x00\x03test \x02\x00\x00\x40\x00\x80" + checksum = 0 + for b in expected_header_content: + checksum ^= b + expected_header_block = b"\x10\xe8\x03\x13\x00" + expected_header_content + bytes([checksum]) + assert expected_header_block in content + + # Data block + # 0x10 + # 0xE8 0x03 + # 0x04 0x00 (Length of data: 1 (type) + 2 (bytes) + 1 (checksum) = 4) + # 0xFF (BLOCK_TYPE_DATA) + # 0x00 0x01 (data) + # Checksum: 0xFF ^ 0x00 ^ 0x01 = 0xFE + expected_data_block = b"\x10\xe8\x03\x04\x00\xff\x00\x01\xfe" + assert expected_data_block in content diff --git a/tests/outfmt/test_z80.py b/tests/outfmt/test_z80.py new file mode 100644 index 000000000..271bf976f --- /dev/null +++ b/tests/outfmt/test_z80.py @@ -0,0 +1,57 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + +from src.outfmt.z80 import Z80Emitter + + +def test_z80_emitter_generate(): + emitter = Z80Emitter() + program_bytes = b"\x00" * 100 + entry_point = 0x8000 + + # generate(self, loader_bytes, clear_addr, entry_point, program_bytes) + z80_data = emitter.generate(None, entry_point - 1, entry_point, program_bytes) + + # Header is 30 bytes + assert len(z80_data) > 30 + assert z80_data[10] == 0x3F # snapshot.I + + # PC in Z80 version 1 header at offset 6 + # GenSnapshot default PC is 0x1B9E -> PCL=0x9E, PCH=0x1B + assert z80_data[6] == 0x9E + assert z80_data[7] == 0x1B + + # End marker + assert z80_data.endswith(b"\x00\xed\xed\x00") + + +def test_z80_compression(): + emitter = Z80Emitter() + + # program_bytes with 4 EDs to test compression + program_bytes = b"\xed\xed\xed\xed" + entry_point = 0x8000 + + z80_data = emitter.generate(None, entry_point - 1, entry_point, program_bytes) + + # The 4 EDs should be compressed to ED ED 04 ED + assert b"\xed\xed\x04\xed" in z80_data + + +def test_z80_emitter_emit(tmp_path): + output_file = tmp_path / "test.z80" + emitter = Z80Emitter() + emitter.emit( + output_filename=str(output_file), + program_name="test", + loader_bytes=None, + entry_point=0x8000, + program_bytes=b"\x00" * 100, + aux_bin_blocks=[], + aux_headless_bin_blocks=[], + ) + assert output_file.exists() diff --git a/tests/runtime/Makefile b/tests/runtime/Makefile index 4808da7a1..163511909 100644 --- a/tests/runtime/Makefile +++ b/tests/runtime/Makefile @@ -1,7 +1,7 @@ -# vim:ts=4:noet: - -.PHONY: test - -test: - ./test_all - +# vim:ts=4:noet: + +.PHONY: test + +test: + ./test_all + diff --git a/tests/runtime/check_test.py b/tests/runtime/check_test.py index d3572ef19..7f9c443e3 100755 --- a/tests/runtime/check_test.py +++ b/tests/runtime/check_test.py @@ -1,5 +1,12 @@ #!/usr/bin/env python3 +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import signal import sys diff --git a/tests/runtime/run b/tests/runtime/run index 4ea370eef..84cf8be6b 100755 --- a/tests/runtime/run +++ b/tests/runtime/run @@ -1,9 +1,9 @@ -# vim:et:ts=4: - -RUN=$(basename -s .bas $1) -EXT=tzx -rm -f "$RUN.$EXT" -killall fuse 2>/dev/null -../../zxbc.py --$EXT -aB "$@" --debug-memory || exit 1 -#fuse --auto-load --speed=100 --machine=plus2 "$RUN.$EXT" & -speccy "$RUN.$EXT" +# vim:et:ts=4: + +RUN=$(basename -s .bas $1) +EXT=tzx +rm -f "$RUN.$EXT" +killall fuse 2>/dev/null +../../zxbc.py --$EXT -aB "$@" --debug-memory || exit 1 +#fuse --auto-load --speed=100 --machine=plus2 "$RUN.$EXT" & +speccy "$RUN.$EXT" diff --git a/tests/runtime/test_all b/tests/runtime/test_all index 5b2cb3c9f..c43884b2e 100755 --- a/tests/runtime/test_all +++ b/tests/runtime/test_all @@ -1,4 +1,4 @@ -#!/bin/bash - -# run tests in parallel, one per CPU -parallel ./test_case ::: cases/*.bas +#!/bin/bash + +# run tests in parallel, one per CPU +parallel ./test_case ::: cases/*.bas diff --git a/tests/runtime/test_case b/tests/runtime/test_case index 5ca2603f9..792c0fa13 100755 --- a/tests/runtime/test_case +++ b/tests/runtime/test_case @@ -1,17 +1,17 @@ -#!/bin/bash -# vim:et:ts=4: - -# Test a single case (prog.bas file) -# A RAM dump /expected/prog.tzx.scr must exists - -TIMEOUT=180 -TIMEKILL=$((TIMEOUT+30)) - -echo -n "Testing $(basename $1): " -RUN=$(basename -s .bas $1).z80 -EXPECTED=$(basename -s .bas $1).tzx.scr -../../zxbc.py -f z80 -aB -o "$RUN" $1 $(grep "PARAMS:" $1 |cut -d':' -f2-) --debug-memory 2>/dev/null -timeout -k $TIMEKILL $TIMEOUT ./check_test.py "$RUN" "./expected/${EXPECTED}" -RETVAL=$? -rm -f "$RUN" 2>/dev/null -exit $RETVAL +#!/bin/bash +# vim:et:ts=4: + +# Test a single case (prog.bas file) +# A RAM dump /expected/prog.tzx.scr must exists + +TIMEOUT=180 +TIMEKILL=$((TIMEOUT+30)) + +echo -n "Testing $(basename $1): " +RUN=$(basename -s .bas $1).z80 +EXPECTED=$(basename -s .bas $1).tzx.scr +../../zxbc.py -f z80 -aB -o "$RUN" $1 $(grep "PARAMS:" $1 |cut -d':' -f2-) --debug-memory 2>/dev/null +timeout -k $TIMEKILL $TIMEOUT ./check_test.py "$RUN" "./expected/${EXPECTED}" +RETVAL=$? +rm -f "$RUN" 2>/dev/null +exit $RETVAL diff --git a/tests/runtime/update_test.py b/tests/runtime/update_test.py index d05f01252..5b608b222 100755 --- a/tests/runtime/update_test.py +++ b/tests/runtime/update_test.py @@ -1,5 +1,12 @@ #!/usr/bin/env python3 +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + __doc__ = "Creates or update a test result" diff --git a/tests/runtime/update_test.sh b/tests/runtime/update_test.sh index 929195ac6..8f0c116d2 100755 --- a/tests/runtime/update_test.sh +++ b/tests/runtime/update_test.sh @@ -1,7 +1,7 @@ -#!/bin/bash - -# ./run $1 -NAME=$(basename -s .bas $1).z80 -../../zxbc.py -f z80 -aB "$@" --debug-memory || exit 1 -./update_test.py $NAME -mv $NAME.scr expected/$(basename -s .bas $1).tzx.scr +#!/bin/bash + +# ./run $1 +NAME=$(basename -s .bas $1).z80 +../../zxbc.py -f z80 -aB "$@" --debug-memory || exit 1 +./update_test.py $NAME +mv $NAME.scr expected/$(basename -s .bas $1).tzx.scr diff --git a/tests/symbols/__init__.py b/tests/symbols/__init__.py index a8507a547..40645bbb2 100644 --- a/tests/symbols/__init__.py +++ b/tests/symbols/__init__.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import os import sys diff --git a/tests/symbols/test_symbolARGLIST.py b/tests/symbols/test_symbolARGLIST.py index fb39c0ecf..e52908785 100644 --- a/tests/symbols/test_symbolARGLIST.py +++ b/tests/symbols/test_symbolARGLIST.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from unittest import TestCase from src.symbols import sym diff --git a/tests/symbols/test_symbolARRAYACCESS.py b/tests/symbols/test_symbolARRAYACCESS.py index aed45bacb..c876e87e3 100644 --- a/tests/symbols/test_symbolARRAYACCESS.py +++ b/tests/symbols/test_symbolARRAYACCESS.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from io import StringIO from unittest import TestCase @@ -10,6 +17,10 @@ class TestSymbolARRAYACCESS(TestCase): + @staticmethod + def _type_ref(type_: sym.TYPE) -> sym.TYPEREF: + return sym.TYPEREF(type_, 1) + def setUp(self): zxbpp.init() l1 = 1 @@ -19,7 +30,7 @@ def setUp(self): b = sym.BOUND(l1, l2) c = sym.BOUND(l3, l4) self.bounds = sym.BOUNDLIST.make_node(None, b, c) - self.arr = sym.ID("test", 1, type_=Type.ubyte).to_vararray(self.bounds) + self.arr = sym.ID("test", 1, type_ref=self._type_ref(Type.ubyte)).to_vararray(self.bounds) self.arg = sym.ARGLIST( sym.ARGUMENT(sym.NUMBER(2, 1, type_=Type.uinteger), 1), sym.ARGUMENT(sym.NUMBER(3, 1, type_=Type.uinteger), 1), @@ -47,7 +58,7 @@ def test_entry__getter(self): self.assertIs(self.aa1.entry, self.arr) def test_entry__setter(self): - ar2 = sym.ID("test2", 1, type_=Type.ubyte).to_vararray(self.bounds) + ar2 = sym.ID("test2", 1, type_ref=self._type_ref(Type.ubyte)).to_vararray(self.bounds) self.aa1.entry = ar2 self.assertIs(self.aa1.entry, ar2) @@ -59,26 +70,26 @@ def test_scope(self): self.assertEqual(self.aa1.scope, self.arr.scope) def test_make_node(self): - gl.SYMBOL_TABLE.declare_array("test", 1, sym.TYPEREF(self.arr.type_, 1), bounds=self.bounds) + gl.SYMBOL_TABLE.declare_array("test", 1, self.arr.type_, bounds=self.bounds) self.aa2 = sym.ARRAYACCESS.make_node("test", self.arg, lineno=2, filename="fake-filename") self.assertIsInstance(self.aa2, sym.ARRAYACCESS) def test_make_node_fail(self): - gl.SYMBOL_TABLE.declare_array("test", 1, sym.TYPEREF(self.arr.type_, 1), bounds=self.bounds) + gl.SYMBOL_TABLE.declare_array("test", 1, self.arr.type_, bounds=self.bounds) self.arg = sym.ARGLIST(sym.ARGUMENT(sym.NUMBER(2, 1), 1)) self.aa2 = sym.ARRAYACCESS.make_node("test", self.arg, lineno=2, filename="fake-filename") self.assertIsNone(self.aa2) self.assertEqual(self.OUTPUT, "(stdin):2: error: Array 'test' has 2 dimensions, not 1\n") def test_make_node_warn(self): - gl.SYMBOL_TABLE.declare_array("test", 1, sym.TYPEREF(self.arr.type_, 1), bounds=self.bounds) + gl.SYMBOL_TABLE.declare_array("test", 1, self.arr.type_, bounds=self.bounds) self.arg[1] = sym.ARGUMENT(sym.NUMBER(9, 1), 1) self.aa2 = sym.ARRAYACCESS.make_node("test", self.arg, lineno=2, filename="fake-filename") self.assertIsNotNone(self.aa2) self.assertEqual(self.OUTPUT, "(stdin):2: warning: Array 'test' subscript out of range\n") def test_offset(self): - gl.SYMBOL_TABLE.declare_array("test", 1, sym.TYPEREF(self.arr.type_, 1), bounds=self.bounds) + gl.SYMBOL_TABLE.declare_array("test", 1, self.arr.type_, bounds=self.bounds) self.aa2 = sym.ARRAYACCESS.make_node("test", self.arg, lineno=2, filename="fake-filename") self.assertIsInstance(self.aa2, sym.ARRAYACCESS) self.assertIsNotNone(self.aa2.offset) diff --git a/tests/symbols/test_symbolBASICTYPE.py b/tests/symbols/test_symbolBASICTYPE.py index 827868262..b206750d2 100644 --- a/tests/symbols/test_symbolBASICTYPE.py +++ b/tests/symbols/test_symbolBASICTYPE.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from unittest import TestCase from src.api.constants import TYPE @@ -45,7 +52,7 @@ def test__ne__(self): def test_to_signed(self): for type_ in TYPE.types: - if type_ in {TYPE.unknown, TYPE.string, TYPE.boolean}: + if type_ in {TYPE.unknown, TYPE.string, TYPE.boolean, TYPE.nil}: continue t = SymbolBASICTYPE(type_) @@ -55,7 +62,7 @@ def test_to_signed(self): def test_bool(self): for type_ in TYPE.types: t = SymbolBASICTYPE(type_) - if t.type_ == TYPE.unknown: + if t == Type.unknown: self.assertFalse(t) else: self.assertTrue(t) diff --git a/tests/symbols/test_symbolBINARY.py b/tests/symbols/test_symbolBINARY.py index de299c314..22c3cdead 100644 --- a/tests/symbols/test_symbolBINARY.py +++ b/tests/symbols/test_symbolBINARY.py @@ -1,16 +1,23 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from io import StringIO from unittest import TestCase from src.api.config import OPTIONS, Action from src.symbols import sym -from src.symbols.type_ import Type +from src.symbols.type_ import SymbolTYPEREF, Type from src.zxbpp import zxbpp class TestSymbolBINARY(TestCase): def setUp(self): zxbpp.init() - self.l = sym.ID("a", lineno=1, type_=Type.ubyte).to_var() + self.l = sym.ID("a", lineno=1, type_ref=SymbolTYPEREF(Type.ubyte, 0)).to_var() self.r = sym.NUMBER(3, lineno=2) self.b = sym.BINARY("PLUS", self.l, self.r, lineno=3) self.st = sym.STRING("ZXBASIC", lineno=1) diff --git a/tests/symbols/test_symbolBLOCK.py b/tests/symbols/test_symbolBLOCK.py index 073c492ce..d30a96812 100644 --- a/tests/symbols/test_symbolBLOCK.py +++ b/tests/symbols/test_symbolBLOCK.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from unittest import TestCase from src.symbols.sym import BLOCK, NUMBER diff --git a/tests/symbols/test_symbolBOUND.py b/tests/symbols/test_symbolBOUND.py index fe1751952..ed047ab46 100644 --- a/tests/symbols/test_symbolBOUND.py +++ b/tests/symbols/test_symbolBOUND.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from io import StringIO from unittest import TestCase diff --git a/tests/symbols/test_symbolBOUNDLIST.py b/tests/symbols/test_symbolBOUNDLIST.py index 8313e7dc1..8a7c7047a 100644 --- a/tests/symbols/test_symbolBOUNDLIST.py +++ b/tests/symbols/test_symbolBOUNDLIST.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from unittest import TestCase from src.symbols import sym diff --git a/tests/symbols/test_symbolFUNCDECL.py b/tests/symbols/test_symbolFUNCDECL.py index e826b0360..f1ee0f21d 100644 --- a/tests/symbols/test_symbolFUNCDECL.py +++ b/tests/symbols/test_symbolFUNCDECL.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from unittest import TestCase import src.api.global_ as gl @@ -5,13 +12,13 @@ import src.api.symboltable.symboltable from src.api.constants import CLASS from src.symbols.sym import FUNCDECL -from src.symbols.type_ import Type +from src.symbols.type_ import SymbolTYPEREF, Type class TestSymbolFUNCDECL(TestCase): def setUp(self): src.api.global_.SYMBOL_TABLE = src.api.symboltable.symboltable.SymbolTable() - self.f = gl.SYMBOL_TABLE.declare_func("f", 1, type_=Type.ubyte) + self.f = gl.SYMBOL_TABLE.declare_func("f", 1, type_=SymbolTYPEREF(Type.ubyte, 0)) self.s = FUNCDECL(self.f, 1) def test__init__fail(self): diff --git a/tests/symbols/test_symbolFUNCTION.py b/tests/symbols/test_symbolFUNCTION.py index 6573666ff..78db533af 100644 --- a/tests/symbols/test_symbolFUNCTION.py +++ b/tests/symbols/test_symbolFUNCTION.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from unittest import TestCase from src.api.constants import CLASS diff --git a/tests/symbols/test_symbolLABEL.py b/tests/symbols/test_symbolLABEL.py index cfdc833b4..3d8478af1 100644 --- a/tests/symbols/test_symbolLABEL.py +++ b/tests/symbols/test_symbolLABEL.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + __author__ = "boriel" from unittest import TestCase diff --git a/tests/symbols/test_symbolNOP.py b/tests/symbols/test_symbolNOP.py index d8afe0573..7a707f9a6 100644 --- a/tests/symbols/test_symbolNOP.py +++ b/tests/symbols/test_symbolNOP.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from unittest import TestCase from src.symbols.sym import NOP diff --git a/tests/symbols/test_symbolNUMBER.py b/tests/symbols/test_symbolNUMBER.py index f2c01a791..fcd9cdf30 100644 --- a/tests/symbols/test_symbolNUMBER.py +++ b/tests/symbols/test_symbolNUMBER.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from unittest import TestCase from src.api.constants import TYPE diff --git a/tests/symbols/test_symbolSENTENCE.py b/tests/symbols/test_symbolSENTENCE.py index a14fd8820..bcb419f26 100644 --- a/tests/symbols/test_symbolSENTENCE.py +++ b/tests/symbols/test_symbolSENTENCE.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from unittest import TestCase from src.symbols import sym diff --git a/tests/symbols/test_symbolSTRING.py b/tests/symbols/test_symbolSTRING.py index 6ec0d6a4a..adda16774 100644 --- a/tests/symbols/test_symbolSTRING.py +++ b/tests/symbols/test_symbolSTRING.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from unittest import TestCase from src.symbols import sym diff --git a/tests/symbols/test_symbolSTRSLICE.py b/tests/symbols/test_symbolSTRSLICE.py index a383cc657..882e001af 100644 --- a/tests/symbols/test_symbolSTRSLICE.py +++ b/tests/symbols/test_symbolSTRSLICE.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from unittest import TestCase import src.api.global_ as gl @@ -52,6 +59,6 @@ def test_make_node(self): self.assertEqual(s.value, "XB") def test_make_node_wrong(self): - bad_index = sym.ID("a", 0, type_=gl.SYMBOL_TABLE.basic_types[gl.TYPE.string]).to_var() + bad_index = sym.ID("a", 0, type_ref=sym.TYPEREF(gl.SYMBOL_TABLE.basic_types[gl.TYPE.string], 0)).to_var() s = sym.STRSLICE.make_node(1, self.str_, bad_index, bad_index) self.assertIsNone(s) diff --git a/tests/symbols/test_symbolTYPE.py b/tests/symbols/test_symbolTYPE.py index b526a567a..33d40e4ec 100644 --- a/tests/symbols/test_symbolTYPE.py +++ b/tests/symbols/test_symbolTYPE.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from unittest import TestCase from src.api.constants import TYPE @@ -7,11 +14,12 @@ class TestSymbolTYPE(TestCase): def test__eq__(self): for t1_ in TYPE.types: - t1 = SymbolBASICTYPE(t1_) + t = SymbolBASICTYPE(t1_) for t2_ in TYPE.types: - t2 = SymbolBASICTYPE(t2_) - t = SymbolTYPE("test_type", 0, t1, t2) - tt = SymbolTYPE("other_type", 0, t) + if t1_ == t2_: + continue + + tt = SymbolBASICTYPE(t2_) self.assertTrue(t == t) self.assertFalse(t != t) self.assertFalse(tt == t) @@ -39,14 +47,13 @@ def test_is_alias(self): def test_size(self): for t1_ in TYPE.types: - t1 = SymbolBASICTYPE(t1_) + t = SymbolBASICTYPE(t1_) for t2_ in TYPE.types: - t2 = SymbolBASICTYPE(t2_) - t = SymbolTYPE("test_type", 0, t1, t2) - self.assertEqual(t.size, t1.size + t2.size) + tt = SymbolBASICTYPE(t2_) + self.assertEqual(TYPE.size(t1_) + TYPE.size(t2_), t.size + tt.size) def test_cmp_types(self): """Test == operator for different types""" tr = SymbolTYPEREF(Type.unknown, 0) self.assertTrue(tr == Type.unknown) - self.assertRaises(AssertionError, tr.__eq__, TYPE.unknown) + self.assertRaises(NotImplementedError, tr.__eq__, "dummy") diff --git a/tests/symbols/test_symbolTYPEALIAS.py b/tests/symbols/test_symbolTYPEALIAS.py index fc36245d2..0c3ea2dcf 100644 --- a/tests/symbols/test_symbolTYPEALIAS.py +++ b/tests/symbols/test_symbolTYPEALIAS.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from unittest import TestCase from src.api.constants import TYPE @@ -8,16 +15,16 @@ class TestSymbolTYPEALIAS(TestCase): def test__eq__(self): for type_ in TYPE.types: t = SymbolBASICTYPE(type_) - ta = SymbolTYPEALIAS("alias", 0, t) + ta = SymbolTYPEALIAS("alias", "", alias=t, lineno=0) self.assertEqual(t.size, ta.size) self.assertTrue(ta == ta) - self.assertTrue(t == ta) - self.assertTrue(ta == t) + self.assertTrue(t.final == ta.final) + self.assertTrue(ta.final == t.final) def test_is_alias(self): for type_ in TYPE.types: t = SymbolBASICTYPE(type_) - ta = SymbolTYPEALIAS("alias", 0, t) + ta = SymbolTYPEALIAS("alias", "", alias=t, lineno=0) self.assertTrue(ta.is_alias) self.assertTrue(ta.is_basic) self.assertFalse(t.is_alias) diff --git a/tests/symbols/test_symbolTYPECAST.py b/tests/symbols/test_symbolTYPECAST.py index 681aaa396..b5c5f8a25 100644 --- a/tests/symbols/test_symbolTYPECAST.py +++ b/tests/symbols/test_symbolTYPECAST.py @@ -1,9 +1,16 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from io import StringIO from unittest import TestCase from src.api.config import OPTIONS, Action from src.symbols.sym import ID, NUMBER, TYPECAST -from src.symbols.type_ import Type +from src.symbols.type_ import SymbolTYPEREF, Type from src.zxbpp import zxbpp @@ -41,7 +48,7 @@ def test_make_node(self): def test_make_const(self): """Must return a number""" - v = ID("a", lineno=1, type_=Type.byte_).to_const(NUMBER(3, lineno=1)) + v = ID("a", lineno=1, type_ref=SymbolTYPEREF(Type.byte_, 0)).to_const(NUMBER(3, lineno=1)) t = TYPECAST.make_node(Type.float_, v, lineno=2) self.assertIsInstance(t, NUMBER) self.assertEqual(t, 3) diff --git a/tests/symbols/test_symbolVAR.py b/tests/symbols/test_symbolVAR.py index d2335f5a2..24abfdeab 100644 --- a/tests/symbols/test_symbolVAR.py +++ b/tests/symbols/test_symbolVAR.py @@ -1,8 +1,15 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + from unittest import TestCase from src.api.constants import SCOPE from src.symbols import sym -from src.symbols.type_ import Type +from src.symbols.type_ import SymbolTYPEREF, Type class TestSymbolVAR(TestCase): @@ -14,7 +21,7 @@ def test__init__fail(self): def test_size(self): self.assertIsNone(self.v.type_) - self.v.type_ = Type.byte_ + self.v.type_ = SymbolTYPEREF(Type.byte_, 0) self.assertEqual(self.v.type_, Type.byte_) def test_set_value(self): @@ -36,7 +43,7 @@ def test_t_const(self): self.assertEqual(self.v.t, "54321") def test_type_(self): - self.v.type_ = Type.byte_ + self.v.type_ = SymbolTYPEREF(Type.byte_, 0) self.assertEqual(self.v.type_, Type.byte_) def test_type_fail(self): diff --git a/tests/symbols/test_symbolVARARRAY.py b/tests/symbols/test_symbolVARARRAY.py index 9e4d5c297..1dfbd2d4c 100644 --- a/tests/symbols/test_symbolVARARRAY.py +++ b/tests/symbols/test_symbolVARARRAY.py @@ -1,3 +1,10 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import functools from unittest import TestCase @@ -16,7 +23,7 @@ def setUp(self): b = sym.BOUND(l1, l2) c = sym.BOUND(l3, l4) self.bounds = sym.BOUNDLIST.make_node(None, b, c) - self.arr = sym.ID("test", 1, type_=Type.ubyte).to_vararray(self.bounds) + self.arr = sym.ID("test", 1, type_ref=sym.TYPEREF(Type.ubyte, 0)).to_vararray(self.bounds) def test__init__fail(self): self.assertRaises(AssertionError, sym.ID("test", 2).to_vararray, "blahblah") diff --git a/tests/zxbc/test_build_parsetab.py b/tests/zxbc/test_build_parsetab.py deleted file mode 100644 index 775b7e209..000000000 --- a/tests/zxbc/test_build_parsetab.py +++ /dev/null @@ -1,19 +0,0 @@ -from unittest import mock - -from src.ply.yacc import LRParser - - -class TestBuildParsetab: - @mock.patch("src.api.utils.load_object", return_value=None) - @mock.patch("src.api.utils.save_object", lambda key, obj: obj) - def test_build_parsetab(self, mock_load_object): - from src.zxbc import zxbparser - - parser = zxbparser.parser - assert isinstance(parser, LRParser), "Could not generate an rparser" - - def test_loads_parsetab(self): - from src.zxbc import zxbparser - - parser = zxbparser.parser - assert isinstance(parser, LRParser), "Could not load an rparser" diff --git a/tools/consolidate-libs.py b/tools/consolidate-libs.py index 4ca91ca67..8ae4354dc 100755 --- a/tools/consolidate-libs.py +++ b/tools/consolidate-libs.py @@ -1,5 +1,12 @@ #!/usr/bin/env python +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + __doc__ = """Scans src/lib//** and does hardlinks to files with the same name and content""" @@ -12,6 +19,17 @@ ROOT_DIR = Path(__file__).parent.parent.absolute() / "src" / "lib" / "arch" ARCHS = "zx48k", "zxnext" +COPYRIGHT_HEADER = """' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) +' ---------------------------------------------------------------- +""" +ARCH_PRIO = { + "zx48k": 0, + "zxnext": 1, +} class FileInfo(NamedTuple): @@ -40,16 +58,43 @@ def scan_arch(root: Path) -> dict[FileInfo, list[str]]: return result +def file_arch(fname: str) -> str: + """Returns the arch the file belongs to. + The arch is extracted from the filename path. + """ + return re.match(r"^.*?/src/lib/arch/([^/]+)/.*$", fname).group(1) + + +def relative_filename(fname: str) -> str: + return re.sub(r"^.*?/src/lib/arch/[^/]+/[^/]+/", "", fname) + + def fold_files(scan: dict[FileInfo, list[str]]) -> None: for path, files in scan.items(): if len(files) == 1: continue - main_file = files[0] - for file in files[1:]: - print(f"Linking {main_file} to {file}") - os.unlink(file) - os.link(main_file, file) + # Get the file with the arch with the highest priority (which is the lowest number) + main_file = min(files, key=lambda f: ARCH_PRIO[file_arch(f)]) + print(main_file) + main_file_basename = relative_filename(main_file) + main_file_ext = os.path.splitext(main_file_basename)[1] + if main_file_ext not in (".asm", ".bas"): + continue + + header = COPYRIGHT_HEADER + if main_file_ext == ".asm": + header = re.sub(r"(^|\n[ \t]*)'", r"\1;", COPYRIGHT_HEADER) + + arch = file_arch(main_file) + for file in files: + if file == main_file: + continue + + print(f"Linking {file} to {main_file} with an include") + with open(file, "wt", encoding="utf-8") as f: + f.write(header) + f.write(f"\n#include once [arch:{arch}] <{main_file_basename}>\n") def main(): diff --git a/tools/fixed.py b/tools/fixed.py index 35738452c..1efc28da0 100644 --- a/tools/fixed.py +++ b/tools/fixed.py @@ -1,4 +1,9 @@ -# A program that displays fixed point numbers in hexa +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import sys diff --git a/tools/mulfix.py b/tools/mulfix.py index 4ad4b415f..6f651de30 100755 --- a/tools/mulfix.py +++ b/tools/mulfix.py @@ -1,6 +1,11 @@ #!/usr/bin/env python -# A program that displays multiplied fixed point numbers +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import sys diff --git a/tools/nextcreator.py b/tools/nextcreator.py index a03b6ac25..0f8edb696 100755 --- a/tools/nextcreator.py +++ b/tools/nextcreator.py @@ -1,5 +1,12 @@ #!/usr/bin/env python3 +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import argparse import os import re diff --git a/tools/normalize_asm.py b/tools/normalize_asm.py index c48918af0..4f06b37eb 100755 --- a/tools/normalize_asm.py +++ b/tools/normalize_asm.py @@ -1,4 +1,12 @@ #!/usr/bin/env python3 + +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import sys INDENT = 4 * " " diff --git a/tools/normalize_bas.py b/tools/normalize_bas.py index c394f14d8..50ce40efb 100755 --- a/tools/normalize_bas.py +++ b/tools/normalize_bas.py @@ -1,5 +1,12 @@ #!/usr/bin/env python3 +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import re import sys diff --git a/tools/profile.sh b/tools/profile.sh index e0baf1687..3ffdc6530 100755 --- a/tools/profile.sh +++ b/tools/profile.sh @@ -1,4 +1,4 @@ -#!/bin/bash - -python -m cProfile "$@" - +#!/bin/bash + +python -m cProfile "$@" + diff --git a/tools/scrview.py b/tools/scrview.py index c3288ea2d..328002b73 100755 --- a/tools/scrview.py +++ b/tools/scrview.py @@ -1,5 +1,12 @@ #!/usr/bin/env python3 +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- + import sys import pygame diff --git a/zxbasm.py b/zxbasm.py index 5faa79853..a816a2439 100755 --- a/zxbasm.py +++ b/zxbasm.py @@ -1,14 +1,11 @@ #!/usr/bin/env python3 -# vim: ts=4:et:sw=4 -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# -# This is the Parser for the ZXBASM (ZXBasic Assembler) -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import os import sys diff --git a/zxbc.py b/zxbc.py index 8db920890..b4835b996 100755 --- a/zxbc.py +++ b/zxbc.py @@ -1,5 +1,11 @@ #!/usr/bin/env python3 -# vim: ts=4:sw=4:et: + +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import os import sys diff --git a/zxbpp.py b/zxbpp.py index b9bc36204..1ef0a66f5 100755 --- a/zxbpp.py +++ b/zxbpp.py @@ -1,14 +1,11 @@ #!/usr/bin/env python3 -# vim: ts=4:sw=4:et: -# ---------------------------------------------------------------------- -# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel) -# -# This program is Free Software and is released under the terms of -# the GNU General License -# -# This is the Parser for the ZXBpp (ZXBasic Preprocessor) -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors. +# See the file CONTRIBUTORS.md for copyright details. +# See https://www.gnu.org/licenses/agpl-3.0.html for details. +# -------------------------------------------------------------------- import os import sys