From 1cedffa99e258733b9855d6a9861d20aa55bb24d Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 28 Sep 2025 12:25:29 +0200 Subject: [PATCH 001/169] Review and change al files to AGPLv3 --- CONTRIBUTORS.md | 35 +++++++++++++++++++ src/__init__.py | 6 ++++ src/api/__init__.py | 6 ++++ src/api/check.py | 15 ++++---- src/api/config.py | 14 ++++---- src/api/constants.py | 14 ++++---- src/api/dataref.py | 7 ++++ src/api/debug.py | 9 +++-- src/api/decorator.py | 7 ++++ src/api/errmsg.py | 14 ++++---- src/api/exception.py | 17 ++++----- src/api/fp.py | 7 +++- src/api/global_.py | 13 ++++--- src/api/opcodestemps.py | 12 +++---- src/api/optimize.py | 7 ++++ src/api/options.py | 14 ++++---- src/api/python_version_check.py | 7 ++++ src/api/string_labels.py | 12 +++---- src/api/symboltable/__init__.py | 6 ++++ src/api/symboltable/scope.py | 14 ++++---- src/api/symboltable/symboltable.py | 15 ++++---- src/api/tmp_labels.py | 7 ++++ src/api/utils.py | 7 ++++ src/arch/__init__.py | 12 +++---- src/arch/interface/__init__.py | 6 ++++ src/arch/interface/backend.py | 7 ++++ src/arch/interface/optimizer.py | 7 ++++ src/arch/interface/quad.py | 7 ++++ src/arch/z80/__init__.py | 7 +++- src/arch/z80/backend/_16bit.py | 14 ++++---- src/arch/z80/backend/_32bit.py | 13 ++++--- src/arch/z80/backend/_8bit.py | 15 ++++---- src/arch/z80/backend/__init__.py | 7 ++++ src/arch/z80/backend/_array.py | 15 ++++---- src/arch/z80/backend/_f16.py | 14 ++++---- src/arch/z80/backend/_float.py | 18 ++++------ src/arch/z80/backend/_parray.py | 15 ++++---- src/arch/z80/backend/_pload.py | 16 ++++----- src/arch/z80/backend/_str.py | 15 ++++---- src/arch/z80/backend/common.py | 7 ++++ src/arch/z80/backend/exception.py | 7 +++- src/arch/z80/backend/generic.py | 9 +++-- src/arch/z80/backend/icinfo.py | 7 ++++ src/arch/z80/backend/icinstruction.py | 7 ++++ src/arch/z80/backend/main.py | 7 ++++ src/arch/z80/backend/quad.py | 7 ++++ src/arch/z80/backend/runtime/__init__.py | 7 ++++ src/arch/z80/backend/runtime/core.py | 7 +++- src/arch/z80/backend/runtime/datarestore.py | 7 +++- src/arch/z80/backend/runtime/io.py | 8 +++-- src/arch/z80/backend/runtime/labels.py | 8 ++++- src/arch/z80/backend/runtime/math.py | 7 +++- src/arch/z80/backend/runtime/misc.py | 7 +++- src/arch/z80/backend/runtime/namespace.py | 7 +++- src/arch/z80/backend/runtime/random.py | 7 +++- src/arch/z80/beep.py | 8 ++++- src/arch/z80/optimizer/__init__.py | 7 ++++ src/arch/z80/optimizer/asm.py | 7 ++++ src/arch/z80/optimizer/basicblock.py | 7 ++++ src/arch/z80/optimizer/cpustate.py | 7 ++++ src/arch/z80/optimizer/errors.py | 7 ++++ src/arch/z80/optimizer/flow_graph.py | 7 ++++ src/arch/z80/optimizer/helpers.py | 7 ++++ src/arch/z80/optimizer/labelinfo.py | 7 ++++ src/arch/z80/optimizer/labels_dict.py | 7 ++++ src/arch/z80/optimizer/main.py | 7 ++++ src/arch/z80/optimizer/memcell.py | 7 ++++ src/arch/z80/optimizer/patterns.py | 7 ++++ src/arch/z80/peephole/__init__.py | 6 ++++ src/arch/z80/peephole/engine.py | 7 ++++ src/arch/z80/peephole/evaluator.py | 7 ++++ src/arch/z80/peephole/parser.py | 7 ++++ src/arch/z80/peephole/pattern.py | 7 ++++ src/arch/z80/peephole/template.py | 7 ++++ src/arch/z80/visitor/__init__.py | 6 ++++ src/arch/z80/visitor/builtin_translator.py | 7 ++++ src/arch/z80/visitor/function_translator.py | 7 ++++ src/arch/z80/visitor/translator.py | 7 ++++ .../z80/visitor/translator_inst_visitor.py | 7 ++++ src/arch/z80/visitor/translator_visitor.py | 7 ++++ src/arch/z80/visitor/unary_op_translator.py | 7 ++++ src/arch/z80/visitor/var_translator.py | 7 ++++ src/arch/zx48k/__init__.py | 7 +++- src/arch/zx48k/backend/__init__.py | 7 ++++ src/arch/zx48k/beep.py | 8 ++++- src/arch/zxnext/__init__.py | 7 +++- src/arch/zxnext/backend/_8bit.py | 7 ++++ src/arch/zxnext/backend/__init__.py | 7 ++++ src/arch/zxnext/backend/generic.py | 9 +++-- src/arch/zxnext/backend/main.py | 7 ++++ src/arch/zxnext/optimizer/__init__.py | 7 ++++ src/arch/zxnext/optimizer/basicblock.py | 7 ++++ src/arch/zxnext/optimizer/cpustate.py | 7 ++++ src/arch/zxnext/optimizer/main.py | 7 ++++ src/arch/zxnext/optimizer/memcell.py | 7 ++++ src/arch/zxnext/peephole/__init__.py | 7 ++++ src/ast/__init__.py | 7 ++++ src/ast/ast.py | 14 ++++---- src/ast/tree.py | 7 ++++ src/outfmt/__init__.py | 7 ++++ src/outfmt/binary.py | 15 ++++---- src/outfmt/codeemitter.py | 15 ++++---- src/outfmt/gensnapshot.py | 23 +++--------- src/outfmt/sna.py | 24 +++---------- src/outfmt/tap.py | 16 ++++----- src/outfmt/tzx.py | 17 ++++----- src/outfmt/z80.py | 24 +++---------- src/parsetab/__init__.py | 6 ++++ src/ply/lex.py | 2 +- src/symbols/__init__.py | 6 ++++ src/symbols/arglist.py | 13 +++---- src/symbols/argument.py | 15 ++++---- src/symbols/arrayaccess.py | 12 +++---- src/symbols/arraydecl.py | 15 ++++---- src/symbols/arrayload.py | 14 ++++---- src/symbols/asm.py | 14 ++++---- src/symbols/binary.py | 12 +++---- src/symbols/block.py | 14 ++++---- src/symbols/bound.py | 14 ++++---- src/symbols/boundlist.py | 15 ++++---- src/symbols/builtin.py | 12 +++---- src/symbols/call.py | 14 ++++---- src/symbols/constexpr.py | 14 ++++---- src/symbols/funccall.py | 14 ++++---- src/symbols/funcdecl.py | 15 ++++---- src/symbols/id_/__init__.py | 7 ++++ src/symbols/id_/_id.py | 14 ++++---- src/symbols/id_/interface.py | 7 ++++ src/symbols/id_/ref/__init__.py | 7 ++++ src/symbols/id_/ref/arrayref.py | 7 ++++ src/symbols/id_/ref/constref.py | 7 ++++ src/symbols/id_/ref/funcref.py | 7 ++++ src/symbols/id_/ref/labelref.py | 7 ++++ src/symbols/id_/ref/symbolref.py | 7 ++++ src/symbols/id_/ref/varref.py | 7 ++++ src/symbols/nop.py | 14 ++++---- src/symbols/number.py | 15 ++++---- src/symbols/paramlist.py | 14 ++++---- src/symbols/sentence.py | 14 ++++---- src/symbols/string_.py | 15 ++++---- src/symbols/strslice.py | 14 ++++---- src/symbols/sym.py | 14 ++++---- src/symbols/symbol_.py | 15 ++++---- src/symbols/type_.py | 14 ++++---- src/symbols/typecast.py | 14 ++++---- src/symbols/unary.py | 14 ++++---- src/symbols/vardecl.py | 14 ++++---- src/zxbasm/__init__.py | 7 ++++ src/zxbasm/asm.py | 7 ++++ src/zxbasm/asm_instruction.py | 7 +++- src/zxbasm/asmlex.py | 17 ++++----- src/zxbasm/asmparse.py | 17 ++++----- src/zxbasm/basic.py | 14 ++++---- src/zxbasm/expr.py | 7 ++++ src/zxbasm/global_.py | 7 ++++ src/zxbasm/label.py | 7 ++++ src/zxbasm/memory.py | 7 ++++ src/zxbasm/version.py | 7 ++++ src/zxbasm/z80.py | 7 +++- src/zxbasm/zxbasm.py | 17 ++++----- src/zxbasm/zxnext.py | 7 ++++ src/zxbc/__init__.py | 14 ++++---- src/zxbc/args_config.py | 7 ++++ src/zxbc/args_parser.py | 7 ++++ src/zxbc/keywords.py | 17 ++++----- src/zxbc/version.py | 7 ++++ src/zxbc/zxbc.py | 8 ++++- src/zxbc/zxblex.py | 15 ++++---- src/zxbc/zxbparser.py | 13 ++++--- src/zxbpp/__init__.py | 7 ++++ src/zxbpp/base_pplex.py | 14 ++++---- src/zxbpp/prepro/__init__.py | 7 +++- src/zxbpp/prepro/args.py | 7 +++- src/zxbpp/prepro/builtinmacro.py | 7 ++++ src/zxbpp/prepro/definestable.py | 7 +++- src/zxbpp/prepro/exceptions.py | 8 +++++ src/zxbpp/prepro/id_.py | 7 +++- src/zxbpp/prepro/macrocall.py | 7 +++- src/zxbpp/prepro/operators.py | 7 +++- src/zxbpp/prepro/output.py | 7 +++- src/zxbpp/zxbasmpplex.py | 14 ++++---- src/zxbpp/zxbpp.py | 17 ++++----- src/zxbpp/zxbpplex.py | 14 ++++---- tests/__init__.py | 7 ++++ tests/api/__init__.py | 6 ++++ tests/api/test_arg_parser.py | 7 ++++ tests/api/test_check.py | 7 ++++ tests/api/test_config.py | 7 ++++ tests/api/test_symbolTable.py | 7 ++++ tests/api/test_utils.py | 7 ++++ tests/arch/zx48k/backend/test_memcell.py | 7 ++++ tests/arch/zx48k/optimizer/common.py | 7 ++++ tests/arch/zx48k/optimizer/test_basicblock.py | 7 ++++ tests/arch/zx48k/optimizer/test_cpustate.py | 7 ++++ tests/arch/zx48k/optimizer/test_helpers.py | 7 ++++ .../arch/zx48k/optimizer/test_o1_optimizer.py | 7 ++++ tests/arch/zx48k/optimizer/test_optimizer.py | 7 ++++ .../zx48k/optimizer/test_optimizer_asm.py | 7 ++++ tests/arch/zx48k/peephole/test_evaluator.py | 7 ++++ tests/arch/zx48k/peephole/test_parser.py | 7 ++++ tests/arch/zx48k/peephole/test_pattern.py | 7 ++++ tests/arch/zx48k/peephole/test_template.py | 7 ++++ tests/cmdline/__init__.py | 7 ++++ tests/cmdline/test_zxb.py | 7 ++++ tests/functional/test.py | 8 ++++- tests/functional/test_.py | 7 +++- tests/functional/test_asm.py | 7 ++++ tests/functional/test_basic.py | 7 ++++ tests/functional/test_prepro.py | 7 ++++ tests/runtime/check_test.py | 7 ++++ tests/runtime/update_test.py | 7 ++++ tests/symbols/__init__.py | 7 ++++ tests/symbols/test_symbolARGLIST.py | 7 ++++ tests/symbols/test_symbolARRAYACCESS.py | 7 ++++ tests/symbols/test_symbolBASICTYPE.py | 7 ++++ tests/symbols/test_symbolBINARY.py | 7 ++++ tests/symbols/test_symbolBLOCK.py | 7 ++++ tests/symbols/test_symbolBOUND.py | 7 ++++ tests/symbols/test_symbolBOUNDLIST.py | 7 ++++ tests/symbols/test_symbolFUNCDECL.py | 7 ++++ tests/symbols/test_symbolFUNCTION.py | 7 ++++ tests/symbols/test_symbolLABEL.py | 7 ++++ tests/symbols/test_symbolNOP.py | 7 ++++ tests/symbols/test_symbolNUMBER.py | 7 ++++ tests/symbols/test_symbolSENTENCE.py | 7 ++++ tests/symbols/test_symbolSTRING.py | 7 ++++ tests/symbols/test_symbolSTRSLICE.py | 7 ++++ tests/symbols/test_symbolTYPE.py | 7 ++++ tests/symbols/test_symbolTYPEALIAS.py | 7 ++++ tests/symbols/test_symbolTYPECAST.py | 7 ++++ tests/symbols/test_symbolVAR.py | 7 ++++ tests/symbols/test_symbolVARARRAY.py | 7 ++++ tests/zxbc/__init__.py | 6 ++++ tests/zxbc/test_build_parsetab.py | 7 ++++ tools/consolidate-libs.py | 7 ++++ tools/fixed.py | 7 +++- tools/mulfix.py | 7 +++- tools/nextcreator.py | 7 ++++ tools/normalize_asm.py | 8 +++++ tools/normalize_bas.py | 7 ++++ tools/scrview.py | 7 ++++ zxbasm.py | 15 ++++---- zxbc.py | 8 ++++- zxbpp.py | 15 ++++---- 244 files changed, 1634 insertions(+), 659 deletions(-) create mode 100644 CONTRIBUTORS.md 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/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..b728d0511 100644 --- a/src/api/check.py +++ b/src/api/check.py @@ -1,12 +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. +# -------------------------------------------------------------------- from src.api import config, errmsg, global_ from src.api.constants import CLASS, SCOPE diff --git a/src/api/config.py b/src/api/config.py index 66361b27d..9923394e9 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 diff --git a/src/api/constants.py b/src/api/constants.py index f5907072a..888a89b95 100644 --- a/src/api/constants.py +++ b/src/api/constants.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 enum import os 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..8c1d57057 100644 --- a/src/api/decorator.py +++ b/src/api/decorator.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 diff --git a/src/api/errmsg.py b/src/api/errmsg.py index 13241294c..ec143223d 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 diff --git a/src/api/exception.py b/src/api/exception.py index 97544bb40..a1c2d849b 100644 --- a/src/api/exception.py +++ b/src/api/exception.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 -# ---------------------------------------------------------------------- - - -# ------------------------- ERROR exception classes --------------------------- +# -------------------------------------------------------------------- +# 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__ = [ "Error", 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..3bf9bac85 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 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..380ac699b 100644 --- a/src/api/optimize.py +++ b/src/api/optimize.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 symtable from collections.abc import Generator from typing import Any, NamedTuple 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..348707bc4 100644 --- a/src/api/python_version_check.py +++ b/src/api/python_version_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 sys from typing import Final 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..9e938c120 100644 --- a/src/api/symboltable/scope.py +++ b/src/api/symboltable/scope.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 collections import OrderedDict from typing import Optional diff --git a/src/api/symboltable/symboltable.py b/src/api/symboltable/symboltable.py index 176a6a4eb..1deaf810a 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_ 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/api/utils.py b/src/api/utils.py index 0a9561cb8..0d9378ac2 100644 --- a/src/api/utils.py +++ b/src/api/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 errno import os import shelve diff --git a/src/arch/__init__.py b/src/arch/__init__.py index 2344039d2..fe5ba2cf3 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 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/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..4606dafce 100644 --- a/src/arch/interface/quad.py +++ b/src/arch/interface/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 dataclasses import dataclass from src.arch.z80.backend.icinstruction import ICInstruction 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..f1086ffe3 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 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..1d2e60f62 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 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..33b21e429 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 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..adcd20076 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 diff --git a/src/arch/z80/backend/common.py b/src/arch/z80/backend/common.py index b49f0e3f3..436413833 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 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..630db6514 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 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..32f09ff73 100644 --- a/src/arch/z80/backend/icinstruction.py +++ b/src/arch/z80/backend/icinstruction.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 enum import StrEnum from functools import lru_cache from typing import Any 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..d796c55c3 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 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..9c041d2c2 100644 --- a/src/arch/z80/optimizer/helpers.py +++ b/src/arch/z80/optimizer/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 collections.abc import Iterable, Mapping from typing import Any, Final, TypeVar, cast 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..d76ff8e4c 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 diff --git a/src/arch/z80/peephole/evaluator.py b/src/arch/z80/peephole/evaluator.py index 7234841bb..0182c5580 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 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..ed8da017b 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 diff --git a/src/arch/z80/visitor/function_translator.py b/src/arch/z80/visitor/function_translator.py index b2b94a0b0..9189e1579 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 diff --git a/src/arch/z80/visitor/translator.py b/src/arch/z80/visitor/translator.py index 8b47a634b..601cdbd98 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 diff --git a/src/arch/z80/visitor/translator_inst_visitor.py b/src/arch/z80/visitor/translator_inst_visitor.py index b31d991f2..cc218d4c9 100644 --- a/src/arch/z80/visitor/translator_inst_visitor.py +++ b/src/arch/z80/visitor/translator_inst_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 src.api.constants import TYPE from src.api.debug import __DEBUG__ from src.arch.interface.quad import Quad diff --git a/src/arch/z80/visitor/translator_visitor.py b/src/arch/z80/visitor/translator_visitor.py index b4d072ab8..7debfdd27 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 diff --git a/src/arch/z80/visitor/unary_op_translator.py b/src/arch/z80/visitor/unary_op_translator.py index 3984eb289..e3486b7b1 100644 --- a/src/arch/z80/visitor/unary_op_translator.py +++ b/src/arch/z80/visitor/unary_op_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.constants import SCOPE from src.arch.z80.visitor.translator_visitor import TranslatorVisitor diff --git a/src/arch/z80/visitor/var_translator.py b/src/arch/z80/visitor/var_translator.py index 276ecb190..b2737d1ea 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 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/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/ast/__init__.py b/src/ast/__init__.py index cf950f96f..9edeeea6b 100644 --- a/src/ast/__init__.py +++ b/src/ast/__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 .ast import Ast, NodeVisitor, types from .tree import Tree diff --git a/src/ast/ast.py b/src/ast/ast.py index 0ad86e4b3..2fb960f46 100644 --- a/src/ast/ast.py +++ b/src/ast/ast.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 types from collections.abc import Callable diff --git a/src/ast/tree.py b/src/ast/tree.py index db2dd3678..389f9b648 100644 --- a/src/ast/tree.py +++ b/src/ast/tree.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 collections.abc 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..9c4a64f4f 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 diff --git a/src/outfmt/codeemitter.py b/src/outfmt/codeemitter.py index aa19ec687..42f537fee 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 diff --git a/src/outfmt/gensnapshot.py b/src/outfmt/gensnapshot.py index ba1e5b605..cf6e2b712 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: diff --git a/src/outfmt/sna.py b/src/outfmt/sna.py index b934b79d0..9d07ccbc2 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 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..fa679923b 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 diff --git a/src/outfmt/z80.py b/src/outfmt/z80.py index 2cb44ae46..34ae1ba96 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 diff --git a/src/parsetab/__init__.py b/src/parsetab/__init__.py index e69de29bb..906667a8d 100644 --- a/src/parsetab/__init__.py +++ b/src/parsetab/__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/ply/lex.py b/src/ply/lex.py index de011fe13..7037c09d3 100644 --- a/src/ply/lex.py +++ b/src/ply/lex.py @@ -714,7 +714,7 @@ def validate_module(self, module): # # Build all of the regular expression rules from definitions in the supplied module # ----------------------------------------------------------------------------- -def lex(*, module=None, object=None, debug=False, +def lex(*, module=None, object=None, debug=False, reflags=int(re.VERBOSE), debuglog=None, errorlog=None): global lexer 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..7e3fb037e 100644 --- a/src/symbols/argument.py +++ b/src/symbols/argument.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 global_ as gl from src.api.config import OPTIONS diff --git a/src/symbols/arrayaccess.py b/src/symbols/arrayaccess.py index f95806e06..f5bbe90da 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 diff --git a/src/symbols/arraydecl.py b/src/symbols/arraydecl.py index 3aff49d26..a409dbd50 100644 --- a/src/symbols/arraydecl.py +++ b/src/symbols/arraydecl.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 .symbol_ import Symbol diff --git a/src/symbols/arrayload.py b/src/symbols/arrayload.py index d6c7f0738..cbdb6ee55 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 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..1d3c256fc 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 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..5c512deac 100644 --- a/src/symbols/builtin.py +++ b/src/symbols/builtin.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 src.api import check diff --git a/src/symbols/call.py b/src/symbols/call.py index ff7d9f13a..4db8c950f 100644 --- a/src/symbols/call.py +++ b/src/symbols/call.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 collections.abc import Iterable from typing import Optional 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..3757abd92 100644 --- a/src/symbols/funcdecl.py +++ b/src/symbols/funcdecl.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 src.api.symboltable.scope from src.api import global_ from src.api.constants import 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..ca9fc8836 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 diff --git a/src/symbols/id_/interface.py b/src/symbols/id_/interface.py index 85983c9ea..dc99fda37 100644 --- a/src/symbols/id_/interface.py +++ b/src/symbols/id_/interface.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 from src.api.constants import CLASS, SCOPE 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..344e63fb9 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 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..51912b143 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 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..8f93a0239 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 diff --git a/src/symbols/symbol_.py b/src/symbols/symbol_.py index 7f63a3aae..c1932c00b 100644 --- a/src/symbols/symbol_.py +++ b/src/symbols/symbol_.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 collections import Counter diff --git a/src/symbols/type_.py b/src/symbols/type_.py index 02fbb49fc..be13c7f8e 100644 --- a/src/symbols/type_.py +++ b/src/symbols/type_.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.config import OPTIONS from src.api.constants import CLASS, TYPE diff --git a/src/symbols/typecast.py b/src/symbols/typecast.py index db0a98a46..664e801fb 100644 --- a/src/symbols/typecast.py +++ b/src/symbols/typecast.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, errmsg from src.api.errmsg import error diff --git a/src/symbols/unary.py b/src/symbols/unary.py index 5021c9d43..963232f38 100644 --- a/src/symbols/unary.py +++ b/src/symbols/unary.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.symbols.number import SymbolNUMBER 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..ec738a0a9 100755 --- a/src/zxbasm/asmlex.py +++ b/src/zxbasm/asmlex.py @@ -1,14 +1,11 @@ #!/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 diff --git a/src/zxbasm/asmparse.py b/src/zxbasm/asmparse.py index e1f8518b8..be4bc0284 100755 --- a/src/zxbasm/asmparse.py +++ b/src/zxbasm/asmparse.py @@ -1,14 +1,11 @@ #!/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-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 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..b0a779f8e 100644 --- a/src/zxbasm/expr.py +++ b/src/zxbasm/expr.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.ast import Ast from src.ast.tree import NotAnAstError 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..a06613413 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 diff --git a/src/zxbasm/version.py b/src/zxbasm/version.py index cca3a688b..a526b3b97 100644 --- a/src/zxbasm/version.py +++ b/src/zxbasm/version.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. +# -------------------------------------------------------------------- + VERSION = "1.18.2" 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..66ecd28f0 100755 --- a/src/zxbasm/zxbasm.py +++ b/src/zxbasm/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 argparse import os 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..72911564c 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 diff --git a/src/zxbc/args_parser.py b/src/zxbc/args_parser.py index fa6d4e52c..7fe196b9d 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 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/version.py b/src/zxbc/version.py index 2c0175d7b..02f034a7c 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" 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..ffd54f741 100755 --- a/src/zxbc/zxblex.py +++ b/src/zxbc/zxblex.py @@ -1,12 +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 -# ---------------------------------------------------------------------- + +# -------------------------------------------------------------------- +# 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/zxbparser.py b/src/zxbc/zxbparser.py index af649f479..6449c8f68 100755 --- a/src/zxbc/zxbparser.py +++ b/src/zxbc/zxbparser.py @@ -1,12 +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 -# ---------------------------------------------------------------------- +# -------------------------------------------------------------------- +# 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 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..20fb5b9e1 100644 --- a/src/zxbpp/base_pplex.py +++ b/src/zxbpp/base_pplex.py @@ -1,11 +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 -# -# 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 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..3c397b749 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. diff --git a/src/zxbpp/prepro/macrocall.py b/src/zxbpp/prepro/macrocall.py index ef9c239ac..17a302573 100644 --- a/src/zxbpp/prepro/macrocall.py +++ b/src/zxbpp/prepro/macrocall.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 copy import re diff --git a/src/zxbpp/prepro/operators.py b/src/zxbpp/prepro/operators.py index 555938b7f..727b13c94 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 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..231f2f9a4 100755 --- a/src/zxbpp/zxbasmpplex.py +++ b/src/zxbpp/zxbasmpplex.py @@ -1,13 +1,11 @@ #!/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 diff --git a/src/zxbpp/zxbpp.py b/src/zxbpp/zxbpp.py index 36dabed3f..c1d4d004f 100755 --- a/src/zxbpp/zxbpp.py +++ b/src/zxbpp/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 argparse import os diff --git a/src/zxbpp/zxbpplex.py b/src/zxbpp/zxbpplex.py index 2373fab03..aba81a9ef 100755 --- a/src/zxbpp/zxbpplex.py +++ b/src/zxbpp/zxbpplex.py @@ -1,13 +1,11 @@ #!/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 diff --git a/tests/__init__.py b/tests/__init__.py index 5ace62cdb..ba3d20693 100644 --- a/tests/__init__.py +++ b/tests/__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/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/functional/test.py b/tests/functional/test.py index 086a2ff0b..8923e5aa4 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 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/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/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/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..9ebd64ed7 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 diff --git a/tests/symbols/test_symbolBASICTYPE.py b/tests/symbols/test_symbolBASICTYPE.py index 827868262..9eab67d54 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 diff --git a/tests/symbols/test_symbolBINARY.py b/tests/symbols/test_symbolBINARY.py index de299c314..369395f8f 100644 --- a/tests/symbols/test_symbolBINARY.py +++ b/tests/symbols/test_symbolBINARY.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_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..b477a3f25 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 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..151efceed 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 diff --git a/tests/symbols/test_symbolTYPE.py b/tests/symbols/test_symbolTYPE.py index b526a567a..3c691c82f 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 diff --git a/tests/symbols/test_symbolTYPEALIAS.py b/tests/symbols/test_symbolTYPEALIAS.py index fc36245d2..53715188b 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 diff --git a/tests/symbols/test_symbolTYPECAST.py b/tests/symbols/test_symbolTYPECAST.py index 681aaa396..f5213aa74 100644 --- a/tests/symbols/test_symbolTYPECAST.py +++ b/tests/symbols/test_symbolTYPECAST.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_symbolVAR.py b/tests/symbols/test_symbolVAR.py index d2335f5a2..74f1d1235 100644 --- a/tests/symbols/test_symbolVAR.py +++ b/tests/symbols/test_symbolVAR.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 SCOPE diff --git a/tests/symbols/test_symbolVARARRAY.py b/tests/symbols/test_symbolVARARRAY.py index 9e4d5c297..440d2e173 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 diff --git a/tests/zxbc/__init__.py b/tests/zxbc/__init__.py index e69de29bb..906667a8d 100644 --- a/tests/zxbc/__init__.py +++ b/tests/zxbc/__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/zxbc/test_build_parsetab.py b/tests/zxbc/test_build_parsetab.py index 775b7e209..c6745d7ec 100644 --- a/tests/zxbc/test_build_parsetab.py +++ b/tests/zxbc/test_build_parsetab.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 mock from src.ply.yacc import LRParser diff --git a/tools/consolidate-libs.py b/tools/consolidate-libs.py index 4ca91ca67..868539092 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""" 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/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 From 82676082f13924604e8a5d5beaa6cc65552ee56e Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 11 Oct 2025 18:53:04 +0200 Subject: [PATCH 002/169] doc: update CHANGELOG.md --- CHANGELOG.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81e2d1a79..5e1bcc47a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,15 @@ -[v1.18.2](https://github.com/boriel-basic/zxbasic/tree/v1.18.1) +[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 From 7d483689648e045df5f25638d6fdd83b308bf509 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 11 Oct 2025 18:57:39 +0200 Subject: [PATCH 003/169] =?UTF-8?q?Bump=20version:=201.18.2=20=E2=86=92=20?= =?UTF-8?q?1.18.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- docs/archive.md | 12 ++++++------ pyproject.toml | 2 +- src/zxbasm/version.py | 2 +- src/zxbc/version.py | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 7949f14eb..b3dd59937 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.18.2 +current_version = 1.18.3 [bumpversion:file:src/zxbc/version.py] search = VERSION: Final[str] = "{current_version}" diff --git a/docs/archive.md b/docs/archive.md index 429c6d6a3..28f52f5c6 100644 --- a/docs/archive.md +++ b/docs/archive.md @@ -10,28 +10,28 @@ repository (git). 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. -The latest stable version is **1.18.2**. +The latest stable version is **1.18.3**. 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.3-win32.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.3-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.3-macos.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.3-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.3-linux64.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.3-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.3.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.3.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.3.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.3.tar.gz)
Windows, Linux, Mac tar.gz package, with python scripts. Requires python installed in your system. ### What's new diff --git a/pyproject.toml b/pyproject.toml index 2a931a4e4..1f9b0d729 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "zxbasic" -version = "1.18.2" +version = "1.18.3" description = "Boriel's ZX BASIC Compiler" authors = ["Jose Rodriguez "] license = "AGPL-3.0-or-later" diff --git a/src/zxbasm/version.py b/src/zxbasm/version.py index a526b3b97..9e545196a 100644 --- a/src/zxbasm/version.py +++ b/src/zxbasm/version.py @@ -5,4 +5,4 @@ # See https://www.gnu.org/licenses/agpl-3.0.html for details. # -------------------------------------------------------------------- -VERSION = "1.18.2" +VERSION = "1.18.3" diff --git a/src/zxbc/version.py b/src/zxbc/version.py index 02f034a7c..1e2e7eef3 100755 --- a/src/zxbc/version.py +++ b/src/zxbc/version.py @@ -7,4 +7,4 @@ from typing import Final -VERSION: Final[str] = "1.18.2" +VERSION: Final[str] = "1.18.3" From 1670f5ef6625d5982442d45e692015d6e7263f49 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 12 Oct 2025 11:36:09 +0200 Subject: [PATCH 004/169] refact: update links to forum to use forum.boriel.com --- README.md | 2 +- docs/archive.md | 2 +- docs/external_resources.md | 8 ++++---- docs/index.md | 4 ++-- docs/library/print64x32.bas.md | 2 +- docs/released_programs.md | 14 +++++++------- docs/released_programs/zx81.md | 2 +- docs/tutorials.md | 4 ++-- src/lib/arch/zx48k/stdlib/mj/fourspriter.bas | 2 +- src/lib/arch/zxnext/stdlib/mj/fourspriter.bas | 2 +- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 19184b400..856fe726a 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 diff --git a/docs/archive.md b/docs/archive.md index 28f52f5c6..785a4eb0e 100644 --- a/docs/archive.md +++ b/docs/archive.md @@ -8,7 +8,7 @@ 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.3**. Click on the desired icon below to download the package suitable for your platform: diff --git a/docs/external_resources.md b/docs/external_resources.md index 83cd40d92..4abecbcc0 100644 --- a/docs/external_resources.md +++ b/docs/external_resources.md @@ -11,7 +11,7 @@ A free IDE created by [Tony Thompson](http://www.users.on.net/~tonyt73). This po 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. -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). @@ -42,8 +42,8 @@ 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. * [Nirvana+](https://spectrumcomputing.co.uk/entry/30002/ZX-Spectrum/NIRVANA_ENGINE) @@ -53,5 +53,5 @@ There are several external engines that can be used within compiler to achieve e * [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. * [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/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/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/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/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/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/zxnext/stdlib/mj/fourspriter.bas b/src/lib/arch/zxnext/stdlib/mj/fourspriter.bas index 1bb8beecd..0fa7e1793 100644 --- a/src/lib/arch/zxnext/stdlib/mj/fourspriter.bas +++ b/src/lib/arch/zxnext/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 '' ------------------------------------------------------------------------- '' ------------------------------------------------------------------------- From bf986c87e2209eb1465032b20a27f8dc1904625f Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 19 Oct 2025 17:25:11 +0200 Subject: [PATCH 005/169] refact: fix comments --- src/arch/z80/backend/_parray.py | 4 ++-- src/symbols/sentence.py | 2 +- src/zxbc/zxbparser.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/arch/z80/backend/_parray.py b/src/arch/z80/backend/_parray.py index 33b21e429..6ebcfe9ef 100644 --- a/src/arch/z80/backend/_parray.py +++ b/src/arch/z80/backend/_parray.py @@ -284,8 +284,8 @@ 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]) diff --git a/src/symbols/sentence.py b/src/symbols/sentence.py index 51912b143..d2153e088 100644 --- a/src/symbols/sentence.py +++ b/src/symbols/sentence.py @@ -29,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/zxbc/zxbparser.py b/src/zxbc/zxbparser.py index 6449c8f68..e4bb260e0 100755 --- a/src/zxbc/zxbparser.py +++ b/src/zxbc/zxbparser.py @@ -371,7 +371,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: From 438a044997b08f6dd74a5b9c33b4cd8d583a85ac Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 19 Oct 2025 18:35:42 +0200 Subject: [PATCH 006/169] Add missing test from previous fix --- .../arch/zx48k/arr_elem_by_ref01.asm | 236 +++++ .../arch/zx48k/arr_elem_by_ref01.bas | 10 + .../arch/zx48k/arr_elem_by_ref02.asm | 840 +++++++++++++++++ .../arch/zx48k/arr_elem_by_ref02.bas | 14 + .../arch/zx48k/arr_elem_by_ref03.asm | 856 ++++++++++++++++++ .../arch/zx48k/arr_elem_by_ref03.bas | 18 + .../arch/zx48k/opt2_arr_elem_by_ref01.asm | 74 ++ .../arch/zx48k/opt2_arr_elem_by_ref01.bas | 10 + 8 files changed, 2058 insertions(+) create mode 100644 tests/functional/arch/zx48k/arr_elem_by_ref01.asm create mode 100644 tests/functional/arch/zx48k/arr_elem_by_ref01.bas create mode 100644 tests/functional/arch/zx48k/arr_elem_by_ref02.asm create mode 100644 tests/functional/arch/zx48k/arr_elem_by_ref02.bas create mode 100644 tests/functional/arch/zx48k/arr_elem_by_ref03.asm create mode 100644 tests/functional/arch/zx48k/arr_elem_by_ref03.bas create mode 100644 tests/functional/arch/zx48k/opt2_arr_elem_by_ref01.asm create mode 100644 tests/functional/arch/zx48k/opt2_arr_elem_by_ref01.bas 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/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) From 77ca25657d0f18421c4c02cf61a48635f6337aaf Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 19 Oct 2025 19:08:28 +0200 Subject: [PATCH 007/169] fix: Fixes assigning a constant string to a local array For a local array (parameter ByRef, or locally declared) the assignation of a constant string value to an array element was broken. --- src/arch/z80/backend/_array.py | 4 +- src/arch/z80/backend/_parray.py | 13 +- src/symbols/id_/ref/varref.py | 2 +- src/zxbc/zxbparser.py | 3 +- .../arch/zx48k/local_str_array1.asm | 279 ++++- .../arch/zx48k/local_str_array2.asm | 279 ++++- .../arch/zx48k/local_str_array3.asm | 279 ++++- .../arch/zx48k/local_str_array4.asm | 1025 +++++++++++++++++ .../arch/zx48k/local_str_array4.bas | 8 + tests/functional/arch/zx48k/ltee7.asm | 277 ++++- 10 files changed, 2034 insertions(+), 135 deletions(-) create mode 100644 tests/functional/arch/zx48k/local_str_array4.asm create mode 100644 tests/functional/arch/zx48k/local_str_array4.bas diff --git a/src/arch/z80/backend/_array.py b/src/arch/z80/backend/_array.py index 1d2e60f62..22f88d461 100644 --- a/src/arch/z80/backend/_array.py +++ b/src/arch/z80/backend/_array.py @@ -313,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/_parray.py b/src/arch/z80/backend/_parray.py index 6ebcfe9ef..dd324a9e9 100644 --- a/src/arch/z80/backend/_parray.py +++ b/src/arch/z80/backend/_parray.py @@ -289,17 +289,20 @@ def _pastorestr(ins: Quad) -> list[str]: 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: @@ -313,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/symbols/id_/ref/varref.py b/src/symbols/id_/ref/varref.py index 344e63fb9..36538833d 100644 --- a/src/symbols/id_/ref/varref.py +++ b/src/symbols/id_/ref/varref.py @@ -39,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/zxbc/zxbparser.py b/src/zxbc/zxbparser.py index e4bb260e0..41071ba0e 100755 --- a/src/zxbc/zxbparser.py +++ b/src/zxbc/zxbparser.py @@ -1198,7 +1198,8 @@ def p_arr_assignment(p): if entry.type_ == TYPE.string: variable = gl.SYMBOL_TABLE.access_array(id_, p.lineno(i)) - if len(variable.ref.bounds) + 1 == len(arg_list): + # variable is an array. If it has 0 bounds means they are undefined (param byref) + if len(variable.ref.bounds) and 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 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" From ead8e6e9a717736c788b1bbfb472184183015baf Mon Sep 17 00:00:00 2001 From: ConradoBadenas Date: Tue, 21 Oct 2025 12:57:30 +0200 Subject: [PATCH 008/169] Updated tests and winscroll.bas --- src/lib/arch/zx48k/stdlib/winscroll.bas | 819 +++++++++--------- src/lib/arch/zxnext/stdlib/winscroll.bas | 819 +++++++++--------- .../arch/zx48k/stdlib_winscroll.asm | 746 +++++++--------- 3 files changed, 1131 insertions(+), 1253 deletions(-) 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/zxnext/stdlib/winscroll.bas b/src/lib/arch/zxnext/stdlib/winscroll.bas index 45e5a9a4e..381f02250 100644 --- a/src/lib/arch/zxnext/stdlib/winscroll.bas +++ b/src/lib/arch/zxnext/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/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 From 46a3578de45010ca053e7ab5874e1626722955c9 Mon Sep 17 00:00:00 2001 From: ConradoBadenas Date: Fri, 24 Oct 2025 19:02:51 +0200 Subject: [PATCH 009/169] Updated tests and scroll.bas --- src/lib/arch/zx48k/stdlib/scroll.bas | 683 ++++++++++++++---- src/lib/arch/zxnext/stdlib/scroll.bas | 683 ++++++++++++++---- tests/functional/arch/zx48k/stdlib_scroll.asm | 597 +++++++++++---- 3 files changed, 1531 insertions(+), 432 deletions(-) diff --git a/src/lib/arch/zx48k/stdlib/scroll.bas b/src/lib/arch/zx48k/stdlib/scroll.bas index 33cfa9919..deec71f2d 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 ' ---------------------------------------------------------------- @@ -26,9 +27,10 @@ sub fastcall ScrollRight(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) push namespace core PROC - LOCAL LOOP1 - LOCAL LOOP2 + 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 @@ -40,51 +42,152 @@ sub fastcall ScrollRight(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) 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 +; Compute height and masks inc a - ld d, a ; d = y2 - y1 + 1 + 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 - LOCAL __PIXEL_ADDR -__PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR + 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 - ld b, e ; C cols - or a ; clear carry flag + 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 - pop hl - - dec d - ret z - call SP.PixelDown - jp LOOP1 + 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 ScrolLeft +' sub ScrollLeft ' pixel by pixel left scroll ' scrolls 1 pixel left the window defined by (x1, y1, x2, y2) ' ---------------------------------------------------------------- @@ -93,9 +196,10 @@ sub fastcall ScrollLeft(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) push namespace core PROC - LOCAL LOOP1 - LOCAL LOOP2 + 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 @@ -107,43 +211,145 @@ sub fastcall ScrollLeft(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) 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 +; Compute height and masks inc a - ld d, a ; d = y2 - y1 + 1 + ld e, a ; e = y2 - y1 + 1 - ld b, h ; BC = y2x1 + 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 - LOCAL __PIXEL_ADDR -__PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR + 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 - ld b, e ; C cols - or a ; clear carry flag + 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 - pop hl - - dec d - ret z - call SP.PixelDown - jp LOOP1 + 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 @@ -152,7 +358,7 @@ end sub ' ---------------------------------------------------------------- -' sub ScrolUp +' sub ScrollUp ' pixel by pixel up scroll ' scrolls 1 pixel up the window defined by (x1, y1, x2, y2) ' ---------------------------------------------------------------- @@ -161,8 +367,11 @@ sub fastcall ScrollUp(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) push namespace core PROC - LOCAL LOOP1 + 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 @@ -174,62 +383,153 @@ sub fastcall ScrollUp(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) 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 +; Compute height and masks + push ix inc a - ld d, a ; d = y2 - y1 + 1 + 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 - LOCAL __PIXEL_ADDR -__PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR + 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 - 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 + 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: - 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 + 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 @@ -238,7 +538,7 @@ end sub ' ---------------------------------------------------------------- -' sub ScrolDown +' sub ScrollDown ' pixel by pixel down scroll ' scrolls 1 pixel down the window defined by (x1, y1, x2, y2) ' ---------------------------------------------------------------- @@ -247,8 +547,11 @@ sub fastcall ScrollDown(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) push namespace core PROC - LOCAL LOOP1 + 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 @@ -260,62 +563,154 @@ sub fastcall ScrollDown(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) 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 +; Compute height and masks + push ix inc a - ld d, a ; d = y2 - y1 + 1 + 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 - LOCAL __PIXEL_ADDR -__PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR + 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 - 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 + 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: - 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 - + 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 @@ -324,8 +719,8 @@ 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/zxnext/stdlib/scroll.bas b/src/lib/arch/zxnext/stdlib/scroll.bas index 33cfa9919..deec71f2d 100644 --- a/src/lib/arch/zxnext/stdlib/scroll.bas +++ b/src/lib/arch/zxnext/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 ' ---------------------------------------------------------------- @@ -26,9 +27,10 @@ sub fastcall ScrollRight(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) push namespace core PROC - LOCAL LOOP1 - LOCAL LOOP2 + 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 @@ -40,51 +42,152 @@ sub fastcall ScrollRight(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) 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 +; Compute height and masks inc a - ld d, a ; d = y2 - y1 + 1 + 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 - LOCAL __PIXEL_ADDR -__PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR + 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 - ld b, e ; C cols - or a ; clear carry flag + 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 - pop hl - - dec d - ret z - call SP.PixelDown - jp LOOP1 + 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 ScrolLeft +' sub ScrollLeft ' pixel by pixel left scroll ' scrolls 1 pixel left the window defined by (x1, y1, x2, y2) ' ---------------------------------------------------------------- @@ -93,9 +196,10 @@ sub fastcall ScrollLeft(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) push namespace core PROC - LOCAL LOOP1 - LOCAL LOOP2 + 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 @@ -107,43 +211,145 @@ sub fastcall ScrollLeft(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) 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 +; Compute height and masks inc a - ld d, a ; d = y2 - y1 + 1 + ld e, a ; e = y2 - y1 + 1 - ld b, h ; BC = y2x1 + 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 - LOCAL __PIXEL_ADDR -__PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR + 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 - ld b, e ; C cols - or a ; clear carry flag + 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 - pop hl - - dec d - ret z - call SP.PixelDown - jp LOOP1 + 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 @@ -152,7 +358,7 @@ end sub ' ---------------------------------------------------------------- -' sub ScrolUp +' sub ScrollUp ' pixel by pixel up scroll ' scrolls 1 pixel up the window defined by (x1, y1, x2, y2) ' ---------------------------------------------------------------- @@ -161,8 +367,11 @@ sub fastcall ScrollUp(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) push namespace core PROC - LOCAL LOOP1 + 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 @@ -174,62 +383,153 @@ sub fastcall ScrollUp(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) 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 +; Compute height and masks + push ix inc a - ld d, a ; d = y2 - y1 + 1 + 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 - LOCAL __PIXEL_ADDR -__PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR + 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 - 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 + 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: - 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 + 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 @@ -238,7 +538,7 @@ end sub ' ---------------------------------------------------------------- -' sub ScrolDown +' sub ScrollDown ' pixel by pixel down scroll ' scrolls 1 pixel down the window defined by (x1, y1, x2, y2) ' ---------------------------------------------------------------- @@ -247,8 +547,11 @@ sub fastcall ScrollDown(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) push namespace core PROC - LOCAL LOOP1 + 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 @@ -260,62 +563,154 @@ sub fastcall ScrollDown(x1 as uByte, y1 as uByte, x2 as Ubyte, y2 as Ubyte) 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 +; Compute height and masks + push ix inc a - ld d, a ; d = y2 - y1 + 1 + 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 - LOCAL __PIXEL_ADDR -__PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR + 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 - 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 + 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: - 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 - + 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 @@ -324,8 +719,8 @@ 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/tests/functional/arch/zx48k/stdlib_scroll.asm b/tests/functional/arch/zx48k/stdlib_scroll.asm index f7de2f0c5..9258d112f 100644 --- a/tests/functional/arch/zx48k/stdlib_scroll.asm +++ b/tests/functional/arch/zx48k/stdlib_scroll.asm @@ -33,11 +33,11 @@ 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 - LOCAL LOOP2 + LOCAL LOOP1, LOOP2, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 pop hl pop bc pop de @@ -46,48 +46,137 @@ _ScrollRight: 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 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 - LOCAL __PIXEL_ADDR - __PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR + 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 b, e - or a + 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 d + 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 83 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +#line 187 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" _ScrollRight__leave: ret _ScrollLeft: -#line 92 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +#line 195 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" push namespace core PROC - LOCAL LOOP1 - LOCAL LOOP2 + LOCAL LOOP1, LOOP2, MASK1, MASK2, COLUMN1, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 pop hl pop bc pop de @@ -96,48 +185,139 @@ _ScrollLeft: 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 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 - LOCAL __PIXEL_ADDR - __PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR + 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 b, e - or a + 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 d + 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 151 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +#line 357 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" _ScrollLeft__leave: ret _ScrollUp: -#line 160 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +#line 366 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" push namespace core PROC - LOCAL LOOP1 + LOCAL LOOP1, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + LOCAL EMPTYLINE pop hl pop bc pop de @@ -146,65 +326,145 @@ _ScrollUp: 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 + push ix + inc a + ld ixL,a + ld a,c + and 7 inc a - ld d, 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 - LOCAL __PIXEL_ADDR - __PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR + call 22ACh 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 + ex af,af' + jr z,NEQ1 + dec a + ld ixH,a + ld b,0 LOOP1: - exx - ld d, h - ld e, l - ld c, a - call SP.PixelDown + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE + call nz,SP.PixelDown + inc ixL 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 + 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 c, a - dec c - ret z + 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 237 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +#line 537 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" _ScrollUp__leave: ret _ScrollDown: -#line 246 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +#line 546 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" push namespace core PROC - LOCAL LOOP1 + LOCAL LOOP1, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + LOCAL EMPTYLINE pop hl pop bc pop de @@ -213,57 +473,137 @@ _ScrollDown: 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 + 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 d, 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 - LOCAL __PIXEL_ADDR - __PIXEL_ADDR EQU 22ACh - call __PIXEL_ADDR + call 22ACh 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 + ex af,af' + jr z,NEQ1 + dec a + ld ixH,a + ld b,0 LOOP1: - exx - ld d, h - ld e, l - ld c, a - call SP.PixelUp + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE + call nz,SP.PixelUp + inc ixL 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 + 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 c, a - dec c - ret z + 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 323 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +#line 718 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" _ScrollDown__leave: ret ;; --- end of user code --- @@ -344,7 +684,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 723 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/SP/PixelUp.asm" ; ; PixelUp @@ -391,36 +731,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 724 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" END From 1be9313c18562af9b5a75b4c783165f736679ab1 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Tue, 4 Nov 2025 23:05:33 +0100 Subject: [PATCH 010/169] docs: fix issue forms Code of Conduct links --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/bug_report_es.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index ad038dd86..2e337207c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -48,7 +48,7 @@ body: 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). + 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..d68e4986c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report_es.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report_es.yaml @@ -48,7 +48,7 @@ body: 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). + 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 From 4e48c6549cc8c445ece88d83825790196fc31dc8 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Thu, 6 Nov 2025 00:47:38 +0100 Subject: [PATCH 011/169] fix: allow ../ in #include paths --- src/zxbpp/zxbpp.py | 13 +- tests/functional/arch/zx48k/rel_include.asm | 42 +++++++ tests/functional/arch/zx48k/rel_include.bas | 3 + tests/functional/asm/rel_include.asm | 3 + tests/functional/asm/rel_include.bin | Bin 0 -> 2 bytes tests/functional/zxbpp/prepro70.out | 4 +- tests/functional/zxbpp/prepro72.out | 4 +- tests/functional/zxbpp/prepro85.bi | 3 + tests/functional/zxbpp/prepro85.out | 132 ++++++++++++++++++++ 9 files changed, 194 insertions(+), 10 deletions(-) create mode 100644 tests/functional/arch/zx48k/rel_include.asm create mode 100644 tests/functional/arch/zx48k/rel_include.bas create mode 100644 tests/functional/asm/rel_include.asm create mode 100644 tests/functional/asm/rel_include.bin create mode 100644 tests/functional/zxbpp/prepro85.bi create mode 100644 tests/functional/zxbpp/prepro85.out diff --git a/src/zxbpp/zxbpp.py b/src/zxbpp/zxbpp.py index c1d4d004f..8ed7ca76a 100755 --- a/src/zxbpp/zxbpp.py +++ b/src/zxbpp/zxbpp.py @@ -179,7 +179,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 @@ -198,8 +198,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) + 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: @@ -221,14 +222,14 @@ def include_once(filename: str, lineno: int, local_first: bool) -> str: 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) + if abs_filename not in INCLUDED: # If not already included return include_file(filename, lineno, local_first) # include it and return 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/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 0000000000000000000000000000000000000000..09f370e38f498a462e1ca0faa724559b6630c04f GIT binary patch literal 2 JcmZQz0000200961 literal 0 HcmV?d00001 diff --git a/tests/functional/zxbpp/prepro70.out b/tests/functional/zxbpp/prepro70.out index 599230c43..47bdb70b4 100644 --- a/tests/functional/zxbpp/prepro70.out +++ b/tests/functional/zxbpp/prepro70.out @@ -3,9 +3,9 @@ DIM b -#line 1 "./prepro70.bi" +#line 1 "prepro70.bi" DIM b -#line 10 "./prepro70.bi" +#line 10 "prepro70.bi" #line 6 "prepro70.bi" DIM a diff --git a/tests/functional/zxbpp/prepro72.out b/tests/functional/zxbpp/prepro72.out index 3ba0b5f2a..b21476152 100644 --- a/tests/functional/zxbpp/prepro72.out +++ b/tests/functional/zxbpp/prepro72.out @@ -3,9 +3,9 @@ DIM b -#line 1 "./prepro70.bi" +#line 1 "prepro70.bi" DIM b -#line 10 "./prepro70.bi" +#line 10 "prepro70.bi" #line 6 "prepro72.bi" DIM a 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..2847d5075 --- /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" From 1759b1f02ba02b4fb1311dd9cd8efb4e711f3726 Mon Sep 17 00:00:00 2001 From: MrKOSMOS Date: Sat, 15 Nov 2025 00:38:22 +0000 Subject: [PATCH 012/169] Update hex.bas Grammar and description fix --- src/lib/arch/zx48k/stdlib/hex.bas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 5299c1da66a4da2ff76157d3fa02c067d02fe76d Mon Sep 17 00:00:00 2001 From: MrKOSMOS Date: Mon, 17 Nov 2025 10:01:02 +0000 Subject: [PATCH 013/169] Update args_parser.py Update the CLI frontend to match args_config.py warnings --- src/zxbc/args_parser.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/zxbc/args_parser.py b/src/zxbc/args_parser.py index 7fe196b9d..ee227e598 100644 --- a/src/zxbc/args_parser.py +++ b/src/zxbc/args_parser.py @@ -66,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", @@ -84,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" @@ -104,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 format", ) parser_.add_argument( "-a", "--autorun", action="store_true", default=None, help="Sets the program to be run once loaded" From 515974cdd496482a599b3020a9d060aacc2b329d Mon Sep 17 00:00:00 2001 From: Bernardo Grazina Date: Mon, 17 Nov 2025 12:55:15 +0000 Subject: [PATCH 014/169] Update args_parser.py Correcting outdated help text in args_parser.py --- src/zxbc/args_parser.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/zxbc/args_parser.py b/src/zxbc/args_parser.py index ee227e598..eb0f4b5ba 100644 --- a/src/zxbc/args_parser.py +++ b/src/zxbc/args_parser.py @@ -51,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", @@ -173,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 output formats 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" From f319d284c86fa09e38012f344d039ac75b4f5e30 Mon Sep 17 00:00:00 2001 From: MrKOSMOS Date: Mon, 17 Nov 2025 13:57:22 +0000 Subject: [PATCH 015/169] Update args_parser.py Changes to reduce line length --- src/zxbc/args_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zxbc/args_parser.py b/src/zxbc/args_parser.py index eb0f4b5ba..e650fee1d 100644 --- a/src/zxbc/args_parser.py +++ b/src/zxbc/args_parser.py @@ -104,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 one of sna, tzx, tap or z80 output format", + 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" @@ -173,7 +173,7 @@ 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 output formats tzx and tap)" + "--append-binary", default=[], action="append", help="Appends binary to tape file (only works with output tzx and tap)" ) parser_.add_argument( "--append-headless-binary", From b00d5d4677f19dfa09256d2da34c322dc866d8a4 Mon Sep 17 00:00:00 2001 From: MrKOSMOS Date: Mon, 17 Nov 2025 14:00:09 +0000 Subject: [PATCH 016/169] Update args_parser.py Further line length fix --- src/zxbc/args_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zxbc/args_parser.py b/src/zxbc/args_parser.py index e650fee1d..1cbd91564 100644 --- a/src/zxbc/args_parser.py +++ b/src/zxbc/args_parser.py @@ -173,7 +173,7 @@ 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 output tzx and tap)" + "--append-binary", default=[], action="append", help="Appends binary to tape file (only works with tzx and tap)" ) parser_.add_argument( "--append-headless-binary", From 1862266fc1c21cee95a0e9ad4264c9225eb607f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BCl=20Torralba=20Adsuara?= Date: Sun, 23 Nov 2025 10:26:51 +0100 Subject: [PATCH 017/169] fix: update Python version requirement to 3.11 in installation documentation --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From e4d7f4aefa0fcdef3df774f1527bfaa2a4972c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BCl=20Torralba=20Adsuara?= Date: Sun, 23 Nov 2025 10:32:13 +0100 Subject: [PATCH 018/169] fix: correct function name from printat42 to print42 in documentation --- docs/library/print42.bas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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. From 1706cff8df0b2f9e0c83569ba569748358603fa5 Mon Sep 17 00:00:00 2001 From: MrKOSMOS Date: Fri, 14 Nov 2025 10:47:58 +0000 Subject: [PATCH 019/169] Update operators.md fix link to Command Line Options --- docs/operators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/operators.md b/docs/operators.md index c66889333..edb824ccf 100644 --- a/docs/operators.md +++ b/docs/operators.md @@ -41,7 +41,7 @@ which is _wrong_. If in doubt, use always parenthesis to enforce the desired eva 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). +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. Operator arguments must be numbers and the result is an unsigned byte value. For binary operators, From 5a0094b5aa933a706fa50eb4ef8dd41afdc4f131 Mon Sep 17 00:00:00 2001 From: Bernardo Grazina Date: Fri, 14 Nov 2025 11:12:17 +0000 Subject: [PATCH 020/169] Update identifier.md Fix reference to Command Line Options and parentheses - add --ignore-case for clarity? --- docs/identifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/identifier.md b/docs/identifier.md index 05cc3d0c7..349db9f09 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_: From 023c80a0ffdd985e5bee19d43efcb17b11e1dd09 Mon Sep 17 00:00:00 2001 From: Bernardo Grazina Date: Fri, 14 Nov 2025 11:30:22 +0000 Subject: [PATCH 021/169] Update zxb.md Updated output of -h with that of the latest version, changed indentation for easier comparison with the old text --- docs/zxb.md | 94 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 38 deletions(-) diff --git a/docs/zxb.md b/docs/zxb.md index 298e936d9..cd7bb10b6 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: From dc256758bf38c2796a9f02a11b5632324eaf1a51 Mon Sep 17 00:00:00 2001 From: Bernardo Grazina Date: Fri, 14 Nov 2025 11:46:53 +0000 Subject: [PATCH 022/169] Update external_resources.md Fix outdated links and formatting issues --- docs/external_resources.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/external_resources.md b/docs/external_resources.md index 4abecbcc0..3fa445a18 100644 --- a/docs/external_resources.md +++ b/docs/external_resources.md @@ -9,7 +9,7 @@ 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://forum.boriel.com/showthread.php?tid=110). @@ -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: @@ -45,13 +45,13 @@ There are several external engines that can be used within compiler to achieve e * [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://forum.boriel.com/showthread.php?tid=568)), a 3d maze and RPG engine with multicolour 8x1 (18x18 chars) support. From 0716977af3d6ab06ec07526e4397bf0cecbafc54 Mon Sep 17 00:00:00 2001 From: Bernardo Grazina Date: Fri, 14 Nov 2025 14:49:39 +0000 Subject: [PATCH 023/169] Update for.md Is this feature present? No examples or documentation even show how it would be used --- docs/for.md | 1 - 1 file changed, 1 deletion(-) 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. From 1c7ed1f8987c4a8293779dd42ee284028a9821b9 Mon Sep 17 00:00:00 2001 From: Bernardo Grazina Date: Fri, 14 Nov 2025 23:45:08 +0000 Subject: [PATCH 024/169] Small fixes fixing up some small inconsistencies between files --- docs/library/attr.md | 2 +- docs/library/csrlin.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) 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 ``` From 3d7c16ecf737f7f32b144fa70d6009c7c215801c Mon Sep 17 00:00:00 2001 From: Bernardo Grazina Date: Sat, 15 Nov 2025 09:36:46 +0000 Subject: [PATCH 025/169] Create hex.md Initial proposal to document this library --- docs/library/hex.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 docs/library/hex.md diff --git a/docs/library/hex.md b/docs/library/hex.md new file mode 100644 index 000000000..23cb60669 --- /dev/null +++ b/docs/library/hex.md @@ -0,0 +1,31 @@ +# HEX/HEX16/HEX8 + +## Syntax + + +```basic +string32 = hex(n32) +string16 = hex(n16) +string8 = hex(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 +``` + +## See also + From afdd042fe58d3f519d20ff6b5415c090acef58b9 Mon Sep 17 00:00:00 2001 From: Bernardo Grazina Date: Sat, 15 Nov 2025 09:55:43 +0000 Subject: [PATCH 026/169] Update hex.md Additions for consistency and clarity --- docs/library/hex.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/library/hex.md b/docs/library/hex.md index 23cb60669..b98313620 100644 --- a/docs/library/hex.md +++ b/docs/library/hex.md @@ -4,9 +4,9 @@ ```basic -string32 = hex(n32) -string16 = hex(n16) -string8 = hex(n8) +A$ = hex(n32) +B$ = hex(n16) +C$ = hex(n8) ``` Where `n32` is a 32-bit ULONG, `n16` is a 16-bit UINTEGER and `n8` is an 8-bit UBYTE. @@ -27,5 +27,11 @@ 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 From 9d09bfed041239337ef83d2416e820d2612c767e Mon Sep 17 00:00:00 2001 From: Bernardo Grazina Date: Mon, 17 Nov 2025 09:34:53 +0000 Subject: [PATCH 027/169] Create INPUT42 doc Initial proposal based on INPUT and the INPUT42 source file --- docs/identifier.md | 2 +- docs/library/input42.md | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 docs/library/input42.md diff --git a/docs/identifier.md b/docs/identifier.md index 349db9f09..9f9a3294d 100644 --- a/docs/identifier.md +++ b/docs/identifier.md @@ -120,7 +120,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/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) From c96e66d3e9e837f46fc4fa2f1f6a1574f59f220d Mon Sep 17 00:00:00 2001 From: Bernardo Grazina Date: Mon, 17 Nov 2025 10:27:10 +0000 Subject: [PATCH 028/169] Small fixes Add Declare to function and sub doc pages See Also --- docs/function.md | 1 + docs/sub.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) 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/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) From 6b6815f5e300dc8e7c215ad1520c4903f14c5e05 Mon Sep 17 00:00:00 2001 From: Bernardo Grazina Date: Mon, 17 Nov 2025 10:27:56 +0000 Subject: [PATCH 029/169] Create to.md Start of TO keyword documentation --- docs/to.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 docs/to.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 From 604250cc78e2666a0e030a4afe2ab234443dfe00 Mon Sep 17 00:00:00 2001 From: MrKOSMOS Date: Mon, 17 Nov 2025 10:36:05 +0000 Subject: [PATCH 030/169] Update hex.md Incorporate corrections from Boriel --- docs/library/hex.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/library/hex.md b/docs/library/hex.md index b98313620..e04868855 100644 --- a/docs/library/hex.md +++ b/docs/library/hex.md @@ -5,8 +5,8 @@ ```basic A$ = hex(n32) -B$ = hex(n16) -C$ = hex(n8) +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. From c9a757fc36e97fc7754553186b48289e433147c2 Mon Sep 17 00:00:00 2001 From: MrKOSMOS Date: Mon, 17 Nov 2025 10:38:20 +0000 Subject: [PATCH 031/169] Update operators.md Incorporate suggestions from Boriel --- docs/operators.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/operators.md b/docs/operators.md index edb824ccf..2242d4856 100644 --- a/docs/operators.md +++ b/docs/operators.md @@ -41,8 +41,6 @@ which is _wrong_. If in doubt, use always parenthesis to enforce the desired eva 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. 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: From 500a399dfe4832760860156e0ebe6e81c6a95f1d Mon Sep 17 00:00:00 2001 From: MrKOSMOS Date: Mon, 17 Nov 2025 10:43:45 +0000 Subject: [PATCH 032/169] Update zxb.md Add corrections by Boriel --- docs/zxb.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/zxb.md b/docs/zxb.md index cd7bb10b6..2a2bd2087 100644 --- a/docs/zxb.md +++ b/docs/zxb.md @@ -211,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. From 356400c04758662612f3ac84f800bd58afee1c53 Mon Sep 17 00:00:00 2001 From: MrKOSMOS Date: Mon, 17 Nov 2025 12:12:42 +0000 Subject: [PATCH 033/169] Update operators.md Add corrections by Boriel --- docs/operators.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/operators.md b/docs/operators.md index 2242d4856..0e5106ed7 100644 --- a/docs/operators.md +++ b/docs/operators.md @@ -39,8 +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**. +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: From 307964741c4df65b23a806c014d3fa59d71ff103 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Thu, 4 Dec 2025 00:02:55 +0100 Subject: [PATCH 034/169] Add back old scroll routines as Scroll...Aligned. These are slightly faster and smaller, but operates aligned to columns. --- src/lib/arch/zx48k/stdlib/scroll.bas | 316 ++++++++++++++++++ src/lib/arch/zxnext/stdlib/scroll.bas | 316 ++++++++++++++++++ tests/functional/arch/zx48k/stdlib_scroll.asm | 238 ++++++++++++- 3 files changed, 868 insertions(+), 2 deletions(-) diff --git a/src/lib/arch/zx48k/stdlib/scroll.bas b/src/lib/arch/zx48k/stdlib/scroll.bas index deec71f2d..06661eb6f 100644 --- a/src/lib/arch/zx48k/stdlib/scroll.bas +++ b/src/lib/arch/zx48k/stdlib/scroll.bas @@ -717,6 +717,322 @@ EMPTYLINE: 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 + 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 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 + 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 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 + 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 + + +' ---------------------------------------------------------------- +' 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 + 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_ADDR and SCREEN_ATTR_ADDR diff --git a/src/lib/arch/zxnext/stdlib/scroll.bas b/src/lib/arch/zxnext/stdlib/scroll.bas index deec71f2d..06661eb6f 100644 --- a/src/lib/arch/zxnext/stdlib/scroll.bas +++ b/src/lib/arch/zxnext/stdlib/scroll.bas @@ -717,6 +717,322 @@ EMPTYLINE: 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 + 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 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 + 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 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 + 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 + + +' ---------------------------------------------------------------- +' 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 + 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_ADDR and SCREEN_ATTR_ADDR diff --git a/tests/functional/arch/zx48k/stdlib_scroll.asm b/tests/functional/arch/zx48k/stdlib_scroll.asm index 9258d112f..7ee1d7a83 100644 --- a/tests/functional/arch/zx48k/stdlib_scroll.asm +++ b/tests/functional/arch/zx48k/stdlib_scroll.asm @@ -606,6 +606,240 @@ EMPTYLINE: #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/zx48k/runtime/SP/PixelDown.asm" ; @@ -684,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 723 "/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 @@ -731,5 +965,5 @@ leave: ret ENDP pop namespace -#line 724 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" +#line 1039 "/zxbasic/src/lib/arch/zx48k/stdlib/scroll.bas" END From 56821401b3c23f0b2f11686015e1e8529536cb42 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Thu, 4 Dec 2025 00:12:29 +0100 Subject: [PATCH 035/169] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e1bcc47a..27fd73b39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +[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 From 66fef093509d0a25f04d4f566927ce9c0d24b2e9 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Thu, 4 Dec 2025 00:13:02 +0100 Subject: [PATCH 036/169] =?UTF-8?q?Bump=20version:=201.18.3=20=E2=86=92=20?= =?UTF-8?q?1.18.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- docs/archive.md | 12 ++++++------ pyproject.toml | 2 +- src/zxbasm/version.py | 2 +- src/zxbc/version.py | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index b3dd59937..fe66c9068 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.18.3 +current_version = 1.18.4 [bumpversion:file:src/zxbc/version.py] search = VERSION: Final[str] = "{current_version}" diff --git a/docs/archive.md b/docs/archive.md index 785a4eb0e..60ba54ed3 100644 --- a/docs/archive.md +++ b/docs/archive.md @@ -10,28 +10,28 @@ repository (git). You can contribute to ZX BASIC by reporting possible bugs or improvement suggestions at the [forum](https://forum.boriel.com/) or in social media. -The latest stable version is **1.18.3**. +The latest stable version is **1.18.4**. Click on the desired icon below to download the package suitable for your platform: * [win32zip - https://www.boriel.com/files/zxb/zxbasic-1.18.3-win32.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.3-win32.zip) + https://www.boriel.com/files/zxb/zxbasic-1.18.4-win32.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.4-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.3-macos.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.3-macos.tar.gz) + https://www.boriel.com/files/zxb/zxbasic-1.18.4-macos.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.4-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.3-linux64.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.3-linux64.tar.gz) + https://www.boriel.com/files/zxb/zxbasic-1.18.4-linux64.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.4-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.3.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.3.zip) + https://www.boriel.com/files/zxb/zxbasic-1.18.4.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.4.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.3.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.3.tar.gz) + https://www.boriel.com/files/zxb/zxbasic-1.18.4.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.4.tar.gz)
Windows, Linux, Mac tar.gz package, with python scripts. Requires python installed in your system. ### What's new diff --git a/pyproject.toml b/pyproject.toml index 1f9b0d729..c9cd61bc6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "zxbasic" -version = "1.18.3" +version = "1.18.4" description = "Boriel's ZX BASIC Compiler" authors = ["Jose Rodriguez "] license = "AGPL-3.0-or-later" diff --git a/src/zxbasm/version.py b/src/zxbasm/version.py index 9e545196a..9ec4d800b 100644 --- a/src/zxbasm/version.py +++ b/src/zxbasm/version.py @@ -5,4 +5,4 @@ # See https://www.gnu.org/licenses/agpl-3.0.html for details. # -------------------------------------------------------------------- -VERSION = "1.18.3" +VERSION = "1.18.4" diff --git a/src/zxbc/version.py b/src/zxbc/version.py index 1e2e7eef3..ca885bb90 100755 --- a/src/zxbc/version.py +++ b/src/zxbc/version.py @@ -7,4 +7,4 @@ from typing import Final -VERSION: Final[str] = "1.18.3" +VERSION: Final[str] = "1.18.4" From 811a2d9e17a0c163a4e286d0af97f014c4e2f01a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 04:47:28 +0000 Subject: [PATCH 037/169] build(deps): bump urllib3 from 2.5.0 to 2.6.0 in /docs Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.5.0 to 2.6.0. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.5.0...2.6.0) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.6.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 75220cf0c..446474c8b 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -27,6 +27,6 @@ pyyaml_env_tag==0.1 regex==2024.11.6 requests==2.32.4 six==1.16.0 -urllib3==2.5.0 +urllib3==2.6.0 watchdog==6.0.0 mkdocs-material From 039f737f0b0d19ba5c307b37ecaa0fe5aa6d21b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 20:48:48 +0000 Subject: [PATCH 038/169] build(deps): bump pymdown-extensions from 10.12 to 10.16.1 in /docs Bumps [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) from 10.12 to 10.16.1. - [Release notes](https://github.com/facelessuser/pymdown-extensions/releases) - [Commits](https://github.com/facelessuser/pymdown-extensions/compare/10.12...10.16.1) --- updated-dependencies: - dependency-name: pymdown-extensions dependency-version: 10.16.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 446474c8b..0b728cdbd 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -20,7 +20,7 @@ paginate==0.5.7 pathspec==0.12.1 platformdirs==4.3.6 Pygments==2.18.0 -pymdown-extensions==10.12 +pymdown-extensions==10.16.1 python-dateutil==2.9.0.post0 PyYAML==6.0.2 pyyaml_env_tag==0.1 From e822f0cde27d3b8cf8ea22345cd78b33ce1ca3e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 22:41:40 +0000 Subject: [PATCH 039/169] build(deps-dev): bump filelock from 3.18.0 to 3.20.1 Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.18.0 to 3.20.1. - [Release notes](https://github.com/tox-dev/py-filelock/releases) - [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/py-filelock/compare/3.18.0...3.20.1) --- updated-dependencies: - dependency-name: filelock dependency-version: 3.20.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- poetry.lock | 74 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 19 deletions(-) diff --git a/poetry.lock b/poetry.lock index ffab4fdd8..228eac5e1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "bump2version" @@ -6,6 +6,7 @@ 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"}, @@ -17,6 +18,7 @@ version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -28,6 +30,7 @@ version = "8.2.1" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.10" +groups = ["dev"] files = [ {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, @@ -42,6 +45,8 @@ 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"}, @@ -53,6 +58,7 @@ version = "7.9.2" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] 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"}, @@ -124,7 +130,7 @@ files = [ ] [package.extras] -toml = ["tomli"] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] [[package]] name = "distlib" @@ -132,6 +138,7 @@ 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"}, @@ -143,6 +150,7 @@ version = "2.1.1" description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, @@ -153,26 +161,23 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "filelock" -version = "3.18.0" +version = "3.20.1" description = "A platform independent file lock." optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" +groups = ["dev"] files = [ - {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"}, - {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"}, + {file = "filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a"}, + {file = "filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c"}, ] -[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 = "*" +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"}, @@ -190,6 +195,7 @@ version = "2.6.12" description = "File identification library for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2"}, {file = "identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6"}, @@ -204,6 +210,7 @@ version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, @@ -215,6 +222,7 @@ 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"}, @@ -232,6 +240,7 @@ version = "3.8.2" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24"}, {file = "markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45"}, @@ -247,6 +256,7 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" +groups = ["dev"] 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"}, @@ -317,6 +327,7 @@ 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"}, @@ -328,6 +339,7 @@ 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"}, @@ -350,7 +362,7 @@ 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)"] +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" @@ -358,6 +370,7 @@ version = "0.2.0" description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" optional = false python-versions = ">=3.8" +groups = ["dev"] 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"}, @@ -374,6 +387,7 @@ version = "1.17.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] 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"}, @@ -427,6 +441,7 @@ 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"}, @@ -438,6 +453,7 @@ 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" +groups = ["dev"] files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -449,6 +465,7 @@ version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, @@ -460,6 +477,7 @@ 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"}, @@ -471,6 +489,7 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -482,6 +501,7 @@ 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" +groups = ["dev"] files = [ {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, @@ -498,6 +518,7 @@ 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"}, @@ -513,6 +534,7 @@ version = "0.36.0" description = "A task runner that works well with poetry and uv." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "poethepoet-0.36.0-py3-none-any.whl", hash = "sha256:693e3c1eae9f6731d3613c3c0c40f747d3c5c68a375beda42e590a63c5623308"}, {file = "poethepoet-0.36.0.tar.gz", hash = "sha256:2217b49cb4e4c64af0b42ff8c4814b17f02e107d38bc461542517348ede25663"}, @@ -523,7 +545,7 @@ pastel = ">=0.2.1,<0.3.0" pyyaml = ">=6.0.2,<7.0" [package.extras] -poetry-plugin = ["poetry (>=1.2.0,<3.0.0)"] +poetry-plugin = ["poetry (>=1.2.0,<3.0.0) ; python_version < \"4.0\""] [[package]] name = "pre-commit" @@ -531,6 +553,7 @@ version = "4.2.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" +groups = ["dev"] 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"}, @@ -549,6 +572,7 @@ version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, @@ -563,6 +587,7 @@ version = "8.4.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, @@ -584,6 +609,7 @@ version = "6.2.1" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" +groups = ["dev"] 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"}, @@ -603,6 +629,7 @@ 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"}, @@ -623,6 +650,7 @@ 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"}, @@ -637,6 +665,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] 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"}, @@ -699,6 +728,7 @@ 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"}, @@ -713,6 +743,7 @@ version = "0.12.4" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" +groups = ["dev"] 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"}, @@ -740,19 +771,20 @@ version = "78.1.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" +groups = ["dev"] 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)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.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)", "platformdirs (>=4.2.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 (>=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"] +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.14.*)", "pytest-mypy"] [[package]] name = "six" @@ -760,6 +792,7 @@ 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"}, @@ -771,6 +804,7 @@ version = "4.14.1" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" +groups = ["dev"] 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"}, @@ -782,6 +816,7 @@ version = "20.31.2" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11"}, {file = "virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af"}, @@ -794,7 +829,7 @@ 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)"] +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) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] [[package]] name = "watchdog" @@ -802,6 +837,7 @@ 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"}, @@ -839,6 +875,6 @@ files = [ watchmedo = ["PyYAML (>=3.10)"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.11" content-hash = "f58f04f170bd72f913d33992224112b9fc24a3bd06cd2dc2120e99d29e043955" From d330c84f5e3f05dc7f9814c4677ef07d23f71ca6 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Wed, 31 Dec 2025 17:08:16 +0100 Subject: [PATCH 040/169] fix: Add LBound table if LBound is used --- src/arch/z80/visitor/var_translator.py | 2 +- tests/functional/arch/zx48k/bound02.asm | 5 ++++- tests/functional/arch/zx48k/lbound2.asm | 5 ++++- tests/functional/arch/zx48k/lbound4.asm | 5 ++++- tests/functional/arch/zx48k/lbound5.asm | 5 ++++- tests/functional/arch/zx48k/lbound7.asm | 5 ++++- tests/functional/arch/zx48k/lbound8.asm | 5 ++++- tests/functional/arch/zx48k/lbound9.asm | 5 ++++- tests/functional/arch/zxnext/bound02.asm | 5 ++++- 9 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/arch/z80/visitor/var_translator.py b/src/arch/z80/visitor/var_translator.py index b2737d1ea..b743819d5 100644 --- a/src/arch/z80/visitor/var_translator.py +++ b/src/arch/z80/visitor/var_translator.py @@ -55,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/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/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/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/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 From cdc264fb807cbdb4fd3fb3f32bf99e12935e77c9 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Wed, 31 Dec 2025 18:41:38 +0000 Subject: [PATCH 041/169] fix: Correctly compute LBound tables for local arrays --- src/arch/z80/visitor/function_translator.py | 4 +- src/symbols/call.py | 13 +- tests/functional/arch/zx48k/array13.asm | 237 ++++++++++++++++++++ tests/functional/arch/zx48k/array13.bas | 9 + tests/functional/arch/zx48k/lbound10.asm | 93 ++++++-- tests/functional/arch/zx48k/lbound11.asm | 93 ++++++-- tests/functional/arch/zx48k/lbound12.asm | 93 ++++++-- tests/functional/arch/zx48k/lbound6.asm | 93 ++++++-- tests/functional/arch/zxnext/lbound12.asm | 93 ++++++-- 9 files changed, 659 insertions(+), 69 deletions(-) create mode 100644 tests/functional/arch/zx48k/array13.asm create mode 100644 tests/functional/arch/zx48k/array13.bas diff --git a/src/arch/z80/visitor/function_translator.py b/src/arch/z80/visitor/function_translator.py index 9189e1579..143a77d4e 100644 --- a/src/arch/z80/visitor/function_translator.py +++ b/src/arch/z80/visitor/function_translator.py @@ -68,7 +68,9 @@ 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 + lbound_needed = not local_var.is_zero_based and ( + local_var.is_dynamically_accessed or local_var.lbound_used + ) bound_ptrs = [lbound_label if lbound_needed else "0", "0"] if local_var.ubound_used: diff --git a/src/symbols/call.py b/src/symbols/call.py index 4db8c950f..f646b79cb 100644 --- a/src/symbols/call.py +++ b/src/symbols/call.py @@ -6,7 +6,7 @@ # -------------------------------------------------------------------- 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 @@ -46,6 +46,13 @@ 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): @@ -80,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) @@ -94,7 +101,7 @@ 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_) 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/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/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/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 From 462cf6933803b487f1509d8c93d6fa51e84cc9b2 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Thu, 1 Jan 2026 19:39:23 +0000 Subject: [PATCH 042/169] fix: do not optimize built-in calls Sentences like: LET c = USR(x) are optimized if c var is not used. But the built-in call must be executed if it has side-effects. Thse builtins are: IN, RND and USR --- src/api/optimize.py | 24 ++++++++++++++---------- src/arch/z80/visitor/translator.py | 2 ++ src/symbols/builtin.py | 3 ++- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/api/optimize.py b/src/api/optimize.py index 380ac699b..ff744fa7d 100644 --- a/src/api/optimize.py +++ b/src/api/optimize.py @@ -321,16 +321,20 @@ 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)) diff --git a/src/arch/z80/visitor/translator.py b/src/arch/z80/visitor/translator.py index 601cdbd98..e027a57f8 100644 --- a/src/arch/z80/visitor/translator.py +++ b/src/arch/z80/visitor/translator.py @@ -152,6 +152,8 @@ def visit_BUILTIN(self, node): 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) diff --git a/src/symbols/builtin.py b/src/symbols/builtin.py index 5c512deac..f23e24dc1 100644 --- a/src/symbols/builtin.py +++ b/src/symbols/builtin.py @@ -13,7 +13,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 +22,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): From 7a7c522efdfb7a037bfe553191b98cbf9f608483 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Thu, 1 Jan 2026 19:51:46 +0000 Subject: [PATCH 043/169] test: add tests --- tests/functional/arch/zx48k/opt2_in_opt.asm | 38 ++++++ tests/functional/arch/zx48k/opt2_in_opt.bas | 3 + tests/functional/arch/zx48k/opt2_rnd_opt.asm | 127 +++++++++++++++++++ tests/functional/arch/zx48k/opt2_rnd_opt.bas | 3 + tests/functional/arch/zx48k/opt2_usr_opt.asm | 73 +++++++++++ tests/functional/arch/zx48k/opt2_usr_opt.bas | 3 + 6 files changed, 247 insertions(+) create mode 100644 tests/functional/arch/zx48k/opt2_in_opt.asm create mode 100644 tests/functional/arch/zx48k/opt2_in_opt.bas create mode 100644 tests/functional/arch/zx48k/opt2_rnd_opt.asm create mode 100644 tests/functional/arch/zx48k/opt2_rnd_opt.bas create mode 100644 tests/functional/arch/zx48k/opt2_usr_opt.asm create mode 100644 tests/functional/arch/zx48k/opt2_usr_opt.bas 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 + From 93745d4c6bfe62d4489029338b2b6dfd7c2471b3 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 5 Jan 2026 20:11:55 +0000 Subject: [PATCH 044/169] refact: use a ICInstruction dummy interface This allows defining another IR instruction set --- src/arch/interface/icinstruction.py | 5 +++++ src/arch/interface/quad.py | 3 ++- src/arch/z80/backend/icinstruction.py | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 src/arch/interface/icinstruction.py 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/quad.py b/src/arch/interface/quad.py index 4606dafce..d649edaf3 100644 --- a/src/arch/interface/quad.py +++ b/src/arch/interface/quad.py @@ -7,9 +7,10 @@ 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/backend/icinstruction.py b/src/arch/z80/backend/icinstruction.py index 32f09ff73..0b9ed3d5b 100644 --- a/src/arch/z80/backend/icinstruction.py +++ b/src/arch/z80/backend/icinstruction.py @@ -5,17 +5,17 @@ # See https://www.gnu.org/licenses/agpl-3.0.html for details. # -------------------------------------------------------------------- -from enum import StrEnum 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" From 53a8527cb971f25449e34b27b863098ea1787d13 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jan 2026 06:36:23 +0000 Subject: [PATCH 045/169] build(deps): bump urllib3 from 2.6.0 to 2.6.3 in /docs Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.0 to 2.6.3. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.6.0...2.6.3) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.6.3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 0b728cdbd..2da055526 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -27,6 +27,6 @@ pyyaml_env_tag==0.1 regex==2024.11.6 requests==2.32.4 six==1.16.0 -urllib3==2.6.0 +urllib3==2.6.3 watchdog==6.0.0 mkdocs-material From 2bdc0d5a5279042706d603585a73e90c51b86f77 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 18 Oct 2025 00:15:45 +0200 Subject: [PATCH 046/169] feat: allow multiple archs include paths --- src/arch/z80/optimizer/cpustate.py | 6 ++-- src/zxbasm/memory.py | 6 ++-- src/zxbc/args_config.py | 2 ++ src/zxbpp/zxbpp.py | 52 +++++++++++++++++++----------- 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/arch/z80/optimizer/cpustate.py b/src/arch/z80/optimizer/cpustate.py index a96e46207..c30c484ce 100644 --- a/src/arch/z80/optimizer/cpustate.py +++ b/src/arch/z80/optimizer/cpustate.py @@ -417,9 +417,9 @@ def set(self, r: str, val: int | str | None) -> None: if is_unknown8(val): val = f"{new_tmp_val()}{HL_SEP}{val}" - assert is_num or is_unknown16(val) or is_label(val), ( - f"val '{val}' is neither a number, nor a label nor an unknown16" - ) + assert ( + is_num or is_unknown16(val) or is_label(val) + ), f"val '{val}' is neither a number, nor a label nor an unknown16" self.regs[r] = val if is_16bit_composed_register(r): # sp register is not included. Special case diff --git a/src/zxbasm/memory.py b/src/zxbasm/memory.py index a06613413..aaf25f609 100644 --- a/src/zxbasm/memory.py +++ b/src/zxbasm/memory.py @@ -229,9 +229,9 @@ def declare_label( fname = gl.FILENAME if label.isdecimal(): # Temporary label? - assert not self._tmp_labels_lines[fname] or self._tmp_labels_lines[fname][-1] <= lineno, ( - "Temporary label out of order" - ) + assert ( + not self._tmp_labels_lines[fname] or self._tmp_labels_lines[fname][-1] <= lineno + ), "Temporary label out of order" if not self._tmp_labels_lines[fname] or self._tmp_labels_lines[fname][-1] != lineno: self._tmp_labels_lines[fname].append(lineno) diff --git a/src/zxbc/args_config.py b/src/zxbc/args_config.py index 72911564c..5e8f5d19a 100644 --- a/src/zxbc/args_config.py +++ b/src/zxbc/args_config.py @@ -184,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/zxbpp/zxbpp.py b/src/zxbpp/zxbpp.py index 8ed7ca76a..ff3e37574 100755 --- a/src/zxbpp/zxbpp.py +++ b/src/zxbpp/zxbpp.py @@ -13,10 +13,11 @@ import sys from dataclasses import dataclass from enum import Enum, unique -from typing import Any, NamedTuple +from typing import Any, Final, NamedTuple from src import arch from src.api import config, global_, utils +from src.arch import AVAILABLE_ARCHITECTURES from src.ply import yacc from src.zxbpp import zxbasmpplex, zxbpplex from src.zxbpp.base_pplex import STDIN @@ -51,6 +52,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 @@ -71,12 +75,12 @@ 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 @@ -135,17 +139,29 @@ 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")] + + INCLUDEPATH = INCLUDE_MAP.get(config.OPTIONS.architecture, []) def setMode(mode: PreprocMode) -> None: @@ -163,7 +179,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. @@ -171,7 +187,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): @@ -187,7 +205,7 @@ 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, at line . @@ -216,7 +234,7 @@ 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, at line . @@ -231,15 +249,12 @@ def include_once(filename: str, lineno: int, local_first: bool) -> str: abs_filename = search_filename(filename, lineno, local_first) 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_" @@ -875,6 +890,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: From 1807a0ff7c95b8b9135a619fa0de232eddfb45f9 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 9 Nov 2025 19:48:16 +0100 Subject: [PATCH 047/169] feat: allow using [arch:xxxx] in includes Now it's possible to include files from other archs, like: #include once [arch:zx48k] --- src/parsetab/tabs.dbm.bak | 8 ++-- src/parsetab/tabs.dbm.dat | Bin 1201342 -> 1205950 bytes src/parsetab/tabs.dbm.dir | 8 ++-- src/zxbpp/zxbasmpplex.py | 15 ++++++++ src/zxbpp/zxbpp.py | 76 ++++++++++++++++++++++++++++++-------- src/zxbpp/zxbpplex.py | 15 ++++++++ 6 files changed, 99 insertions(+), 23 deletions(-) diff --git a/src/parsetab/tabs.dbm.bak b/src/parsetab/tabs.dbm.bak index 9357bb728..f55990c88 100644 --- a/src/parsetab/tabs.dbm.bak +++ b/src/parsetab/tabs.dbm.bak @@ -1,4 +1,4 @@ -'zxbpp', (0, 67003) -'asmparse', (67072, 233956) -'zxnext_asmparse', (301056, 259034) -'zxbparser', (560128, 641214) +'zxbpp', (0, 71563) +'asmparse', (71680, 233956) +'zxnext_asmparse', (305664, 259034) +'zxbparser', (564736, 641214) diff --git a/src/parsetab/tabs.dbm.dat b/src/parsetab/tabs.dbm.dat index 3dd407408656a536b75cf6f112a5780220736dba..4c673d3934144bc75fe490681f227cdf9e7812e4 100644 GIT binary patch delta 77195 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 G1O6YY&%3Dr diff --git a/src/parsetab/tabs.dbm.dir b/src/parsetab/tabs.dbm.dir index 9357bb728..f55990c88 100644 --- a/src/parsetab/tabs.dbm.dir +++ b/src/parsetab/tabs.dbm.dir @@ -1,4 +1,4 @@ -'zxbpp', (0, 67003) -'asmparse', (67072, 233956) -'zxnext_asmparse', (301056, 259034) -'zxbparser', (560128, 641214) +'zxbpp', (0, 71563) +'asmparse', (71680, 233956) +'zxnext_asmparse', (305664, 259034) +'zxbparser', (564736, 641214) diff --git a/src/zxbpp/zxbasmpplex.py b/src/zxbpp/zxbasmpplex.py index 231f2f9a4..71e927820 100755 --- a/src/zxbpp/zxbasmpplex.py +++ b/src/zxbpp/zxbasmpplex.py @@ -39,6 +39,7 @@ "OR", "STRING", "TEXT", + "CO", "TOKEN", "NEWLINE", "_ENDFILE_", @@ -48,10 +49,12 @@ "EQ", "PUSH", "POP", + "LB", "LP", "LLP", "RRP", "RP", + "RB", "COMMA", "CONTINUE", "NUMBER", @@ -133,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 ff3e37574..a2bb00ce9 100755 --- a/src/zxbpp/zxbpp.py +++ b/src/zxbpp/zxbpp.py @@ -207,7 +207,7 @@ def search_filename(fname: str, lineno: int, local_first: bool, arch: 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 @@ -216,7 +216,7 @@ def include_file(filename: str, lineno: int, local_first: bool, arch: str = "") """ global CURRENT_DIR - abs_filename = search_filename(filename, lineno, local_first) + abs_filename = search_filename(filename, lineno, local_first, arch=arch) filename = utils.get_relative_filename_path(abs_filename) if abs_filename not in INCLUDED: @@ -236,7 +236,7 @@ def include_file(filename: str, lineno: int, local_first: bool, arch: 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 @@ -246,7 +246,7 @@ def include_once(filename: str, lineno: int, local_first: bool, arch: str = "") local path before looking for it in the include path chain. This is used when doing a #include "filename". """ - abs_filename = search_filename(filename, lineno, local_first) + 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, arch) # include it and return @@ -395,34 +395,55 @@ def p_include_once_ok(p): def p_include_fname(p): - """include : INCLUDE FILENAME""" + """include : INCLUDE include_modifier FILENAME""" + modifier = p[2] + if modifier is None: + p[0] = [] + return + + filename = p[3] if ENABLED: - p[0] = include_file(p[2], p.lineno(2), local_first=False) + arch = modifier.get("arch", "") + p[0] = include_file(filename, p.lineno(3), local_first=False, arch=arch) 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]) + """include : INCLUDE include_modifier expr""" + modifier = p[2] + if modifier is None: + p[0] = [] + return + + expr = p[3] + global_fist = RE_GLOBAL_FIRST_FILENAME.match(expr) + local_first = RE_LOCAL_FIRST_FILENAME.match(expr) if global_fist is None and local_first is None: - error(p.lineno(1), f"invalid filename {p[2]}") + error(p.lineno(1), f"invalid filename {expr}") p[0] = [] return if ENABLED: - p[0] = include_file(p[2][1:-1], p.lineno(2), local_first=local_first is not None) + arch = modifier.get("arch", "") + p[0] = include_file(expr[1:-1], p.lineno(3), local_first=local_first is not None, arch=arch) else: p[0] = [] p.lexer.next_token = "_ENDFILE_" def p_include_once(p): - """include_once : INCLUDE ONCE STRING""" + """include_once : INCLUDE ONCE include_modifier STRING""" + modifier = p[3] + if modifier is None: + p[0] = [] + return + + string = p[4] if ENABLED: - p[0] = include_once(p[3][1:-1], p.lineno(3), local_first=True) + arch = modifier.get("arch", "") + p[0] = include_once(string[1:-1], p.lineno(4), local_first=True, arch=arch) else: p[0] = [] @@ -431,11 +452,16 @@ def p_include_once(p): def p_include_once_fname(p): - """include_once : INCLUDE ONCE FILENAME""" + """include_once : INCLUDE ONCE include_modifier FILENAME""" p[0] = [] + modifier = p[3] + if modifier is None: + return + filename = p[4] if ENABLED: - p[0] = include_once(p[3], p.lineno(3), local_first=False) + arch = modifier.get("arch", "") + p[0] = include_once(filename, p.lineno(4), local_first=False, arch=arch) else: p[0] = [] @@ -443,6 +469,26 @@ def p_include_once_fname(p): p.lexer.next_token = "_ENDFILE_" +def p_include_modifier(p): + """include_modifier : + | LB ID CO ID RB + """ + if len(p) == 1: + p[0] = {} + return + + modifier = p[2] + value = p[4] + + if modifier == "arch": + p[0] = {"arch": value} + else: + p[0] = None + error(p.lineno(1), f"unknown modifier {modifier}") + + return + + def p_line(p): """line : LINE INTEGER NEWLINE""" if ENABLED: diff --git a/src/zxbpp/zxbpplex.py b/src/zxbpp/zxbpplex.py index aba81a9ef..d8ab506c7 100755 --- a/src/zxbpp/zxbpplex.py +++ b/src/zxbpp/zxbpplex.py @@ -36,6 +36,7 @@ "OR", "STRING", "TEXT", + "CO", "TOKEN", "NEWLINE", "_ENDFILE_", @@ -45,10 +46,12 @@ "EQ", "PUSH", "POP", + "LB", "LP", "LLP", "RRP", "RP", + "RB", "COMMA", "CONTINUE", "NUMBER", @@ -151,6 +154,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 From f33f869939a3b7cfe8770b360b81b436beb4371d Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 22 Nov 2025 20:44:43 +0100 Subject: [PATCH 048/169] refact: refactorize zxnext libraries They must use zx48k ones whenever possible --- src/lib/arch/zx48k/stdlib/SP/Fill.bas | 5 - src/lib/arch/zxnext/runtime/arith/mul16.asm | 2 +- src/lib/arch/zxnext/runtime/mem/alloc.asm | 179 +- src/lib/arch/zxnext/stdlib/IM2.bas | 130 +- src/lib/arch/zxnext/stdlib/SP/Fill.bas | 553 +---- src/lib/arch/zxnext/stdlib/alloc.bas | 232 +-- src/lib/arch/zxnext/stdlib/asc.bas | 27 +- src/lib/arch/zxnext/stdlib/attr.bas | 120 +- src/lib/arch/zxnext/stdlib/basic.bas | 156 +- src/lib/arch/zxnext/stdlib/clearbox.bas | 108 +- src/lib/arch/zxnext/stdlib/csrlin.bas | 38 +- src/lib/arch/zxnext/stdlib/esxdos.bas | 767 +------ src/lib/arch/zxnext/stdlib/fastplot.bas | 142 +- src/lib/arch/zxnext/stdlib/fmath.bas | 138 +- src/lib/arch/zxnext/stdlib/hex.bas | 111 +- src/lib/arch/zxnext/stdlib/input.bas | 119 +- src/lib/arch/zxnext/stdlib/input42.bas | 89 +- src/lib/arch/zxnext/stdlib/keys.bas | 154 +- src/lib/arch/zxnext/stdlib/megalz.bas | 163 +- src/lib/arch/zxnext/stdlib/memcopy.bas | 127 +- src/lib/arch/zxnext/stdlib/memorybank.bas | 90 +- src/lib/arch/zxnext/stdlib/mj/fourspriter.bas | 900 +-------- src/lib/arch/zxnext/stdlib/point.bas | 84 +- src/lib/arch/zxnext/stdlib/pos.bas | 40 +- src/lib/arch/zxnext/stdlib/print42.bas | 535 +---- src/lib/arch/zxnext/stdlib/print64.bas | 297 +-- src/lib/arch/zxnext/stdlib/printfzx.bas | 315 +-- src/lib/arch/zxnext/stdlib/putchars.bas | 665 +------ src/lib/arch/zxnext/stdlib/puttile.bas | 296 +-- src/lib/arch/zxnext/stdlib/random.bas | 53 +- src/lib/arch/zxnext/stdlib/retrace.bas | 16 +- src/lib/arch/zxnext/stdlib/round.bas | 33 +- src/lib/arch/zxnext/stdlib/scrbuffer.bas | 83 +- src/lib/arch/zxnext/stdlib/screen.bas | 101 +- src/lib/arch/zxnext/stdlib/scroll.bas | 1042 +--------- src/lib/arch/zxnext/stdlib/sinclair.bas | 35 +- src/lib/arch/zxnext/stdlib/spectranet.bas | 429 +--- src/lib/arch/zxnext/stdlib/template.bas | 58 +- src/lib/arch/zxnext/stdlib/winscroll.bas | 556 +----- src/lib/arch/zxnext/stdlib/zx0.bas | 1770 +---------------- tools/consolidate-libs.py | 44 +- 41 files changed, 199 insertions(+), 10603 deletions(-) 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/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/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/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/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/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 0fa7e1793..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 -'' https://forum.boriel.com/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 06661eb6f..3c9b6803a 100644 --- a/src/lib/arch/zxnext/stdlib/scroll.bas +++ b/src/lib/arch/zxnext/stdlib/scroll.bas @@ -1,1044 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License ' -' 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 -' ---------------------------------------------------------------- - -#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 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 - - +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -' 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 - 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 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 - 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 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 - 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 - - -' ---------------------------------------------------------------- -' 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 - 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_ADDR and SCREEN_ATTR_ADDR -#require "sysvars.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 381f02250..116072dee 100644 --- a/src/lib/arch/zxnext/stdlib/winscroll.bas +++ b/src/lib/arch/zxnext/stdlib/winscroll.bas @@ -1,558 +1,8 @@ ' ---------------------------------------------------------------- ' This file is released under the MIT License ' -' 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 +' Copyleft (k) 2008 +' 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 -' 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 - PROC - 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 ;height - ld a, e - or a - 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 - add hl,hl - add hl,hl - add hl,hl - add hl,hl - ld a,l - add a,c - ld l,a ;HL = row*32 + colRight - ld de,(SCREEN_ATTR_ADDR) - add hl,de - -; 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 -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 BucleRows, BucleScans, AfterLDIR, AfterLDIR2, AfterLDIR3 - -; 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 ;height - ld a, e - or a - 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 - add hl,hl - add hl,hl - add hl,hl - add hl,hl - ld a,l - add a,c - ld l,a ;HL = row*32 + col - ld de,(SCREEN_ATTR_ADDR) - add hl,de - -; 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 -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 BucleRows, BucleScans, AttrAddress - LOCAL CleanBottomRow, CleanBottomScans, AfterLDIR - -; 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 ;height - ld a, e - or a - ret z ;height=0 - ld a, d - or a - ret z ;width=0 - -; 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 - add hl,hl - add hl,hl - add hl,hl - add hl,hl - ld a,l - add a,c - ld l,a ;HL = row*32 + col - ld de,(SCREEN_ATTR_ADDR) - add hl,de - 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 - 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 BucleRows, BucleScans, AttrAddress - LOCAL CleanTopRow, CleanTopScans, AfterLDIR - -; 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 ;height - ld a, b - add a, e - dec a - 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 - -; 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 - add hl,hl - add hl,hl - add hl,hl - add hl,hl - ld a,l - add a,c - ld l,a ;HL = row*32 + col - ld de,(SCREEN_ATTR_ADDR) - add hl,de - 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 - 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_ADDR and SCREEN_ATTR_ADDR -#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/tools/consolidate-libs.py b/tools/consolidate-libs.py index 868539092..81a96db8b 100755 --- a/tools/consolidate-libs.py +++ b/tools/consolidate-libs.py @@ -19,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): @@ -47,16 +58,39 @@ 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 + + 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(COPYRIGHT_HEADER) + f.write(f"\n#include once [arch:{arch}] <{main_file_basename}>\n") def main(): From 4614fe94baa360840ced9335baab76e54fdc8f87 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 22 Nov 2025 20:01:37 +0100 Subject: [PATCH 049/169] Add tests This test also that the include [arch:zx48k] works. Also refactorize other tests. --- src/arch/z80/optimizer/cpustate.py | 6 +- src/zxbasm/memory.py | 6 +- tests/functional/arch/zxnext/array03.asm | 2 +- tests/functional/arch/zxnext/array06.asm | 2 +- tests/functional/arch/zxnext/array07.asm | 32 +- tests/functional/arch/zxnext/array08.asm | 34 +- tests/functional/arch/zxnext/array09.asm | 34 +- tests/functional/arch/zxnext/mul16.asm | 2 +- tests/functional/arch/zxnext/mul16a.asm | 2 +- tests/functional/arch/zxnext/mul16b.asm | 2 +- tests/functional/arch/zxnext/mul16c.asm | 2 +- tests/functional/arch/zxnext/mulu16.asm | 2 +- tests/functional/arch/zxnext/opt4_mul8.asm | 2 +- .../functional/arch/zxnext/stdlib_SP_Fill.bas | 1 + tests/functional/arch/zxnext/stdlib_alloc.asm | 774 ++++ tests/functional/arch/zxnext/stdlib_alloc.bas | 1 + tests/functional/arch/zxnext/stdlib_attr.asm | 271 ++ tests/functional/arch/zxnext/stdlib_attr.bas | 1 + tests/functional/arch/zxnext/stdlib_basic.asm | 154 + tests/functional/arch/zxnext/stdlib_basic.bas | 1 + .../arch/zxnext/stdlib_clearbox.asm | 121 + .../arch/zxnext/stdlib_clearbox.bas | 1 + .../functional/arch/zxnext/stdlib_csrlin.asm | 214 + .../functional/arch/zxnext/stdlib_csrlin.bas | 1 + tests/functional/arch/zxnext/stdlib_hex.asm | 782 ++++ tests/functional/arch/zxnext/stdlib_hex.bas | 1 + .../functional/arch/zxnext/stdlib_memcopy.asm | 123 + .../functional/arch/zxnext/stdlib_memcopy.bas | 1 + tests/functional/arch/zxnext/stdlib_pos.asm | 214 + tests/functional/arch/zxnext/stdlib_pos.bas | 1 + .../arch/zxnext/stdlib_putchars.asm | 592 +++ .../arch/zxnext/stdlib_putchars.bas | 1 + .../arch/zxnext/stdlib_scrbuffer.asm | 80 + .../arch/zxnext/stdlib_scrbuffer.bas | 1 + .../functional/arch/zxnext/stdlib_screen.asm | 638 +++ .../functional/arch/zxnext/stdlib_screen.bas | 1 + .../functional/arch/zxnext/stdlib_scroll.asm | 962 ++++ .../functional/arch/zxnext/stdlib_scroll.bas | 1 + .../arch/zxnext/stdlib_spectranet.asm | 4036 +++++++++++++++++ .../arch/zxnext/stdlib_spectranet.bas | 1 + .../arch/zxnext/stdlib_winscroll.asm | 505 +++ .../arch/zxnext/stdlib_winscroll.bas | 1 + tests/functional/zxbpp/other_arch.bi | 2 + tests/functional/zxbpp/other_arch.out | 255 ++ 44 files changed, 9820 insertions(+), 46 deletions(-) create mode 100644 tests/functional/arch/zxnext/stdlib_SP_Fill.bas create mode 100644 tests/functional/arch/zxnext/stdlib_alloc.asm create mode 100644 tests/functional/arch/zxnext/stdlib_alloc.bas create mode 100644 tests/functional/arch/zxnext/stdlib_attr.asm create mode 100644 tests/functional/arch/zxnext/stdlib_attr.bas create mode 100644 tests/functional/arch/zxnext/stdlib_basic.asm create mode 100644 tests/functional/arch/zxnext/stdlib_basic.bas create mode 100644 tests/functional/arch/zxnext/stdlib_clearbox.asm create mode 100644 tests/functional/arch/zxnext/stdlib_clearbox.bas create mode 100644 tests/functional/arch/zxnext/stdlib_csrlin.asm create mode 100644 tests/functional/arch/zxnext/stdlib_csrlin.bas create mode 100644 tests/functional/arch/zxnext/stdlib_hex.asm create mode 100644 tests/functional/arch/zxnext/stdlib_hex.bas create mode 100644 tests/functional/arch/zxnext/stdlib_memcopy.asm create mode 100644 tests/functional/arch/zxnext/stdlib_memcopy.bas create mode 100644 tests/functional/arch/zxnext/stdlib_pos.asm create mode 100644 tests/functional/arch/zxnext/stdlib_pos.bas create mode 100644 tests/functional/arch/zxnext/stdlib_putchars.asm create mode 100644 tests/functional/arch/zxnext/stdlib_putchars.bas create mode 100644 tests/functional/arch/zxnext/stdlib_scrbuffer.asm create mode 100644 tests/functional/arch/zxnext/stdlib_scrbuffer.bas create mode 100644 tests/functional/arch/zxnext/stdlib_screen.asm create mode 100644 tests/functional/arch/zxnext/stdlib_screen.bas create mode 100644 tests/functional/arch/zxnext/stdlib_scroll.asm create mode 100644 tests/functional/arch/zxnext/stdlib_scroll.bas create mode 100644 tests/functional/arch/zxnext/stdlib_spectranet.asm create mode 100644 tests/functional/arch/zxnext/stdlib_spectranet.bas create mode 100644 tests/functional/arch/zxnext/stdlib_winscroll.asm create mode 100644 tests/functional/arch/zxnext/stdlib_winscroll.bas create mode 100644 tests/functional/zxbpp/other_arch.bi create mode 100644 tests/functional/zxbpp/other_arch.out diff --git a/src/arch/z80/optimizer/cpustate.py b/src/arch/z80/optimizer/cpustate.py index c30c484ce..a96e46207 100644 --- a/src/arch/z80/optimizer/cpustate.py +++ b/src/arch/z80/optimizer/cpustate.py @@ -417,9 +417,9 @@ def set(self, r: str, val: int | str | None) -> None: if is_unknown8(val): val = f"{new_tmp_val()}{HL_SEP}{val}" - assert ( - is_num or is_unknown16(val) or is_label(val) - ), f"val '{val}' is neither a number, nor a label nor an unknown16" + assert is_num or is_unknown16(val) or is_label(val), ( + f"val '{val}' is neither a number, nor a label nor an unknown16" + ) self.regs[r] = val if is_16bit_composed_register(r): # sp register is not included. Special case diff --git a/src/zxbasm/memory.py b/src/zxbasm/memory.py index aaf25f609..a06613413 100644 --- a/src/zxbasm/memory.py +++ b/src/zxbasm/memory.py @@ -229,9 +229,9 @@ def declare_label( fname = gl.FILENAME if label.isdecimal(): # Temporary label? - assert ( - not self._tmp_labels_lines[fname] or self._tmp_labels_lines[fname][-1] <= lineno - ), "Temporary label out of order" + assert not self._tmp_labels_lines[fname] or self._tmp_labels_lines[fname][-1] <= lineno, ( + "Temporary label out of order" + ) if not self._tmp_labels_lines[fname] or self._tmp_labels_lines[fname][-1] != lineno: self._tmp_labels_lines[fname].append(lineno) 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/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/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/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..6089f0518 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_hex.asm @@ -0,0 +1,782 @@ + 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/zxnext/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/zxnext/stdlib/hex.bas" +#line 30 "/zxbasic/src/lib/arch/zxnext/stdlib/hex.bas" + ld hl, 0 +#line 33 "/zxbasic/src/lib/arch/zxnext/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 + ; ----- 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 116 "/zxbasic/src/lib/arch/zxnext/stdlib/hex.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 117 "/zxbasic/src/lib/arch/zxnext/stdlib/hex.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 118 "/zxbasic/src/lib/arch/zxnext/stdlib/hex.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. + ; +#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 18 "/zxbasic/src/lib/arch/zxnext/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 119 "/zxbasic/src/lib/arch/zxnext/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..5c0b81b74 --- /dev/null +++ b/tests/functional/arch/zxnext/stdlib_spectranet.asm @@ -0,0 +1,4036 @@ + 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" + ; -------------------------------- + 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 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/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..161311b03 --- /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" From 1c231fc8013a8600b1870de04782192d9c5734e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 20:01:51 +0000 Subject: [PATCH 050/169] build(deps-dev): bump virtualenv from 20.31.2 to 20.36.1 Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.31.2 to 20.36.1. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.31.2...20.36.1) --- updated-dependencies: - dependency-name: virtualenv dependency-version: 20.36.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 228eac5e1..51c397e5c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -812,19 +812,19 @@ files = [ [[package]] name = "virtualenv" -version = "20.31.2" +version = "20.36.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11"}, - {file = "virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af"}, + {file = "virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f"}, + {file = "virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba"}, ] [package.dependencies] distlib = ">=0.3.7,<1" -filelock = ">=3.12.2,<4" +filelock = {version = ">=3.20.1,<4", markers = "python_version >= \"3.10\""} platformdirs = ">=3.9.1,<5" [package.extras] From f03eae35a1d872ca09a560739882bf9da67e42d9 Mon Sep 17 00:00:00 2001 From: Nicolas Ramz Date: Tue, 13 Jan 2026 10:29:59 +0100 Subject: [PATCH 051/169] Update README.md - fixed small typo - updated sample: removed outdated -ftaB switch and added --BASIC switch (otherwise the created tape won't work) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 856fe726a..a3bd52fd0 100644 --- a/README.md +++ b/README.md @@ -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. From 0b30ec51d18d978de9343c633e734447ee087a30 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 18 Jan 2026 11:32:34 +0000 Subject: [PATCH 052/169] fix: typo in mul32.asm runtime library --- src/lib/arch/zx48k/runtime/arith/mul32.asm | 3 - tests/functional/arch/zx48k/fact.asm | 1 - tests/functional/arch/zx48k/ltee9.asm | 1 - tests/functional/arch/zx48k/mul32.asm | 154 +++++++++++++++++++++ tests/functional/arch/zx48k/mul32.bas | 10 ++ tests/functional/arch/zx48k/mul32a.asm | 135 ++++++++++++++++++ tests/functional/arch/zx48k/mul32a.bas | 4 + tests/functional/arch/zx48k/mul32b.asm | 145 +++++++++++++++++++ tests/functional/arch/zx48k/mul32b.bas | 4 + tests/functional/arch/zx48k/mul32c.asm | 130 +++++++++++++++++ tests/functional/arch/zx48k/mul32c.bas | 3 + tests/functional/arch/zx48k/subrec.asm | 1 - 12 files changed, 585 insertions(+), 6 deletions(-) create mode 100644 tests/functional/arch/zx48k/mul32.asm create mode 100644 tests/functional/arch/zx48k/mul32.bas create mode 100644 tests/functional/arch/zx48k/mul32a.asm create mode 100644 tests/functional/arch/zx48k/mul32a.bas create mode 100644 tests/functional/arch/zx48k/mul32b.asm create mode 100644 tests/functional/arch/zx48k/mul32b.bas create mode 100644 tests/functional/arch/zx48k/mul32c.asm create mode 100644 tests/functional/arch/zx48k/mul32c.bas 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/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/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/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 From 5cac7640df8de061557df89baee61c3d3599ff07 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 18 Jan 2026 10:27:53 +0000 Subject: [PATCH 053/169] tests: add SGN tests for zxnext arch --- tests/functional/arch/zxnext/sgnf.asm | 78 +++++++++++++++++++++++++ tests/functional/arch/zxnext/sgnf.bas | 2 + tests/functional/arch/zxnext/sgnf16.asm | 76 ++++++++++++++++++++++++ tests/functional/arch/zxnext/sgnf16.bas | 2 + tests/functional/arch/zxnext/sgni16.asm | 73 +++++++++++++++++++++++ tests/functional/arch/zxnext/sgni16.bas | 2 + tests/functional/arch/zxnext/sgni32.asm | 76 ++++++++++++++++++++++++ tests/functional/arch/zxnext/sgni32.bas | 2 + tests/functional/arch/zxnext/sgni8.asm | 46 +++++++++++++++ tests/functional/arch/zxnext/sgni8.bas | 2 + tests/functional/arch/zxnext/sgnu16.asm | 46 +++++++++++++++ tests/functional/arch/zxnext/sgnu16.bas | 2 + tests/functional/arch/zxnext/sgnu32.asm | 51 ++++++++++++++++ tests/functional/arch/zxnext/sgnu32.bas | 2 + tests/functional/arch/zxnext/sgnu8.asm | 44 ++++++++++++++ tests/functional/arch/zxnext/sgnu8.bas | 2 + 16 files changed, 506 insertions(+) create mode 100644 tests/functional/arch/zxnext/sgnf.asm create mode 100644 tests/functional/arch/zxnext/sgnf.bas create mode 100644 tests/functional/arch/zxnext/sgnf16.asm create mode 100644 tests/functional/arch/zxnext/sgnf16.bas create mode 100644 tests/functional/arch/zxnext/sgni16.asm create mode 100644 tests/functional/arch/zxnext/sgni16.bas create mode 100644 tests/functional/arch/zxnext/sgni32.asm create mode 100644 tests/functional/arch/zxnext/sgni32.bas create mode 100644 tests/functional/arch/zxnext/sgni8.asm create mode 100644 tests/functional/arch/zxnext/sgni8.bas create mode 100644 tests/functional/arch/zxnext/sgnu16.asm create mode 100644 tests/functional/arch/zxnext/sgnu16.bas create mode 100644 tests/functional/arch/zxnext/sgnu32.asm create mode 100644 tests/functional/arch/zxnext/sgnu32.bas create mode 100644 tests/functional/arch/zxnext/sgnu8.asm create mode 100644 tests/functional/arch/zxnext/sgnu8.bas diff --git a/tests/functional/arch/zxnext/sgnf.asm b/tests/functional/arch/zxnext/sgnf.asm new file mode 100644 index 000000000..75d8446ff --- /dev/null +++ b/tests/functional/arch/zxnext/sgnf.asm @@ -0,0 +1,78 @@ + 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" +#line 1 "/zxbasic/src/lib/arch/zxnext/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 2 "/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..e7709c53e --- /dev/null +++ b/tests/functional/arch/zxnext/sgnf16.asm @@ -0,0 +1,76 @@ + 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" +#line 1 "/zxbasic/src/lib/arch/zxnext/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 2 "/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..26f6608c6 --- /dev/null +++ b/tests/functional/arch/zxnext/sgni16.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 +_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" +#line 1 "/zxbasic/src/lib/arch/zxnext/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 2 "/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..243567436 --- /dev/null +++ b/tests/functional/arch/zxnext/sgni32.asm @@ -0,0 +1,76 @@ + 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" +#line 1 "/zxbasic/src/lib/arch/zxnext/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 2 "/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..28649be72 --- /dev/null +++ b/tests/functional/arch/zxnext/sgni8.asm @@ -0,0 +1,46 @@ + 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" + ; 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 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..49b5132f6 --- /dev/null +++ b/tests/functional/arch/zxnext/sgnu16.asm @@ -0,0 +1,46 @@ + 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" + ; 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 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..5d9837438 --- /dev/null +++ b/tests/functional/arch/zxnext/sgnu32.asm @@ -0,0 +1,51 @@ + 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" + ; 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 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..f53b2fe57 --- /dev/null +++ b/tests/functional/arch/zxnext/sgnu8.asm @@ -0,0 +1,44 @@ + 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" + ; Returns SGN (SIGN) for 8 bits unsigned integera + push namespace core +__SGNU8: + or a + ret z + ld a, 1 + ret + pop namespace +#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 From 95fc202072543bdc9b7515373d8dc1ea1b00716a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 21:01:31 +0000 Subject: [PATCH 054/169] build(deps-dev): bump filelock from 3.20.1 to 3.20.3 Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.20.1 to 3.20.3. - [Release notes](https://github.com/tox-dev/py-filelock/releases) - [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/py-filelock/compare/3.20.1...3.20.3) --- updated-dependencies: - dependency-name: filelock dependency-version: 3.20.3 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 51c397e5c..4343d7468 100644 --- a/poetry.lock +++ b/poetry.lock @@ -161,14 +161,14 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "filelock" -version = "3.20.1" +version = "3.20.3" description = "A platform independent file lock." optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a"}, - {file = "filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c"}, + {file = "filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1"}, + {file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"}, ] [[package]] From e1615f139337d08ccfcfb54e05089cb2bebf415b Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 18 Jan 2026 10:31:40 +0000 Subject: [PATCH 055/169] refact: use #include [arch:zx48k] for common SGN libs --- src/lib/arch/zxnext/runtime/sgn.asm | 52 ++++--------------------- src/lib/arch/zxnext/runtime/sgnf.asm | 8 +++- src/lib/arch/zxnext/runtime/sgnf16.asm | 8 +++- src/lib/arch/zxnext/runtime/sgni16.asm | 8 +++- src/lib/arch/zxnext/runtime/sgni32.asm | 8 +++- src/lib/arch/zxnext/runtime/sgni8.asm | 22 ++++------- src/lib/arch/zxnext/runtime/sgnu16.asm | 21 ++++------ src/lib/arch/zxnext/runtime/sgnu32.asm | 24 ++++-------- src/lib/arch/zxnext/runtime/sgnu8.asm | 20 ++++------ tests/functional/arch/zxnext/sgnf.asm | 18 ++++++++- tests/functional/arch/zxnext/sgnf16.asm | 18 ++++++++- tests/functional/arch/zxnext/sgni16.asm | 18 ++++++++- tests/functional/arch/zxnext/sgni32.asm | 18 ++++++++- tests/functional/arch/zxnext/sgni8.asm | 8 ++++ tests/functional/arch/zxnext/sgnu16.asm | 8 ++++ tests/functional/arch/zxnext/sgnu32.asm | 8 ++++ tests/functional/arch/zxnext/sgnu8.asm | 8 ++++ 17 files changed, 168 insertions(+), 107 deletions(-) diff --git a/src/lib/arch/zxnext/runtime/sgn.asm b/src/lib/arch/zxnext/runtime/sgn.asm index 47eb9e452..ca8c13f71 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..1e99b6348 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..ebc6b002f 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..095731b0b 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..507e4f863 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..4c01b4850 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..18b51a97c 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..4a15499da 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..a651cf690 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/tests/functional/arch/zxnext/sgnf.asm b/tests/functional/arch/zxnext/sgnf.asm index 75d8446ff..7bb8739a0 100644 --- a/tests/functional/arch/zxnext/sgnf.asm +++ b/tests/functional/arch/zxnext/sgnf.asm @@ -38,7 +38,21 @@ _y: 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 @@ -73,6 +87,8 @@ __ENDSGN: ret ENDP pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sgnf.asm" +#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/sgnf16.asm b/tests/functional/arch/zxnext/sgnf16.asm index e7709c53e..835e3e446 100644 --- a/tests/functional/arch/zxnext/sgnf16.asm +++ b/tests/functional/arch/zxnext/sgnf16.asm @@ -36,7 +36,21 @@ _y: 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 @@ -71,6 +85,8 @@ __ENDSGN: ret ENDP pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sgnf16.asm" +#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/sgni16.asm b/tests/functional/arch/zxnext/sgni16.asm index 26f6608c6..40f0205f3 100644 --- a/tests/functional/arch/zxnext/sgni16.asm +++ b/tests/functional/arch/zxnext/sgni16.asm @@ -33,7 +33,21 @@ _y: 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 @@ -68,6 +82,8 @@ __ENDSGN: ret ENDP pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sgni16.asm" +#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/sgni32.asm b/tests/functional/arch/zxnext/sgni32.asm index 243567436..cd6786f83 100644 --- a/tests/functional/arch/zxnext/sgni32.asm +++ b/tests/functional/arch/zxnext/sgni32.asm @@ -36,7 +36,21 @@ _y: 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 @@ -71,6 +85,8 @@ __ENDSGN: ret ENDP pop namespace -#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sgni32.asm" +#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/sgni8.asm b/tests/functional/arch/zxnext/sgni8.asm index 28649be72..555b8e66f 100644 --- a/tests/functional/arch/zxnext/sgni8.asm +++ b/tests/functional/arch/zxnext/sgni8.asm @@ -32,6 +32,13 @@ _y: 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: @@ -42,5 +49,6 @@ __SGNI8: 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/sgnu16.asm b/tests/functional/arch/zxnext/sgnu16.asm index 49b5132f6..5f1e7d3b1 100644 --- a/tests/functional/arch/zxnext/sgnu16.asm +++ b/tests/functional/arch/zxnext/sgnu16.asm @@ -33,6 +33,13 @@ _y: 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: @@ -42,5 +49,6 @@ __SGNU16: 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/sgnu32.asm b/tests/functional/arch/zxnext/sgnu32.asm index 5d9837438..f7a8c303c 100644 --- a/tests/functional/arch/zxnext/sgnu32.asm +++ b/tests/functional/arch/zxnext/sgnu32.asm @@ -36,6 +36,13 @@ _y: 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: @@ -47,5 +54,6 @@ __SGNU32: 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/sgnu8.asm b/tests/functional/arch/zxnext/sgnu8.asm index f53b2fe57..a2326eb43 100644 --- a/tests/functional/arch/zxnext/sgnu8.asm +++ b/tests/functional/arch/zxnext/sgnu8.asm @@ -32,6 +32,13 @@ _y: 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: @@ -40,5 +47,6 @@ __SGNU8: ld a, 1 ret pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/sgnu8.asm" #line 16 "arch/zxnext/sgnu8.bas" END From 98d1d83284415b72d27f06d60f08a02a94f277dc Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 18 Jan 2026 11:37:52 +0000 Subject: [PATCH 056/169] tests: add mul32 test for zxnext arch --- tests/functional/arch/zxnext/mul32.asm | 74 +++++++++++++++++++++++++ tests/functional/arch/zxnext/mul32.bas | 10 ++++ tests/functional/arch/zxnext/mul32a.asm | 67 ++++++++++++++++++++++ tests/functional/arch/zxnext/mul32a.bas | 4 ++ tests/functional/arch/zxnext/mul32b.asm | 73 ++++++++++++++++++++++++ tests/functional/arch/zxnext/mul32b.bas | 4 ++ tests/functional/arch/zxnext/mul32c.asm | 64 +++++++++++++++++++++ tests/functional/arch/zxnext/mul32c.bas | 3 + 8 files changed, 299 insertions(+) create mode 100644 tests/functional/arch/zxnext/mul32.asm create mode 100644 tests/functional/arch/zxnext/mul32.bas create mode 100644 tests/functional/arch/zxnext/mul32a.asm create mode 100644 tests/functional/arch/zxnext/mul32a.bas create mode 100644 tests/functional/arch/zxnext/mul32b.asm create mode 100644 tests/functional/arch/zxnext/mul32b.bas create mode 100644 tests/functional/arch/zxnext/mul32c.asm create mode 100644 tests/functional/arch/zxnext/mul32c.bas 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 From c62af9511667d934579431723770fdfff0a430e7 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 18 Jan 2026 11:39:50 +0000 Subject: [PATCH 057/169] refact: Use runtime /arith from zx48k if they are identical --- src/lib/arch/zxnext/runtime/arith/_mul32.asm | 78 +-------- src/lib/arch/zxnext/runtime/arith/addf.asm | 32 +--- src/lib/arch/zxnext/runtime/arith/div16.asm | 112 +------------ src/lib/arch/zxnext/runtime/arith/div32.asm | 152 +----------------- src/lib/arch/zxnext/runtime/arith/div8.asm | 98 +---------- src/lib/arch/zxnext/runtime/arith/divf.asm | 69 +------- src/lib/arch/zxnext/runtime/arith/divf16.asm | 94 +---------- src/lib/arch/zxnext/runtime/arith/modf.asm | 35 +--- src/lib/arch/zxnext/runtime/arith/modf16.asm | 48 +----- src/lib/arch/zxnext/runtime/arith/mul32.asm | 34 +--- src/lib/arch/zxnext/runtime/arith/mulf.asm | 32 +--- src/lib/arch/zxnext/runtime/arith/mulf16.asm | 54 +------ src/lib/arch/zxnext/runtime/arith/sub32.asm | 39 +---- src/lib/arch/zxnext/runtime/arith/subf.asm | 35 +--- tests/functional/arch/zxnext/addf.asm | 10 +- tests/functional/arch/zxnext/divf00.asm | 12 +- tests/functional/arch/zxnext/divf01.asm | 12 +- tests/functional/arch/zxnext/divf16.asm | 20 ++- tests/functional/arch/zxnext/divf16a.asm | 20 ++- tests/functional/arch/zxnext/divf16b.asm | 20 ++- tests/functional/arch/zxnext/divf16c.asm | 20 ++- tests/functional/arch/zxnext/divi16a.asm | 10 +- tests/functional/arch/zxnext/divi16b.asm | 10 +- tests/functional/arch/zxnext/divi32c.asm | 10 +- tests/functional/arch/zxnext/divi8.asm | 8 + tests/functional/arch/zxnext/divi8a.asm | 8 + tests/functional/arch/zxnext/divi8b.asm | 8 + tests/functional/arch/zxnext/divu16.asm | 10 +- tests/functional/arch/zxnext/divu16a.asm | 10 +- tests/functional/arch/zxnext/divu16b.asm | 10 +- tests/functional/arch/zxnext/divu32c.asm | 10 +- tests/functional/arch/zxnext/divu8.asm | 8 + tests/functional/arch/zxnext/divu8a.asm | 8 + tests/functional/arch/zxnext/divu8b.asm | 8 + tests/functional/arch/zxnext/equf.asm | 10 +- tests/functional/arch/zxnext/fact.asm | 26 ++- tests/functional/arch/zxnext/gef16.asm | 8 + tests/functional/arch/zxnext/gei32.asm | 8 + tests/functional/arch/zxnext/geu32.asm | 8 + tests/functional/arch/zxnext/gtf16.asm | 8 + tests/functional/arch/zxnext/gti32.asm | 8 + tests/functional/arch/zxnext/lei32.asm | 8 + tests/functional/arch/zxnext/ltf16.asm | 8 + tests/functional/arch/zxnext/lti32.asm | 8 + tests/functional/arch/zxnext/ltu32.asm | 8 + tests/functional/arch/zxnext/modf.asm | 10 +- tests/functional/arch/zxnext/modf16c.asm | 50 +++++- tests/functional/arch/zxnext/modi32c.asm | 10 +- tests/functional/arch/zxnext/modi8.asm | 8 + tests/functional/arch/zxnext/modi8a.asm | 8 + tests/functional/arch/zxnext/modi8b.asm | 8 + tests/functional/arch/zxnext/modu32c.asm | 10 +- tests/functional/arch/zxnext/modu8.asm | 8 + tests/functional/arch/zxnext/modu8a.asm | 8 + tests/functional/arch/zxnext/modu8b.asm | 8 + tests/functional/arch/zxnext/mulf00.asm | 10 +- tests/functional/arch/zxnext/mulf01.asm | 10 +- tests/functional/arch/zxnext/mulf16.asm | 20 ++- tests/functional/arch/zxnext/mulf16a.asm | 20 ++- tests/functional/arch/zxnext/print_i16.asm | 10 +- tests/functional/arch/zxnext/print_i32.asm | 8 + tests/functional/arch/zxnext/print_i8.asm | 8 + tests/functional/arch/zxnext/print_u16.asm | 10 +- tests/functional/arch/zxnext/print_u32.asm | 8 + tests/functional/arch/zxnext/print_u8.asm | 8 + .../arch/zxnext/stdlib_spectranet.asm | 8 + tests/functional/arch/zxnext/subf00.asm | 10 +- tests/functional/arch/zxnext/subf01.asm | 10 +- tests/functional/arch/zxnext/subf16c.asm | 8 + tests/functional/arch/zxnext/subi32c.asm | 8 + 70 files changed, 696 insertions(+), 840 deletions(-) diff --git a/src/lib/arch/zxnext/runtime/arith/_mul32.asm b/src/lib/arch/zxnext/runtime/arith/_mul32.asm index 60aa191a5..0f8c4d04f 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..b48d92e0d 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). -; -; 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 - - 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/div16.asm b/src/lib/arch/zxnext/runtime/arith/div16.asm index 1eed19848..120f1f1a0 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..cccc0c78c 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..b131a6ef7 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..0e8fb5494 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). -; -; 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 - - 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/divf16.asm b/src/lib/arch/zxnext/runtime/arith/divf16.asm index 2961c62fb..dc5873d6c 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..c5b8a14b0 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. -; -; 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 - - 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/modf16.asm b/src/lib/arch/zxnext/runtime/arith/modf16.asm index edba61785..ea647fd18 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/mul32.asm b/src/lib/arch/zxnext/runtime/arith/mul32.asm index 641bc1efb..16212bf1a 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..ee75de8d6 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. -; -; Uses CALLEE convention -; ------------------------------------------------------------- - - push namespace core - -__MULF: ; Multiplication - call __FPSTACK_PUSH2 - - ; ------------- ROM MUL - rst 28h - defb 04h ; - 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/arith/mulf16.asm b/src/lib/arch/zxnext/runtime/arith/mulf16.asm index 9c960669c..b2d8c553d 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..a1d0f929b 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..54aa06129 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. -; -; 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 - - 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/tests/functional/arch/zxnext/addf.asm b/tests/functional/arch/zxnext/addf.asm index 7095c62f8..37fa6e724 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/divf00.asm b/tests/functional/arch/zxnext/divf00.asm index 800f362e2..c398e7663 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..834a3b0e7 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..8fff86ee2 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..76c3de713 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..803fc41e9 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..e23a54d39 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..654924c74 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..1f1579086 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..ccdb1d51a 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..bb797ef48 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..eb49f9817 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..baa23cc43 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..ad677a4ce 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..d5c4cb928 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..fd5e23746 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..3fd2607d3 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..2a4d98320 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..92ec2d0e0 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..7dad972bd 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..c1f454320 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..f234d54d5 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..68fdd84df 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..0a085d107 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..308414e69 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..0df55aa79 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..37f8a793d 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/lei32.asm b/tests/functional/arch/zxnext/lei32.asm index c797304f2..9c2c9ca96 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..9296fa20a 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..e2090c640 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..5850b0370 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..66b8ae1b2 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..3f27c5d53 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..4f60900d6 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..72780f141 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..69023bf2e 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..fe2ca67d4 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..c50be0120 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..14c0ec2b9 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..dc6511722 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..47fb7741d 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/mulf00.asm b/tests/functional/arch/zxnext/mulf00.asm index dd3cc7166..8e0133423 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..8ff9e79b3 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..9e16165d2 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..f98844c69 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/print_i16.asm b/tests/functional/arch/zxnext/print_i16.asm index ee0bfbb6d..8d21a531e 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..9c2593eb1 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..ba879845d 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_u16.asm b/tests/functional/arch/zxnext/print_u16.asm index 305e88269..785cfec93 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..ea8865d0e 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..483676c33 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/stdlib_spectranet.asm b/tests/functional/arch/zxnext/stdlib_spectranet.asm index 5c0b81b74..51bce0c96 100644 --- a/tests/functional/arch/zxnext/stdlib_spectranet.asm +++ b/tests/functional/arch/zxnext/stdlib_spectranet.asm @@ -3383,6 +3383,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 @@ -3447,6 +3454,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/subf00.asm b/tests/functional/arch/zxnext/subf00.asm index 94f097455..bbd905474 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..59c24a6ea 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..aff96fea2 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..06e1725c2 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 From abb56ba5c9a5013a60e080f44f17fff4d86a474c Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 19 Jan 2026 00:39:57 +0100 Subject: [PATCH 058/169] =?UTF-8?q?Bump=20version:=201.18.4=20=E2=86=92=20?= =?UTF-8?q?1.18.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGELOG.md | 6 ++++++ docs/archive.md | 12 ++++++------ pyproject.toml | 2 +- src/zxbasm/version.py | 2 +- src/zxbc/version.py | 2 +- 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index fe66c9068..4970d70a3 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.18.4 +current_version = 1.18.5 [bumpversion:file:src/zxbc/version.py] search = VERSION: Final[str] = "{current_version}" diff --git a/CHANGELOG.md b/CHANGELOG.md index 27fd73b39..ec6425ac3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +[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 diff --git a/docs/archive.md b/docs/archive.md index 60ba54ed3..b04067bf1 100644 --- a/docs/archive.md +++ b/docs/archive.md @@ -10,28 +10,28 @@ repository (git). You can contribute to ZX BASIC by reporting possible bugs or improvement suggestions at the [forum](https://forum.boriel.com/) or in social media. -The latest stable version is **1.18.4**. +The latest stable version is **1.18.5**. Click on the desired icon below to download the package suitable for your platform: * [win32zip - https://www.boriel.com/files/zxb/zxbasic-1.18.4-win32.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.4-win32.zip) + https://www.boriel.com/files/zxb/zxbasic-1.18.5-win32.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.5-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.4-macos.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.4-macos.tar.gz) + https://www.boriel.com/files/zxb/zxbasic-1.18.5-macos.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.5-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.4-linux64.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.4-linux64.tar.gz) + https://www.boriel.com/files/zxb/zxbasic-1.18.5-linux64.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.5-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.4.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.4.zip) + https://www.boriel.com/files/zxb/zxbasic-1.18.5.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.5.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.4.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.4.tar.gz) + https://www.boriel.com/files/zxb/zxbasic-1.18.5.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.5.tar.gz)
Windows, Linux, Mac tar.gz package, with python scripts. Requires python installed in your system. ### What's new diff --git a/pyproject.toml b/pyproject.toml index c9cd61bc6..9853d1cd6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "zxbasic" -version = "1.18.4" +version = "1.18.5" description = "Boriel's ZX BASIC Compiler" authors = ["Jose Rodriguez "] license = "AGPL-3.0-or-later" diff --git a/src/zxbasm/version.py b/src/zxbasm/version.py index 9ec4d800b..419faa241 100644 --- a/src/zxbasm/version.py +++ b/src/zxbasm/version.py @@ -5,4 +5,4 @@ # See https://www.gnu.org/licenses/agpl-3.0.html for details. # -------------------------------------------------------------------- -VERSION = "1.18.4" +VERSION = "1.18.5" diff --git a/src/zxbc/version.py b/src/zxbc/version.py index ca885bb90..c1a8bf10a 100755 --- a/src/zxbc/version.py +++ b/src/zxbc/version.py @@ -7,4 +7,4 @@ from typing import Final -VERSION: Final[str] = "1.18.4" +VERSION: Final[str] = "1.18.5" From 757d5a800d61c20fcb336c8f31a884c7cdcfc158 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 25 Jan 2026 13:09:45 +0100 Subject: [PATCH 059/169] feat: update dependencies --- poetry.lock | 780 ++++++++++++++++++++++++++++++------------------- pyproject.toml | 2 +- 2 files changed, 478 insertions(+), 304 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4343d7468..ccafe0a5c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.1 and should not be changed by hand. [[package]] name = "bump2version" @@ -14,26 +14,26 @@ files = [ [[package]] name = "cfgv" -version = "3.4.0" +version = "3.5.0" description = "Validate configuration and produce human readable error messages." optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, - {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, + {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.2.1" +version = "8.3.1" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, - {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, + {file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"}, + {file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"}, ] [package.dependencies] @@ -46,7 +46,7 @@ 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\"" +markers = "sys_platform == \"win32\" or platform_system == \"Windows\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -54,79 +54,104 @@ files = [ [[package]] name = "coverage" -version = "7.9.2" +version = "7.13.1" description = "Code coverage measurement for Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] 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"}, + {file = "coverage-7.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1fa280b3ad78eea5be86f94f461c04943d942697e0dac889fa18fff8f5f9147"}, + {file = "coverage-7.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c3d8c679607220979434f494b139dfb00131ebf70bb406553d69c1ff01a5c33d"}, + {file = "coverage-7.13.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:339dc63b3eba969067b00f41f15ad161bf2946613156fb131266d8debc8e44d0"}, + {file = "coverage-7.13.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:db622b999ffe49cb891f2fff3b340cdc2f9797d01a0a202a0973ba2562501d90"}, + {file = "coverage-7.13.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1443ba9acbb593fa7c1c29e011d7c9761545fe35e7652e85ce7f51a16f7e08d"}, + {file = "coverage-7.13.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c832ec92c4499ac463186af72f9ed4d8daec15499b16f0a879b0d1c8e5cf4a3b"}, + {file = "coverage-7.13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:562ec27dfa3f311e0db1ba243ec6e5f6ab96b1edfcfc6cf86f28038bc4961ce6"}, + {file = "coverage-7.13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4de84e71173d4dada2897e5a0e1b7877e5eefbfe0d6a44edee6ce31d9b8ec09e"}, + {file = "coverage-7.13.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:a5a68357f686f8c4d527a2dc04f52e669c2fc1cbde38f6f7eb6a0e58cbd17cae"}, + {file = "coverage-7.13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:77cc258aeb29a3417062758975521eae60af6f79e930d6993555eeac6a8eac29"}, + {file = "coverage-7.13.1-cp310-cp310-win32.whl", hash = "sha256:bb4f8c3c9a9f34423dba193f241f617b08ffc63e27f67159f60ae6baf2dcfe0f"}, + {file = "coverage-7.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:c8e2706ceb622bc63bac98ebb10ef5da80ed70fbd8a7999a5076de3afaef0fb1"}, + {file = "coverage-7.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a55d509a1dc5a5b708b5dad3b5334e07a16ad4c2185e27b40e4dba796ab7f88"}, + {file = "coverage-7.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d010d080c4888371033baab27e47c9df7d6fb28d0b7b7adf85a4a49be9298b3"}, + {file = "coverage-7.13.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d938b4a840fb1523b9dfbbb454f652967f18e197569c32266d4d13f37244c3d9"}, + {file = "coverage-7.13.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bf100a3288f9bb7f919b87eb84f87101e197535b9bd0e2c2b5b3179633324fee"}, + {file = "coverage-7.13.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef6688db9bf91ba111ae734ba6ef1a063304a881749726e0d3575f5c10a9facf"}, + {file = "coverage-7.13.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0b609fc9cdbd1f02e51f67f51e5aee60a841ef58a68d00d5ee2c0faf357481a3"}, + {file = "coverage-7.13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c43257717611ff5e9a1d79dce8e47566235ebda63328718d9b65dd640bc832ef"}, + {file = "coverage-7.13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e09fbecc007f7b6afdfb3b07ce5bd9f8494b6856dd4f577d26c66c391b829851"}, + {file = "coverage-7.13.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:a03a4f3a19a189919c7055098790285cc5c5b0b3976f8d227aea39dbf9f8bfdb"}, + {file = "coverage-7.13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3820778ea1387c2b6a818caec01c63adc5b3750211af6447e8dcfb9b6f08dbba"}, + {file = "coverage-7.13.1-cp311-cp311-win32.whl", hash = "sha256:ff10896fa55167371960c5908150b434b71c876dfab97b69478f22c8b445ea19"}, + {file = "coverage-7.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:a998cc0aeeea4c6d5622a3754da5a493055d2d95186bad877b0a34ea6e6dbe0a"}, + {file = "coverage-7.13.1-cp311-cp311-win_arm64.whl", hash = "sha256:fea07c1a39a22614acb762e3fbbb4011f65eedafcb2948feeef641ac78b4ee5c"}, + {file = "coverage-7.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6f34591000f06e62085b1865c9bc5f7858df748834662a51edadfd2c3bfe0dd3"}, + {file = "coverage-7.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b67e47c5595b9224599016e333f5ec25392597a89d5744658f837d204e16c63e"}, + {file = "coverage-7.13.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e7b8bd70c48ffb28461ebe092c2345536fb18bbbf19d287c8913699735f505c"}, + {file = "coverage-7.13.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c223d078112e90dc0e5c4e35b98b9584164bea9fbbd221c0b21c5241f6d51b62"}, + {file = "coverage-7.13.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:794f7c05af0763b1bbd1b9e6eff0e52ad068be3b12cd96c87de037b01390c968"}, + {file = "coverage-7.13.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0642eae483cc8c2902e4af7298bf886d605e80f26382124cddc3967c2a3df09e"}, + {file = "coverage-7.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f5e772ed5fef25b3de9f2008fe67b92d46831bd2bc5bdc5dd6bfd06b83b316f"}, + {file = "coverage-7.13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:45980ea19277dc0a579e432aef6a504fe098ef3a9032ead15e446eb0f1191aee"}, + {file = "coverage-7.13.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e4f18eca6028ffa62adbd185a8f1e1dd242f2e68164dba5c2b74a5204850b4cf"}, + {file = "coverage-7.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8dca5590fec7a89ed6826fce625595279e586ead52e9e958d3237821fbc750c"}, + {file = "coverage-7.13.1-cp312-cp312-win32.whl", hash = "sha256:ff86d4e85188bba72cfb876df3e11fa243439882c55957184af44a35bd5880b7"}, + {file = "coverage-7.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:16cc1da46c04fb0fb128b4dc430b78fa2aba8a6c0c9f8eb391fd5103409a6ac6"}, + {file = "coverage-7.13.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d9bc218650022a768f3775dd7fdac1886437325d8d295d923ebcfef4892ad5c"}, + {file = "coverage-7.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cb237bfd0ef4d5eb6a19e29f9e528ac67ac3be932ea6b44fb6cc09b9f3ecff78"}, + {file = "coverage-7.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1dcb645d7e34dcbcc96cd7c132b1fc55c39263ca62eb961c064eb3928997363b"}, + {file = "coverage-7.13.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3d42df8201e00384736f0df9be2ced39324c3907607d17d50d50116c989d84cd"}, + {file = "coverage-7.13.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa3edde1aa8807de1d05934982416cb3ec46d1d4d91e280bcce7cca01c507992"}, + {file = "coverage-7.13.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9edd0e01a343766add6817bc448408858ba6b489039eaaa2018474e4001651a4"}, + {file = "coverage-7.13.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:985b7836931d033570b94c94713c6dba5f9d3ff26045f72c3e5dbc5fe3361e5a"}, + {file = "coverage-7.13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ffed1e4980889765c84a5d1a566159e363b71d6b6fbaf0bebc9d3c30bc016766"}, + {file = "coverage-7.13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8842af7f175078456b8b17f1b73a0d16a65dcbdc653ecefeb00a56b3c8c298c4"}, + {file = "coverage-7.13.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:ccd7a6fca48ca9c131d9b0a2972a581e28b13416fc313fb98b6d24a03ce9a398"}, + {file = "coverage-7.13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0403f647055de2609be776965108447deb8e384fe4a553c119e3ff6bfbab4784"}, + {file = "coverage-7.13.1-cp313-cp313-win32.whl", hash = "sha256:549d195116a1ba1e1ae2f5ca143f9777800f6636eab917d4f02b5310d6d73461"}, + {file = "coverage-7.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:5899d28b5276f536fcf840b18b61a9fce23cc3aec1d114c44c07fe94ebeaa500"}, + {file = "coverage-7.13.1-cp313-cp313-win_arm64.whl", hash = "sha256:868a2fae76dfb06e87291bcbd4dcbcc778a8500510b618d50496e520bd94d9b9"}, + {file = "coverage-7.13.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:67170979de0dacac3f3097d02b0ad188d8edcea44ccc44aaa0550af49150c7dc"}, + {file = "coverage-7.13.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f80e2bb21bfab56ed7405c2d79d34b5dc0bc96c2c1d2a067b643a09fb756c43a"}, + {file = "coverage-7.13.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f83351e0f7dcdb14d7326c3d8d8c4e915fa685cbfdc6281f9470d97a04e9dfe4"}, + {file = "coverage-7.13.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb3f6562e89bad0110afbe64e485aac2462efdce6232cdec7862a095dc3412f6"}, + {file = "coverage-7.13.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77545b5dcda13b70f872c3b5974ac64c21d05e65b1590b441c8560115dc3a0d1"}, + {file = "coverage-7.13.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4d240d260a1aed814790bbe1f10a5ff31ce6c21bc78f0da4a1e8268d6c80dbd"}, + {file = "coverage-7.13.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d2287ac9360dec3837bfdad969963a5d073a09a85d898bd86bea82aa8876ef3c"}, + {file = "coverage-7.13.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0d2c11f3ea4db66b5cbded23b20185c35066892c67d80ec4be4bab257b9ad1e0"}, + {file = "coverage-7.13.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:3fc6a169517ca0d7ca6846c3c5392ef2b9e38896f61d615cb75b9e7134d4ee1e"}, + {file = "coverage-7.13.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d10a2ed46386e850bb3de503a54f9fe8192e5917fcbb143bfef653a9355e9a53"}, + {file = "coverage-7.13.1-cp313-cp313t-win32.whl", hash = "sha256:75a6f4aa904301dab8022397a22c0039edc1f51e90b83dbd4464b8a38dc87842"}, + {file = "coverage-7.13.1-cp313-cp313t-win_amd64.whl", hash = "sha256:309ef5706e95e62578cda256b97f5e097916a2c26247c287bbe74794e7150df2"}, + {file = "coverage-7.13.1-cp313-cp313t-win_arm64.whl", hash = "sha256:92f980729e79b5d16d221038dbf2e8f9a9136afa072f9d5d6ed4cb984b126a09"}, + {file = "coverage-7.13.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:97ab3647280d458a1f9adb85244e81587505a43c0c7cff851f5116cd2814b894"}, + {file = "coverage-7.13.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8f572d989142e0908e6acf57ad1b9b86989ff057c006d13b76c146ec6a20216a"}, + {file = "coverage-7.13.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d72140ccf8a147e94274024ff6fd8fb7811354cf7ef88b1f0a988ebaa5bc774f"}, + {file = "coverage-7.13.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3c9f051b028810f5a87c88e5d6e9af3c0ff32ef62763bf15d29f740453ca909"}, + {file = "coverage-7.13.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f398ba4df52d30b1763f62eed9de5620dcde96e6f491f4c62686736b155aa6e4"}, + {file = "coverage-7.13.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:132718176cc723026d201e347f800cd1a9e4b62ccd3f82476950834dad501c75"}, + {file = "coverage-7.13.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e549d642426e3579b3f4b92d0431543b012dcb6e825c91619d4e93b7363c3f9"}, + {file = "coverage-7.13.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:90480b2134999301eea795b3a9dbf606c6fbab1b489150c501da84a959442465"}, + {file = "coverage-7.13.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e825dbb7f84dfa24663dd75835e7257f8882629fc11f03ecf77d84a75134b864"}, + {file = "coverage-7.13.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:623dcc6d7a7ba450bbdbeedbaa0c42b329bdae16491af2282f12a7e809be7eb9"}, + {file = "coverage-7.13.1-cp314-cp314-win32.whl", hash = "sha256:6e73ebb44dca5f708dc871fe0b90cf4cff1a13f9956f747cc87b535a840386f5"}, + {file = "coverage-7.13.1-cp314-cp314-win_amd64.whl", hash = "sha256:be753b225d159feb397bd0bf91ae86f689bad0da09d3b301478cd39b878ab31a"}, + {file = "coverage-7.13.1-cp314-cp314-win_arm64.whl", hash = "sha256:228b90f613b25ba0019361e4ab81520b343b622fc657daf7e501c4ed6a2366c0"}, + {file = "coverage-7.13.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:60cfb538fe9ef86e5b2ab0ca8fc8d62524777f6c611dcaf76dc16fbe9b8e698a"}, + {file = "coverage-7.13.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:57dfc8048c72ba48a8c45e188d811e5efd7e49b387effc8fb17e97936dde5bf6"}, + {file = "coverage-7.13.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3f2f725aa3e909b3c5fdb8192490bdd8e1495e85906af74fe6e34a2a77ba0673"}, + {file = "coverage-7.13.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ee68b21909686eeb21dfcba2c3b81fee70dcf38b140dcd5aa70680995fa3aa5"}, + {file = "coverage-7.13.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:724b1b270cb13ea2e6503476e34541a0b1f62280bc997eab443f87790202033d"}, + {file = "coverage-7.13.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:916abf1ac5cf7eb16bc540a5bf75c71c43a676f5c52fcb9fe75a2bd75fb944e8"}, + {file = "coverage-7.13.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:776483fd35b58d8afe3acbd9988d5de592ab6da2d2a865edfdbc9fdb43e7c486"}, + {file = "coverage-7.13.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b6f3b96617e9852703f5b633ea01315ca45c77e879584f283c44127f0f1ec564"}, + {file = "coverage-7.13.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:bd63e7b74661fed317212fab774e2a648bc4bb09b35f25474f8e3325d2945cd7"}, + {file = "coverage-7.13.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:933082f161bbb3e9f90d00990dc956120f608cdbcaeea15c4d897f56ef4fe416"}, + {file = "coverage-7.13.1-cp314-cp314t-win32.whl", hash = "sha256:18be793c4c87de2965e1c0f060f03d9e5aff66cfeae8e1dbe6e5b88056ec153f"}, + {file = "coverage-7.13.1-cp314-cp314t-win_amd64.whl", hash = "sha256:0e42e0ec0cd3e0d851cb3c91f770c9301f48647cb2877cb78f74bdaa07639a79"}, + {file = "coverage-7.13.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eaecf47ef10c72ece9a2a92118257da87e460e113b83cc0d2905cbbe931792b4"}, + {file = "coverage-7.13.1-py3-none-any.whl", hash = "sha256:2016745cb3ba554469d02819d78958b571792bb68e31302610e898f80dd3a573"}, + {file = "coverage-7.13.1.tar.gz", hash = "sha256:b7593fe7eb5feaa3fbb461ac79aac9f9fc0387a5ca8080b0c6fe2ca27b091afd"}, ] [package.extras] @@ -146,14 +171,14 @@ files = [ [[package]] name = "execnet" -version = "2.1.1" +version = "2.1.2" description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, - {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, + {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"}, + {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"}, ] [package.extras] @@ -191,14 +216,14 @@ dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "identify" -version = "2.6.12" +version = "2.6.16" description = "File identification library for Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2"}, - {file = "identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6"}, + {file = "identify-2.6.16-py2.py3-none-any.whl", hash = "sha256:391ee4d77741d994189522896270b787aed8670389bfd60f326d677d64a6dfb0"}, + {file = "identify-2.6.16.tar.gz", hash = "sha256:846857203b5511bbe94d5a352a48ef2359532bc8f6727b5544077a0dcfb24980"}, ] [package.extras] @@ -206,14 +231,14 @@ license = ["ukkonen"] [[package]] name = "iniconfig" -version = "2.1.0" +version = "2.3.0" description = "brain-dead simple config-ini parsing" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, - {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, + {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, + {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, ] [[package]] @@ -234,91 +259,206 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "librt" +version = "0.7.8" +description = "Mypyc runtime library" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "librt-0.7.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b45306a1fc5f53c9330fbee134d8b3227fe5da2ab09813b892790400aa49352d"}, + {file = "librt-0.7.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:864c4b7083eeee250ed55135d2127b260d7eb4b5e953a9e5df09c852e327961b"}, + {file = "librt-0.7.8-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6938cc2de153bc927ed8d71c7d2f2ae01b4e96359126c602721340eb7ce1a92d"}, + {file = "librt-0.7.8-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:66daa6ac5de4288a5bbfbe55b4caa7bf0cd26b3269c7a476ffe8ce45f837f87d"}, + {file = "librt-0.7.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4864045f49dc9c974dadb942ac56a74cd0479a2aafa51ce272c490a82322ea3c"}, + {file = "librt-0.7.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a36515b1328dc5b3ffce79fe204985ca8572525452eacabee2166f44bb387b2c"}, + {file = "librt-0.7.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b7e7f140c5169798f90b80d6e607ed2ba5059784968a004107c88ad61fb3641d"}, + {file = "librt-0.7.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff71447cb778a4f772ddc4ce360e6ba9c95527ed84a52096bd1bbf9fee2ec7c0"}, + {file = "librt-0.7.8-cp310-cp310-win32.whl", hash = "sha256:047164e5f68b7a8ebdf9fae91a3c2161d3192418aadd61ddd3a86a56cbe3dc85"}, + {file = "librt-0.7.8-cp310-cp310-win_amd64.whl", hash = "sha256:d6f254d096d84156a46a84861183c183d30734e52383602443292644d895047c"}, + {file = "librt-0.7.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ff3e9c11aa260c31493d4b3197d1e28dd07768594a4f92bec4506849d736248f"}, + {file = "librt-0.7.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddb52499d0b3ed4aa88746aaf6f36a08314677d5c346234c3987ddc506404eac"}, + {file = "librt-0.7.8-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e9c0afebbe6ce177ae8edba0c7c4d626f2a0fc12c33bb993d163817c41a7a05c"}, + {file = "librt-0.7.8-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:631599598e2c76ded400c0a8722dec09217c89ff64dc54b060f598ed68e7d2a8"}, + {file = "librt-0.7.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c1ba843ae20db09b9d5c80475376168feb2640ce91cd9906414f23cc267a1ff"}, + {file = "librt-0.7.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b5b007bb22ea4b255d3ee39dfd06d12534de2fcc3438567d9f48cdaf67ae1ae3"}, + {file = "librt-0.7.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dbd79caaf77a3f590cbe32dc2447f718772d6eea59656a7dcb9311161b10fa75"}, + {file = "librt-0.7.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:87808a8d1e0bd62a01cafc41f0fd6818b5a5d0ca0d8a55326a81643cdda8f873"}, + {file = "librt-0.7.8-cp311-cp311-win32.whl", hash = "sha256:31724b93baa91512bd0a376e7cf0b59d8b631ee17923b1218a65456fa9bda2e7"}, + {file = "librt-0.7.8-cp311-cp311-win_amd64.whl", hash = "sha256:978e8b5f13e52cf23a9e80f3286d7546baa70bc4ef35b51d97a709d0b28e537c"}, + {file = "librt-0.7.8-cp311-cp311-win_arm64.whl", hash = "sha256:20e3946863d872f7cabf7f77c6c9d370b8b3d74333d3a32471c50d3a86c0a232"}, + {file = "librt-0.7.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b6943885b2d49c48d0cff23b16be830ba46b0152d98f62de49e735c6e655a63"}, + {file = "librt-0.7.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46ef1f4b9b6cc364b11eea0ecc0897314447a66029ee1e55859acb3dd8757c93"}, + {file = "librt-0.7.8-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:907ad09cfab21e3c86e8f1f87858f7049d1097f77196959c033612f532b4e592"}, + {file = "librt-0.7.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2991b6c3775383752b3ca0204842743256f3ad3deeb1d0adc227d56b78a9a850"}, + {file = "librt-0.7.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03679b9856932b8c8f674e87aa3c55ea11c9274301f76ae8dc4d281bda55cf62"}, + {file = "librt-0.7.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3968762fec1b2ad34ce57458b6de25dbb4142713e9ca6279a0d352fa4e9f452b"}, + {file = "librt-0.7.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bb7a7807523a31f03061288cc4ffc065d684c39db7644c676b47d89553c0d714"}, + {file = "librt-0.7.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad64a14b1e56e702e19b24aae108f18ad1bf7777f3af5fcd39f87d0c5a814449"}, + {file = "librt-0.7.8-cp312-cp312-win32.whl", hash = "sha256:0241a6ed65e6666236ea78203a73d800dbed896cf12ae25d026d75dc1fcd1dac"}, + {file = "librt-0.7.8-cp312-cp312-win_amd64.whl", hash = "sha256:6db5faf064b5bab9675c32a873436b31e01d66ca6984c6f7f92621656033a708"}, + {file = "librt-0.7.8-cp312-cp312-win_arm64.whl", hash = "sha256:57175aa93f804d2c08d2edb7213e09276bd49097611aefc37e3fa38d1fb99ad0"}, + {file = "librt-0.7.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4c3995abbbb60b3c129490fa985dfe6cac11d88fc3c36eeb4fb1449efbbb04fc"}, + {file = "librt-0.7.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44e0c2cbc9bebd074cf2cdbe472ca185e824be4e74b1c63a8e934cea674bebf2"}, + {file = "librt-0.7.8-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d2f1e492cae964b3463a03dc77a7fe8742f7855d7258c7643f0ee32b6651dd3"}, + {file = "librt-0.7.8-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:451e7ffcef8f785831fdb791bd69211f47e95dc4c6ddff68e589058806f044c6"}, + {file = "librt-0.7.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3469e1af9f1380e093ae06bedcbdd11e407ac0b303a56bbe9afb1d6824d4982d"}, + {file = "librt-0.7.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f11b300027ce19a34f6d24ebb0a25fd0e24a9d53353225a5c1e6cadbf2916b2e"}, + {file = "librt-0.7.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4adc73614f0d3c97874f02f2c7fd2a27854e7e24ad532ea6b965459c5b757eca"}, + {file = "librt-0.7.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:60c299e555f87e4c01b2eca085dfccda1dde87f5a604bb45c2906b8305819a93"}, + {file = "librt-0.7.8-cp313-cp313-win32.whl", hash = "sha256:b09c52ed43a461994716082ee7d87618096851319bf695d57ec123f2ab708951"}, + {file = "librt-0.7.8-cp313-cp313-win_amd64.whl", hash = "sha256:f8f4a901a3fa28969d6e4519deceab56c55a09d691ea7b12ca830e2fa3461e34"}, + {file = "librt-0.7.8-cp313-cp313-win_arm64.whl", hash = "sha256:43d4e71b50763fcdcf64725ac680d8cfa1706c928b844794a7aa0fa9ac8e5f09"}, + {file = "librt-0.7.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:be927c3c94c74b05128089a955fba86501c3b544d1d300282cc1b4bd370cb418"}, + {file = "librt-0.7.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7b0803e9008c62a7ef79058233db7ff6f37a9933b8f2573c05b07ddafa226611"}, + {file = "librt-0.7.8-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:79feb4d00b2a4e0e05c9c56df707934f41fcb5fe53fd9efb7549068d0495b758"}, + {file = "librt-0.7.8-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9122094e3f24aa759c38f46bd8863433820654927370250f460ae75488b66ea"}, + {file = "librt-0.7.8-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e03bea66af33c95ce3addf87a9bf1fcad8d33e757bc479957ddbc0e4f7207ac"}, + {file = "librt-0.7.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f1ade7f31675db00b514b98f9ab9a7698c7282dad4be7492589109471852d398"}, + {file = "librt-0.7.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a14229ac62adcf1b90a15992f1ab9c69ae8b99ffb23cb64a90878a6e8a2f5b81"}, + {file = "librt-0.7.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5bcaaf624fd24e6a0cb14beac37677f90793a96864c67c064a91458611446e83"}, + {file = "librt-0.7.8-cp314-cp314-win32.whl", hash = "sha256:7aa7d5457b6c542ecaed79cec4ad98534373c9757383973e638ccced0f11f46d"}, + {file = "librt-0.7.8-cp314-cp314-win_amd64.whl", hash = "sha256:3d1322800771bee4a91f3b4bd4e49abc7d35e65166821086e5afd1e6c0d9be44"}, + {file = "librt-0.7.8-cp314-cp314-win_arm64.whl", hash = "sha256:5363427bc6a8c3b1719f8f3845ea53553d301382928a86e8fab7984426949bce"}, + {file = "librt-0.7.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ca916919793a77e4a98d4a1701e345d337ce53be4a16620f063191f7322ac80f"}, + {file = "librt-0.7.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:54feb7b4f2f6706bb82325e836a01be805770443e2400f706e824e91f6441dde"}, + {file = "librt-0.7.8-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:39a4c76fee41007070f872b648cc2f711f9abf9a13d0c7162478043377b52c8e"}, + {file = "librt-0.7.8-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac9c8a458245c7de80bc1b9765b177055efff5803f08e548dd4bb9ab9a8d789b"}, + {file = "librt-0.7.8-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b67aa7eff150f075fda09d11f6bfb26edffd300f6ab1666759547581e8f666"}, + {file = "librt-0.7.8-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:535929b6eff670c593c34ff435d5440c3096f20fa72d63444608a5aef64dd581"}, + {file = "librt-0.7.8-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:63937bd0f4d1cb56653dc7ae900d6c52c41f0015e25aaf9902481ee79943b33a"}, + {file = "librt-0.7.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf243da9e42d914036fd362ac3fa77d80a41cadcd11ad789b1b5eec4daaf67ca"}, + {file = "librt-0.7.8-cp314-cp314t-win32.whl", hash = "sha256:171ca3a0a06c643bd0a2f62a8944e1902c94aa8e5da4db1ea9a8daf872685365"}, + {file = "librt-0.7.8-cp314-cp314t-win_amd64.whl", hash = "sha256:445b7304145e24c60288a2f172b5ce2ca35c0f81605f5299f3fa567e189d2e32"}, + {file = "librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06"}, + {file = "librt-0.7.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c7e8f88f79308d86d8f39c491773cbb533d6cb7fa6476f35d711076ee04fceb6"}, + {file = "librt-0.7.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:389bd25a0db916e1d6bcb014f11aa9676cedaa485e9ec3752dfe19f196fd377b"}, + {file = "librt-0.7.8-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:73fd300f501a052f2ba52ede721232212f3b06503fa12665408ecfc9d8fd149c"}, + {file = "librt-0.7.8-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d772edc6a5f7835635c7562f6688e031f0b97e31d538412a852c49c9a6c92d5"}, + {file = "librt-0.7.8-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde8a130bd0f239e45503ab39fab239ace094d63ee1d6b67c25a63d741c0f71"}, + {file = "librt-0.7.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fdec6e2368ae4f796fc72fad7fd4bd1753715187e6d870932b0904609e7c878e"}, + {file = "librt-0.7.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:00105e7d541a8f2ee5be52caacea98a005e0478cfe78c8080fbb7b5d2b340c63"}, + {file = "librt-0.7.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c6f8947d3dfd7f91066c5b4385812c18be26c9d5a99ca56667547f2c39149d94"}, + {file = "librt-0.7.8-cp39-cp39-win32.whl", hash = "sha256:41d7bb1e07916aeb12ae4a44e3025db3691c4149ab788d0315781b4d29b86afb"}, + {file = "librt-0.7.8-cp39-cp39-win_amd64.whl", hash = "sha256:e90a8e237753c83b8e484d478d9a996dc5e39fd5bd4c6ce32563bc8123f132be"}, + {file = "librt-0.7.8.tar.gz", hash = "sha256:1a4ede613941d9c3470b0368be851df6bb78ab218635512d0370b27a277a0862"}, +] + [[package]] name = "markdown" -version = "3.8.2" +version = "3.10.1" description = "Python implementation of John Gruber's Markdown." optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24"}, - {file = "markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45"}, + {file = "markdown-3.10.1-py3-none-any.whl", hash = "sha256:867d788939fe33e4b736426f5b9f651ad0c0ae0ecf89df0ca5d1176c70812fe3"}, + {file = "markdown-3.10.1.tar.gz", hash = "sha256:1c19c10bd5c14ac948c53d0d762a04e2fa35a6d58a6b7b1e6bfcbe6fefc0001a"}, ] [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]"] +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.2" +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.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"}, + {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]] @@ -383,47 +523,54 @@ pyyaml = ">=5.1" [[package]] name = "mypy" -version = "1.17.0" +version = "1.19.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" groups = ["dev"] 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"}, + {file = "mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec"}, + {file = "mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b"}, + {file = "mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6"}, + {file = "mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74"}, + {file = "mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1"}, + {file = "mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac"}, + {file = "mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288"}, + {file = "mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab"}, + {file = "mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6"}, + {file = "mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331"}, + {file = "mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925"}, + {file = "mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042"}, + {file = "mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1"}, + {file = "mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e"}, + {file = "mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2"}, + {file = "mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8"}, + {file = "mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a"}, + {file = "mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13"}, + {file = "mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250"}, + {file = "mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b"}, + {file = "mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e"}, + {file = "mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef"}, + {file = "mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75"}, + {file = "mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd"}, + {file = "mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1"}, + {file = "mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718"}, + {file = "mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b"}, + {file = "mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045"}, + {file = "mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957"}, + {file = "mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f"}, + {file = "mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3"}, + {file = "mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a"}, + {file = "mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67"}, + {file = "mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e"}, + {file = "mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376"}, + {file = "mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24"}, + {file = "mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247"}, + {file = "mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba"}, ] [package.dependencies] +librt = {version = ">=0.6.2", markers = "platform_python_implementation != \"PyPy\""} mypy_extensions = ">=1.0.0" pathspec = ">=0.9.0" typing_extensions = ">=4.6.0" @@ -449,26 +596,26 @@ files = [ [[package]] name = "nodeenv" -version = "1.9.1" +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.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, - {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, + {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 = "25.0" +version = "26.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, + {file = "packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529"}, + {file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"}, ] [[package]] @@ -485,32 +632,38 @@ files = [ [[package]] name = "pathspec" -version = "0.12.1" +version = "1.0.3" description = "Utility library for gitignore style pattern matching of file paths." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, + {file = "pathspec-1.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c"}, + {file = "pathspec-1.0.3.tar.gz", hash = "sha256:bac5cf97ae2c2876e2d25ebb15078eb04d76e4b98921ee31c6f85ade8b59444d"}, ] +[package.extras] +hyperscan = ["hyperscan (>=0.7)"] +optional = ["typing-extensions (>=4)"] +re2 = ["google-re2 (>=1.1)"] +tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] + [[package]] name = "platformdirs" -version = "4.3.8" +version = "4.5.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, - {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, + {file = "platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31"}, + {file = "platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda"}, ] [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)"] +docs = ["furo (>=2025.9.25)", "proselint (>=0.14)", "sphinx (>=8.2.3)", "sphinx-autodoc-typehints (>=3.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.4.2)", "pytest-cov (>=7)", "pytest-mock (>=3.15.1)"] +type = ["mypy (>=1.18.2)"] [[package]] name = "pluggy" @@ -530,33 +683,33 @@ testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "poethepoet" -version = "0.36.0" +version = "0.40.0" description = "A task runner that works well with poetry and uv." optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "poethepoet-0.36.0-py3-none-any.whl", hash = "sha256:693e3c1eae9f6731d3613c3c0c40f747d3c5c68a375beda42e590a63c5623308"}, - {file = "poethepoet-0.36.0.tar.gz", hash = "sha256:2217b49cb4e4c64af0b42ff8c4814b17f02e107d38bc461542517348ede25663"}, + {file = "poethepoet-0.40.0-py3-none-any.whl", hash = "sha256:afd276ae31d5c53573c0c14898118d4848ccee3709b6b0be6a1c6cbe522bbc8a"}, + {file = "poethepoet-0.40.0.tar.gz", hash = "sha256:91835f00d03d6c4f0e146f80fa510e298ad865e7edd27fe4cb9c94fdc090791b"}, ] [package.dependencies] pastel = ">=0.2.1,<0.3.0" -pyyaml = ">=6.0.2,<7.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.2.0" +version = "4.5.1" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] 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"}, + {file = "pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77"}, + {file = "pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61"}, ] [package.dependencies] @@ -583,20 +736,20 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "8.4.1" +version = "9.0.2" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, - {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, + {file = "pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b"}, + {file = "pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11"}, ] [package.dependencies] colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} -iniconfig = ">=1" -packaging = ">=20" +iniconfig = ">=1.0.1" +packaging = ">=22" pluggy = ">=1.5,<2" pygments = ">=2.7.2" @@ -605,23 +758,23 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests [[package]] name = "pytest-cov" -version = "6.2.1" +version = "7.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" groups = ["dev"] 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"}, + {file = "pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861"}, + {file = "pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1"}, ] [package.dependencies] -coverage = {version = ">=7.5", extras = ["toml"]} +coverage = {version = ">=7.10.6", extras = ["toml"]} pluggy = ">=1.2" -pytest = ">=6.2.5" +pytest = ">=7" [package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] +testing = ["process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-xdist" @@ -661,65 +814,85 @@ six = ">=1.5" [[package]] name = "pyyaml" -version = "6.0.2" +version = "6.0.3" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" groups = ["dev"] 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"}, + {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]] @@ -739,30 +912,31 @@ pyyaml = "*" [[package]] name = "ruff" -version = "0.12.4" +version = "0.14.14" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["dev"] 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"}, + {file = "ruff-0.14.14-py3-none-linux_armv6l.whl", hash = "sha256:7cfe36b56e8489dee8fbc777c61959f60ec0f1f11817e8f2415f429552846aed"}, + {file = "ruff-0.14.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6006a0082336e7920b9573ef8a7f52eec837add1265cc74e04ea8a4368cd704c"}, + {file = "ruff-0.14.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:026c1d25996818f0bf498636686199d9bd0d9d6341c9c2c3b62e2a0198b758de"}, + {file = "ruff-0.14.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f666445819d31210b71e0a6d1c01e24447a20b85458eea25a25fe8142210ae0e"}, + {file = "ruff-0.14.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c0f18b922c6d2ff9a5e6c3ee16259adc513ca775bcf82c67ebab7cbd9da5bc8"}, + {file = "ruff-0.14.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1629e67489c2dea43e8658c3dba659edbfd87361624b4040d1df04c9740ae906"}, + {file = "ruff-0.14.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:27493a2131ea0f899057d49d303e4292b2cae2bb57253c1ed1f256fbcd1da480"}, + {file = "ruff-0.14.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ff589aab3f5b539e35db38425da31a57521efd1e4ad1ae08fc34dbe30bd7df"}, + {file = "ruff-0.14.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc12d74eef0f29f51775f5b755913eb523546b88e2d733e1d701fe65144e89b"}, + {file = "ruff-0.14.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb8481604b7a9e75eff53772496201690ce2687067e038b3cc31aaf16aa0b974"}, + {file = "ruff-0.14.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:14649acb1cf7b5d2d283ebd2f58d56b75836ed8c6f329664fa91cdea19e76e66"}, + {file = "ruff-0.14.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8058d2145566510790eab4e2fad186002e288dec5e0d343a92fe7b0bc1b3e13"}, + {file = "ruff-0.14.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e651e977a79e4c758eb807f0481d673a67ffe53cfa92209781dfa3a996cf8412"}, + {file = "ruff-0.14.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cc8b22da8d9d6fdd844a68ae937e2a0adf9b16514e9a97cc60355e2d4b219fc3"}, + {file = "ruff-0.14.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:16bc890fb4cc9781bb05beb5ab4cd51be9e7cb376bf1dd3580512b24eb3fda2b"}, + {file = "ruff-0.14.14-py3-none-win32.whl", hash = "sha256:b530c191970b143375b6a68e6f743800b2b786bbcf03a7965b06c4bf04568167"}, + {file = "ruff-0.14.14-py3-none-win_amd64.whl", hash = "sha256:3dde1435e6b6fe5b66506c1dff67a421d0b7f6488d466f651c07f4cab3bf20fd"}, + {file = "ruff-0.14.14-py3-none-win_arm64.whl", hash = "sha256:56e6981a98b13a32236a72a8da421d7839221fa308b223b9283312312e5ac76c"}, + {file = "ruff-0.14.14.tar.gz", hash = "sha256:2d0f819c9a90205f3a867dbbd0be083bee9912e170fd7d9704cc8ae45824896b"}, ] [[package]] @@ -800,14 +974,14 @@ files = [ [[package]] name = "typing-extensions" -version = "4.14.1" +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.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"}, - {file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"}, + {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]] diff --git a/pyproject.toml b/pyproject.toml index 9853d1cd6..f9fb783f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ pytest-xdist = "*" setuptools = ">=70.1.1,<79.0.0" [build-system] -requires = ["poetry>=0.12"] +requires = ["poetry>=2.3.1"] build-backend = "poetry.masonry.api" [tool.poe.tasks] From ef3d6a4743ef1199fe9b3f9f9b21491d2dfec9cb Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 25 Jan 2026 13:14:20 +0100 Subject: [PATCH 060/169] feat: drop usage of removed rule The rule no longer exist in ruff system. --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f9fb783f1..60eb0b931 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -162,5 +162,4 @@ ignore = [ "UP028", "UP030", "UP031", - "UP038", ] From 6bd3be7043629c35b3bd5179d9d1ffe4e4145c7f Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 25 Jan 2026 13:19:17 +0100 Subject: [PATCH 061/169] lint: mark parameters as dummy --- src/arch/z80/backend/_str.py | 22 +++++++++++----------- src/zxbasm/memory.py | 2 +- tests/functional/test.py | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/arch/z80/backend/_str.py b/src/arch/z80/backend/_str.py index adcd20076..3f57ba47e 100644 --- a/src/arch/z80/backend/_str.py +++ b/src/arch/z80/backend/_str.py @@ -125,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") @@ -143,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 @@ -153,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 @@ -163,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 @@ -173,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 @@ -183,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 @@ -193,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 @@ -201,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") @@ -243,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)) @@ -326,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 @@ -343,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/zxbasm/memory.py b/src/zxbasm/memory.py index a06613413..55e795b9c 100644 --- a/src/zxbasm/memory.py +++ b/src/zxbasm/memory.py @@ -276,7 +276,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/tests/functional/test.py b/tests/functional/test.py index 8923e5aa4..91a330149 100755 --- a/tests/functional/test.py +++ b/tests/functional/test.py @@ -555,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) From 16a29507717a6dc84d2d51e64011ef615631ac66 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 25 Jan 2026 13:27:10 +0100 Subject: [PATCH 062/169] lint: ignore scratch/ folder if exists --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 60eb0b931..b4cbd47c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,7 +99,8 @@ disable_error_code = [ "var-annotated", ] exclude = [ - 'src/ply/.*\.py$' + 'src/ply/.*\.py$', + 'scratch/*', ] [[tool.mypy.overrides]] From b2c3d3fd01e1ce09cc63cd0a8f83bae25dc976e9 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 1 Feb 2026 13:29:28 +0100 Subject: [PATCH 063/169] Uses ; for ASM comments --- tools/consolidate-libs.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/consolidate-libs.py b/tools/consolidate-libs.py index 81a96db8b..8ae4354dc 100755 --- a/tools/consolidate-libs.py +++ b/tools/consolidate-libs.py @@ -82,6 +82,10 @@ def fold_files(scan: dict[FileInfo, list[str]]) -> None: 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: @@ -89,7 +93,7 @@ def fold_files(scan: dict[FileInfo, list[str]]) -> None: print(f"Linking {file} to {main_file} with an include") with open(file, "wt", encoding="utf-8") as f: - f.write(COPYRIGHT_HEADER) + f.write(header) f.write(f"\n#include once [arch:{arch}] <{main_file_basename}>\n") From a2c66a1c39123b6064ccbc5488ea05c0a51ad7bf Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 1 Feb 2026 13:31:13 +0100 Subject: [PATCH 064/169] Fix: use ; for asm comments --- src/lib/arch/zxnext/runtime/arith/_mul32.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/arith/addf.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/arith/div16.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/arith/div32.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/arith/div8.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/arith/divf.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/arith/divf16.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/arith/modf.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/arith/modf16.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/arith/mul32.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/arith/mulf.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/arith/mulf16.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/arith/sub32.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/arith/subf.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/sgn.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/sgnf.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/sgnf16.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/sgni16.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/sgni32.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/sgni8.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/sgnu16.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/sgnu32.asm | 12 ++++++------ src/lib/arch/zxnext/runtime/sgnu8.asm | 12 ++++++------ 23 files changed, 138 insertions(+), 138 deletions(-) diff --git a/src/lib/arch/zxnext/runtime/arith/_mul32.asm b/src/lib/arch/zxnext/runtime/arith/_mul32.asm index 0f8c4d04f..48b0800f6 100644 --- a/src/lib/arch/zxnext/runtime/arith/_mul32.asm +++ b/src/lib/arch/zxnext/runtime/arith/_mul32.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 b48d92e0d..176227d05 100644 --- a/src/lib/arch/zxnext/runtime/arith/addf.asm +++ b/src/lib/arch/zxnext/runtime/arith/addf.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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/div16.asm b/src/lib/arch/zxnext/runtime/arith/div16.asm index 120f1f1a0..e11169336 100644 --- a/src/lib/arch/zxnext/runtime/arith/div16.asm +++ b/src/lib/arch/zxnext/runtime/arith/div16.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 cccc0c78c..95758a3b3 100644 --- a/src/lib/arch/zxnext/runtime/arith/div32.asm +++ b/src/lib/arch/zxnext/runtime/arith/div32.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 b131a6ef7..5488bfb63 100644 --- a/src/lib/arch/zxnext/runtime/arith/div8.asm +++ b/src/lib/arch/zxnext/runtime/arith/div8.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 0e8fb5494..56194aa25 100644 --- a/src/lib/arch/zxnext/runtime/arith/divf.asm +++ b/src/lib/arch/zxnext/runtime/arith/divf.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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/divf16.asm b/src/lib/arch/zxnext/runtime/arith/divf16.asm index dc5873d6c..7b4144547 100644 --- a/src/lib/arch/zxnext/runtime/arith/divf16.asm +++ b/src/lib/arch/zxnext/runtime/arith/divf16.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 c5b8a14b0..0998f7245 100644 --- a/src/lib/arch/zxnext/runtime/arith/modf.asm +++ b/src/lib/arch/zxnext/runtime/arith/modf.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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/modf16.asm b/src/lib/arch/zxnext/runtime/arith/modf16.asm index ea647fd18..d97738174 100644 --- a/src/lib/arch/zxnext/runtime/arith/modf16.asm +++ b/src/lib/arch/zxnext/runtime/arith/modf16.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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/mul32.asm b/src/lib/arch/zxnext/runtime/arith/mul32.asm index 16212bf1a..f11551544 100644 --- a/src/lib/arch/zxnext/runtime/arith/mul32.asm +++ b/src/lib/arch/zxnext/runtime/arith/mul32.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 ee75de8d6..4c10d267f 100644 --- a/src/lib/arch/zxnext/runtime/arith/mulf.asm +++ b/src/lib/arch/zxnext/runtime/arith/mulf.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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/mulf16.asm b/src/lib/arch/zxnext/runtime/arith/mulf16.asm index b2d8c553d..477afc3ee 100644 --- a/src/lib/arch/zxnext/runtime/arith/mulf16.asm +++ b/src/lib/arch/zxnext/runtime/arith/mulf16.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 a1d0f929b..26e971142 100644 --- a/src/lib/arch/zxnext/runtime/arith/sub32.asm +++ b/src/lib/arch/zxnext/runtime/arith/sub32.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 54aa06129..19f7d42e2 100644 --- a/src/lib/arch/zxnext/runtime/arith/subf.asm +++ b/src/lib/arch/zxnext/runtime/arith/subf.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 ca8c13f71..d75fbeff2 100644 --- a/src/lib/arch/zxnext/runtime/sgn.asm +++ b/src/lib/arch/zxnext/runtime/sgn.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 1e99b6348..b478c5294 100644 --- a/src/lib/arch/zxnext/runtime/sgnf.asm +++ b/src/lib/arch/zxnext/runtime/sgnf.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 ebc6b002f..53e69c4fe 100644 --- a/src/lib/arch/zxnext/runtime/sgnf16.asm +++ b/src/lib/arch/zxnext/runtime/sgnf16.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 095731b0b..d46e6ee4d 100644 --- a/src/lib/arch/zxnext/runtime/sgni16.asm +++ b/src/lib/arch/zxnext/runtime/sgni16.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 507e4f863..b4d02e549 100644 --- a/src/lib/arch/zxnext/runtime/sgni32.asm +++ b/src/lib/arch/zxnext/runtime/sgni32.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 4c01b4850..c8334c54b 100644 --- a/src/lib/arch/zxnext/runtime/sgni8.asm +++ b/src/lib/arch/zxnext/runtime/sgni8.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 18b51a97c..046778634 100644 --- a/src/lib/arch/zxnext/runtime/sgnu16.asm +++ b/src/lib/arch/zxnext/runtime/sgnu16.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 4a15499da..0a0f1f8a2 100644 --- a/src/lib/arch/zxnext/runtime/sgnu32.asm +++ b/src/lib/arch/zxnext/runtime/sgnu32.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; 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 a651cf690..47e9a2b8f 100644 --- a/src/lib/arch/zxnext/runtime/sgnu8.asm +++ b/src/lib/arch/zxnext/runtime/sgnu8.asm @@ -1,8 +1,8 @@ -' ---------------------------------------------------------------- -' This file is released under the MIT License -' -' Copyleft (k) 2008 -' by Jose Rodriguez-Rosa (a.k.a. Boriel) -' ---------------------------------------------------------------- +; ---------------------------------------------------------------- +; This file is released under the MIT License +; +; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) +; ---------------------------------------------------------------- #include once [arch:zx48k] From cf759682859b19542f989ab240e886612ee91e57 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 1 Feb 2026 13:40:48 +0100 Subject: [PATCH 065/169] Update tests --- tests/functional/arch/zxnext/addf.asm | 12 ++-- tests/functional/arch/zxnext/divf00.asm | 12 ++-- tests/functional/arch/zxnext/divf01.asm | 12 ++-- tests/functional/arch/zxnext/divf16.asm | 24 ++++---- tests/functional/arch/zxnext/divf16a.asm | 24 ++++---- tests/functional/arch/zxnext/divf16b.asm | 24 ++++---- tests/functional/arch/zxnext/divf16c.asm | 24 ++++---- tests/functional/arch/zxnext/divi16a.asm | 12 ++-- tests/functional/arch/zxnext/divi16b.asm | 12 ++-- tests/functional/arch/zxnext/divi32c.asm | 12 ++-- tests/functional/arch/zxnext/divi8.asm | 12 ++-- tests/functional/arch/zxnext/divi8a.asm | 12 ++-- tests/functional/arch/zxnext/divi8b.asm | 12 ++-- tests/functional/arch/zxnext/divu16.asm | 12 ++-- tests/functional/arch/zxnext/divu16a.asm | 12 ++-- tests/functional/arch/zxnext/divu16b.asm | 12 ++-- tests/functional/arch/zxnext/divu32c.asm | 12 ++-- tests/functional/arch/zxnext/divu8.asm | 12 ++-- tests/functional/arch/zxnext/divu8a.asm | 12 ++-- tests/functional/arch/zxnext/divu8b.asm | 12 ++-- tests/functional/arch/zxnext/equf.asm | 12 ++-- tests/functional/arch/zxnext/fact.asm | 36 +++++------ tests/functional/arch/zxnext/gef16.asm | 12 ++-- tests/functional/arch/zxnext/gei32.asm | 12 ++-- tests/functional/arch/zxnext/geu32.asm | 12 ++-- tests/functional/arch/zxnext/gtf16.asm | 12 ++-- tests/functional/arch/zxnext/gti32.asm | 12 ++-- tests/functional/arch/zxnext/lei32.asm | 12 ++-- tests/functional/arch/zxnext/ltf16.asm | 12 ++-- tests/functional/arch/zxnext/lti32.asm | 12 ++-- tests/functional/arch/zxnext/ltu32.asm | 12 ++-- tests/functional/arch/zxnext/modf.asm | 12 ++-- tests/functional/arch/zxnext/modf16c.asm | 60 +++++++++---------- tests/functional/arch/zxnext/modi32c.asm | 12 ++-- tests/functional/arch/zxnext/modi8.asm | 12 ++-- tests/functional/arch/zxnext/modi8a.asm | 12 ++-- tests/functional/arch/zxnext/modi8b.asm | 12 ++-- tests/functional/arch/zxnext/modu32c.asm | 12 ++-- tests/functional/arch/zxnext/modu8.asm | 12 ++-- tests/functional/arch/zxnext/modu8a.asm | 12 ++-- tests/functional/arch/zxnext/modu8b.asm | 12 ++-- tests/functional/arch/zxnext/mulf00.asm | 12 ++-- tests/functional/arch/zxnext/mulf01.asm | 12 ++-- tests/functional/arch/zxnext/mulf16.asm | 24 ++++---- tests/functional/arch/zxnext/mulf16a.asm | 24 ++++---- tests/functional/arch/zxnext/print_i16.asm | 12 ++-- tests/functional/arch/zxnext/print_i32.asm | 12 ++-- tests/functional/arch/zxnext/print_i8.asm | 12 ++-- tests/functional/arch/zxnext/print_u16.asm | 12 ++-- tests/functional/arch/zxnext/print_u32.asm | 12 ++-- tests/functional/arch/zxnext/print_u8.asm | 12 ++-- tests/functional/arch/zxnext/sgnf.asm | 24 ++++---- tests/functional/arch/zxnext/sgnf16.asm | 24 ++++---- tests/functional/arch/zxnext/sgni16.asm | 24 ++++---- tests/functional/arch/zxnext/sgni32.asm | 24 ++++---- tests/functional/arch/zxnext/sgni8.asm | 12 ++-- tests/functional/arch/zxnext/sgnu16.asm | 12 ++-- tests/functional/arch/zxnext/sgnu32.asm | 12 ++-- tests/functional/arch/zxnext/sgnu8.asm | 12 ++-- .../arch/zxnext/stdlib_spectranet.asm | 12 ++-- tests/functional/arch/zxnext/subf00.asm | 12 ++-- tests/functional/arch/zxnext/subf01.asm | 12 ++-- tests/functional/arch/zxnext/subf16c.asm | 12 ++-- tests/functional/arch/zxnext/subi32c.asm | 12 ++-- 64 files changed, 480 insertions(+), 480 deletions(-) diff --git a/tests/functional/arch/zxnext/addf.asm b/tests/functional/arch/zxnext/addf.asm index 37fa6e724..c8b3c636d 100644 --- a/tests/functional/arch/zxnext/addf.asm +++ b/tests/functional/arch/zxnext/addf.asm @@ -54,12 +54,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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" ; ------------------------------------------------------------- diff --git a/tests/functional/arch/zxnext/divf00.asm b/tests/functional/arch/zxnext/divf00.asm index c398e7663..da0a18f3b 100644 --- a/tests/functional/arch/zxnext/divf00.asm +++ b/tests/functional/arch/zxnext/divf00.asm @@ -47,12 +47,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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" ; ------------------------------------------------------------- diff --git a/tests/functional/arch/zxnext/divf01.asm b/tests/functional/arch/zxnext/divf01.asm index 834a3b0e7..67c32839d 100644 --- a/tests/functional/arch/zxnext/divf01.asm +++ b/tests/functional/arch/zxnext/divf01.asm @@ -51,12 +51,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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" ; ------------------------------------------------------------- diff --git a/tests/functional/arch/zxnext/divf16.asm b/tests/functional/arch/zxnext/divf16.asm index 8fff86ee2..8a2a54ee7 100644 --- a/tests/functional/arch/zxnext/divf16.asm +++ b/tests/functional/arch/zxnext/divf16.asm @@ -98,20 +98,20 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divf16a.asm b/tests/functional/arch/zxnext/divf16a.asm index 76c3de713..3a85505f8 100644 --- a/tests/functional/arch/zxnext/divf16a.asm +++ b/tests/functional/arch/zxnext/divf16a.asm @@ -43,20 +43,20 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divf16b.asm b/tests/functional/arch/zxnext/divf16b.asm index 803fc41e9..8215bcb86 100644 --- a/tests/functional/arch/zxnext/divf16b.asm +++ b/tests/functional/arch/zxnext/divf16b.asm @@ -53,20 +53,20 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divf16c.asm b/tests/functional/arch/zxnext/divf16c.asm index e23a54d39..ba334fab9 100644 --- a/tests/functional/arch/zxnext/divf16c.asm +++ b/tests/functional/arch/zxnext/divf16c.asm @@ -85,20 +85,20 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divi16a.asm b/tests/functional/arch/zxnext/divi16a.asm index 654924c74..d3ddff193 100644 --- a/tests/functional/arch/zxnext/divi16a.asm +++ b/tests/functional/arch/zxnext/divi16a.asm @@ -35,12 +35,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divi16b.asm b/tests/functional/arch/zxnext/divi16b.asm index 1f1579086..a9abbb4a5 100644 --- a/tests/functional/arch/zxnext/divi16b.asm +++ b/tests/functional/arch/zxnext/divi16b.asm @@ -42,12 +42,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divi32c.asm b/tests/functional/arch/zxnext/divi32c.asm index ccdb1d51a..8032f1097 100644 --- a/tests/functional/arch/zxnext/divi32c.asm +++ b/tests/functional/arch/zxnext/divi32c.asm @@ -88,12 +88,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divi8.asm b/tests/functional/arch/zxnext/divi8.asm index bb797ef48..cac68778c 100644 --- a/tests/functional/arch/zxnext/divi8.asm +++ b/tests/functional/arch/zxnext/divi8.asm @@ -58,12 +58,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divi8a.asm b/tests/functional/arch/zxnext/divi8a.asm index eb49f9817..d2d17a125 100644 --- a/tests/functional/arch/zxnext/divi8a.asm +++ b/tests/functional/arch/zxnext/divi8a.asm @@ -35,12 +35,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divi8b.asm b/tests/functional/arch/zxnext/divi8b.asm index baa23cc43..6167a3b63 100644 --- a/tests/functional/arch/zxnext/divi8b.asm +++ b/tests/functional/arch/zxnext/divi8b.asm @@ -41,12 +41,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divu16.asm b/tests/functional/arch/zxnext/divu16.asm index ad677a4ce..d864a7b02 100644 --- a/tests/functional/arch/zxnext/divu16.asm +++ b/tests/functional/arch/zxnext/divu16.asm @@ -59,12 +59,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divu16a.asm b/tests/functional/arch/zxnext/divu16a.asm index d5c4cb928..f435badbd 100644 --- a/tests/functional/arch/zxnext/divu16a.asm +++ b/tests/functional/arch/zxnext/divu16a.asm @@ -35,12 +35,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divu16b.asm b/tests/functional/arch/zxnext/divu16b.asm index fd5e23746..b296a0e3a 100644 --- a/tests/functional/arch/zxnext/divu16b.asm +++ b/tests/functional/arch/zxnext/divu16b.asm @@ -42,12 +42,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divu32c.asm b/tests/functional/arch/zxnext/divu32c.asm index 3fd2607d3..581d0b191 100644 --- a/tests/functional/arch/zxnext/divu32c.asm +++ b/tests/functional/arch/zxnext/divu32c.asm @@ -88,12 +88,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divu8.asm b/tests/functional/arch/zxnext/divu8.asm index 2a4d98320..6a31559eb 100644 --- a/tests/functional/arch/zxnext/divu8.asm +++ b/tests/functional/arch/zxnext/divu8.asm @@ -58,12 +58,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divu8a.asm b/tests/functional/arch/zxnext/divu8a.asm index 92ec2d0e0..7e236c98d 100644 --- a/tests/functional/arch/zxnext/divu8a.asm +++ b/tests/functional/arch/zxnext/divu8a.asm @@ -35,12 +35,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/divu8b.asm b/tests/functional/arch/zxnext/divu8b.asm index 7dad972bd..b8509aea6 100644 --- a/tests/functional/arch/zxnext/divu8b.asm +++ b/tests/functional/arch/zxnext/divu8b.asm @@ -41,12 +41,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/equf.asm b/tests/functional/arch/zxnext/equf.asm index c1f454320..918367e2e 100644 --- a/tests/functional/arch/zxnext/equf.asm +++ b/tests/functional/arch/zxnext/equf.asm @@ -46,12 +46,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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" ; ------------------------------------------------------------- diff --git a/tests/functional/arch/zxnext/fact.asm b/tests/functional/arch/zxnext/fact.asm index f234d54d5..7e875f4ad 100644 --- a/tests/functional/arch/zxnext/fact.asm +++ b/tests/functional/arch/zxnext/fact.asm @@ -97,20 +97,20 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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. @@ -196,12 +196,12 @@ __TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) #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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/gef16.asm b/tests/functional/arch/zxnext/gef16.asm index 68fdd84df..d90cc89ef 100644 --- a/tests/functional/arch/zxnext/gef16.asm +++ b/tests/functional/arch/zxnext/gef16.asm @@ -105,12 +105,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/gei32.asm b/tests/functional/arch/zxnext/gei32.asm index 0a085d107..de31b572d 100644 --- a/tests/functional/arch/zxnext/gei32.asm +++ b/tests/functional/arch/zxnext/gei32.asm @@ -121,12 +121,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/geu32.asm b/tests/functional/arch/zxnext/geu32.asm index 308414e69..d66a4656b 100644 --- a/tests/functional/arch/zxnext/geu32.asm +++ b/tests/functional/arch/zxnext/geu32.asm @@ -120,12 +120,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/gtf16.asm b/tests/functional/arch/zxnext/gtf16.asm index 0df55aa79..3a39cde3e 100644 --- a/tests/functional/arch/zxnext/gtf16.asm +++ b/tests/functional/arch/zxnext/gtf16.asm @@ -105,12 +105,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/gti32.asm b/tests/functional/arch/zxnext/gti32.asm index 37f8a793d..1c37f5ffd 100644 --- a/tests/functional/arch/zxnext/gti32.asm +++ b/tests/functional/arch/zxnext/gti32.asm @@ -121,12 +121,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/lei32.asm b/tests/functional/arch/zxnext/lei32.asm index 9c2c9ca96..4e337f4b7 100644 --- a/tests/functional/arch/zxnext/lei32.asm +++ b/tests/functional/arch/zxnext/lei32.asm @@ -106,12 +106,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/ltf16.asm b/tests/functional/arch/zxnext/ltf16.asm index 9296fa20a..44a6fccaf 100644 --- a/tests/functional/arch/zxnext/ltf16.asm +++ b/tests/functional/arch/zxnext/ltf16.asm @@ -93,12 +93,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/lti32.asm b/tests/functional/arch/zxnext/lti32.asm index e2090c640..fd9c08a94 100644 --- a/tests/functional/arch/zxnext/lti32.asm +++ b/tests/functional/arch/zxnext/lti32.asm @@ -106,12 +106,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/ltu32.asm b/tests/functional/arch/zxnext/ltu32.asm index 5850b0370..2b1a05ea4 100644 --- a/tests/functional/arch/zxnext/ltu32.asm +++ b/tests/functional/arch/zxnext/ltu32.asm @@ -115,12 +115,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/modf.asm b/tests/functional/arch/zxnext/modf.asm index 66b8ae1b2..caffde7eb 100644 --- a/tests/functional/arch/zxnext/modf.asm +++ b/tests/functional/arch/zxnext/modf.asm @@ -37,12 +37,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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" ; ------------------------------------------------------------- diff --git a/tests/functional/arch/zxnext/modf16c.asm b/tests/functional/arch/zxnext/modf16c.asm index 3f27c5d53..0ca0b87b3 100644 --- a/tests/functional/arch/zxnext/modf16c.asm +++ b/tests/functional/arch/zxnext/modf16c.asm @@ -85,29 +85,29 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 @@ -321,20 +321,20 @@ __ENDF16DIV: ; Put the sign on the result #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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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. diff --git a/tests/functional/arch/zxnext/modi32c.asm b/tests/functional/arch/zxnext/modi32c.asm index 4f60900d6..275a73e93 100644 --- a/tests/functional/arch/zxnext/modi32c.asm +++ b/tests/functional/arch/zxnext/modi32c.asm @@ -88,12 +88,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/modi8.asm b/tests/functional/arch/zxnext/modi8.asm index 72780f141..88c9e15b8 100644 --- a/tests/functional/arch/zxnext/modi8.asm +++ b/tests/functional/arch/zxnext/modi8.asm @@ -57,12 +57,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/modi8a.asm b/tests/functional/arch/zxnext/modi8a.asm index 69023bf2e..ee3b7f28a 100644 --- a/tests/functional/arch/zxnext/modi8a.asm +++ b/tests/functional/arch/zxnext/modi8a.asm @@ -35,12 +35,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/modi8b.asm b/tests/functional/arch/zxnext/modi8b.asm index fe2ca67d4..3455987b9 100644 --- a/tests/functional/arch/zxnext/modi8b.asm +++ b/tests/functional/arch/zxnext/modi8b.asm @@ -41,12 +41,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/modu32c.asm b/tests/functional/arch/zxnext/modu32c.asm index c50be0120..28f8ecf51 100644 --- a/tests/functional/arch/zxnext/modu32c.asm +++ b/tests/functional/arch/zxnext/modu32c.asm @@ -88,12 +88,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/modu8.asm b/tests/functional/arch/zxnext/modu8.asm index 14c0ec2b9..5527e0e55 100644 --- a/tests/functional/arch/zxnext/modu8.asm +++ b/tests/functional/arch/zxnext/modu8.asm @@ -57,12 +57,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/modu8a.asm b/tests/functional/arch/zxnext/modu8a.asm index dc6511722..c5c067e1d 100644 --- a/tests/functional/arch/zxnext/modu8a.asm +++ b/tests/functional/arch/zxnext/modu8a.asm @@ -35,12 +35,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/modu8b.asm b/tests/functional/arch/zxnext/modu8b.asm index 47fb7741d..d0f35a0e4 100644 --- a/tests/functional/arch/zxnext/modu8b.asm +++ b/tests/functional/arch/zxnext/modu8b.asm @@ -41,12 +41,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/mulf00.asm b/tests/functional/arch/zxnext/mulf00.asm index 8e0133423..f575707be 100644 --- a/tests/functional/arch/zxnext/mulf00.asm +++ b/tests/functional/arch/zxnext/mulf00.asm @@ -47,12 +47,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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" ; ------------------------------------------------------------- diff --git a/tests/functional/arch/zxnext/mulf01.asm b/tests/functional/arch/zxnext/mulf01.asm index 8ff9e79b3..d2f80a325 100644 --- a/tests/functional/arch/zxnext/mulf01.asm +++ b/tests/functional/arch/zxnext/mulf01.asm @@ -51,12 +51,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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" ; ------------------------------------------------------------- diff --git a/tests/functional/arch/zxnext/mulf16.asm b/tests/functional/arch/zxnext/mulf16.asm index 9e16165d2..73797e05d 100644 --- a/tests/functional/arch/zxnext/mulf16.asm +++ b/tests/functional/arch/zxnext/mulf16.asm @@ -62,12 +62,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 @@ -96,12 +96,12 @@ __NEG32: ; Negates DEHL (Two's complement) pop namespace #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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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. diff --git a/tests/functional/arch/zxnext/mulf16a.asm b/tests/functional/arch/zxnext/mulf16a.asm index f98844c69..55ca31cba 100644 --- a/tests/functional/arch/zxnext/mulf16a.asm +++ b/tests/functional/arch/zxnext/mulf16a.asm @@ -46,12 +46,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 @@ -80,12 +80,12 @@ __NEG32: ; Negates DEHL (Two's complement) pop namespace #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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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. diff --git a/tests/functional/arch/zxnext/print_i16.asm b/tests/functional/arch/zxnext/print_i16.asm index 8d21a531e..570b3e2a3 100644 --- a/tests/functional/arch/zxnext/print_i16.asm +++ b/tests/functional/arch/zxnext/print_i16.asm @@ -972,12 +972,12 @@ __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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/print_i32.asm b/tests/functional/arch/zxnext/print_i32.asm index 9c2593eb1..24954cfe5 100644 --- a/tests/functional/arch/zxnext/print_i32.asm +++ b/tests/functional/arch/zxnext/print_i32.asm @@ -999,12 +999,12 @@ __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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/print_i8.asm b/tests/functional/arch/zxnext/print_i8.asm index ba879845d..9a3d90cb1 100644 --- a/tests/functional/arch/zxnext/print_i8.asm +++ b/tests/functional/arch/zxnext/print_i8.asm @@ -972,12 +972,12 @@ __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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/print_u16.asm b/tests/functional/arch/zxnext/print_u16.asm index 785cfec93..99b32db29 100644 --- a/tests/functional/arch/zxnext/print_u16.asm +++ b/tests/functional/arch/zxnext/print_u16.asm @@ -973,12 +973,12 @@ __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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/print_u32.asm b/tests/functional/arch/zxnext/print_u32.asm index ea8865d0e..29f8dee6c 100644 --- a/tests/functional/arch/zxnext/print_u32.asm +++ b/tests/functional/arch/zxnext/print_u32.asm @@ -1000,12 +1000,12 @@ __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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/print_u8.asm b/tests/functional/arch/zxnext/print_u8.asm index 483676c33..0a4d6325e 100644 --- a/tests/functional/arch/zxnext/print_u8.asm +++ b/tests/functional/arch/zxnext/print_u8.asm @@ -973,12 +973,12 @@ __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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/sgnf.asm b/tests/functional/arch/zxnext/sgnf.asm index 7bb8739a0..1915e4439 100644 --- a/tests/functional/arch/zxnext/sgnf.asm +++ b/tests/functional/arch/zxnext/sgnf.asm @@ -38,20 +38,20 @@ _y: 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/sgnf16.asm b/tests/functional/arch/zxnext/sgnf16.asm index 835e3e446..9d4c01650 100644 --- a/tests/functional/arch/zxnext/sgnf16.asm +++ b/tests/functional/arch/zxnext/sgnf16.asm @@ -36,20 +36,20 @@ _y: 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/sgni16.asm b/tests/functional/arch/zxnext/sgni16.asm index 40f0205f3..e6a9f8a93 100644 --- a/tests/functional/arch/zxnext/sgni16.asm +++ b/tests/functional/arch/zxnext/sgni16.asm @@ -33,20 +33,20 @@ _y: 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/sgni32.asm b/tests/functional/arch/zxnext/sgni32.asm index cd6786f83..691d66604 100644 --- a/tests/functional/arch/zxnext/sgni32.asm +++ b/tests/functional/arch/zxnext/sgni32.asm @@ -36,20 +36,20 @@ _y: 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/sgni8.asm b/tests/functional/arch/zxnext/sgni8.asm index 555b8e66f..fb22ca2a9 100644 --- a/tests/functional/arch/zxnext/sgni8.asm +++ b/tests/functional/arch/zxnext/sgni8.asm @@ -32,12 +32,12 @@ _y: 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/sgnu16.asm b/tests/functional/arch/zxnext/sgnu16.asm index 5f1e7d3b1..f8918e958 100644 --- a/tests/functional/arch/zxnext/sgnu16.asm +++ b/tests/functional/arch/zxnext/sgnu16.asm @@ -33,12 +33,12 @@ _y: 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/sgnu32.asm b/tests/functional/arch/zxnext/sgnu32.asm index f7a8c303c..e3c54660f 100644 --- a/tests/functional/arch/zxnext/sgnu32.asm +++ b/tests/functional/arch/zxnext/sgnu32.asm @@ -36,12 +36,12 @@ _y: 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/sgnu8.asm b/tests/functional/arch/zxnext/sgnu8.asm index a2326eb43..2b892a556 100644 --- a/tests/functional/arch/zxnext/sgnu8.asm +++ b/tests/functional/arch/zxnext/sgnu8.asm @@ -32,12 +32,12 @@ _y: 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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/stdlib_spectranet.asm b/tests/functional/arch/zxnext/stdlib_spectranet.asm index 51bce0c96..739c0e1e4 100644 --- a/tests/functional/arch/zxnext/stdlib_spectranet.asm +++ b/tests/functional/arch/zxnext/stdlib_spectranet.asm @@ -3383,12 +3383,12 @@ __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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/subf00.asm b/tests/functional/arch/zxnext/subf00.asm index bbd905474..f02386b45 100644 --- a/tests/functional/arch/zxnext/subf00.asm +++ b/tests/functional/arch/zxnext/subf00.asm @@ -47,12 +47,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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" ; ------------------------------------------------------------- diff --git a/tests/functional/arch/zxnext/subf01.asm b/tests/functional/arch/zxnext/subf01.asm index 59c24a6ea..fcbf3310d 100644 --- a/tests/functional/arch/zxnext/subf01.asm +++ b/tests/functional/arch/zxnext/subf01.asm @@ -51,12 +51,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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" ; ------------------------------------------------------------- diff --git a/tests/functional/arch/zxnext/subf16c.asm b/tests/functional/arch/zxnext/subf16c.asm index aff96fea2..e553a3e4a 100644 --- a/tests/functional/arch/zxnext/subf16c.asm +++ b/tests/functional/arch/zxnext/subf16c.asm @@ -67,12 +67,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 diff --git a/tests/functional/arch/zxnext/subi32c.asm b/tests/functional/arch/zxnext/subi32c.asm index 06e1725c2..a2f710ad6 100644 --- a/tests/functional/arch/zxnext/subi32c.asm +++ b/tests/functional/arch/zxnext/subi32c.asm @@ -76,12 +76,12 @@ _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) - ' ---------------------------------------------------------------- + ; ---------------------------------------------------------------- + ; 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 From d6523ec28436e92387bada22b6375e18416ccacf Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 2 Feb 2026 00:44:45 +0100 Subject: [PATCH 066/169] docs: update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec6425ac3..588256255 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +[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 From af873b8cad2d0860f956a32b9368db14248fee58 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 2 Feb 2026 00:46:39 +0100 Subject: [PATCH 067/169] =?UTF-8?q?Bump=20version:=201.18.5=20=E2=86=92=20?= =?UTF-8?q?1.18.6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- docs/archive.md | 12 ++++++------ pyproject.toml | 2 +- src/zxbasm/version.py | 2 +- src/zxbc/version.py | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 4970d70a3..942d2c866 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.18.5 +current_version = 1.18.6 [bumpversion:file:src/zxbc/version.py] search = VERSION: Final[str] = "{current_version}" diff --git a/docs/archive.md b/docs/archive.md index b04067bf1..94ecbd05b 100644 --- a/docs/archive.md +++ b/docs/archive.md @@ -10,28 +10,28 @@ repository (git). You can contribute to ZX BASIC by reporting possible bugs or improvement suggestions at the [forum](https://forum.boriel.com/) or in social media. -The latest stable version is **1.18.5**. +The latest stable version is **1.18.6**. Click on the desired icon below to download the package suitable for your platform: * [win32zip - https://www.boriel.com/files/zxb/zxbasic-1.18.5-win32.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.5-win32.zip) + https://www.boriel.com/files/zxb/zxbasic-1.18.6-win32.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.6-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.5-macos.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.5-macos.tar.gz) + https://www.boriel.com/files/zxb/zxbasic-1.18.6-macos.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.6-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.5-linux64.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.5-linux64.tar.gz) + https://www.boriel.com/files/zxb/zxbasic-1.18.6-linux64.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.6-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.5.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.5.zip) + https://www.boriel.com/files/zxb/zxbasic-1.18.6.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.6.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.5.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.5.tar.gz) + https://www.boriel.com/files/zxb/zxbasic-1.18.6.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.6.tar.gz)
Windows, Linux, Mac tar.gz package, with python scripts. Requires python installed in your system. ### What's new diff --git a/pyproject.toml b/pyproject.toml index b4cbd47c9..45e6df83d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "zxbasic" -version = "1.18.5" +version = "1.18.6" description = "Boriel's ZX BASIC Compiler" authors = ["Jose Rodriguez "] license = "AGPL-3.0-or-later" diff --git a/src/zxbasm/version.py b/src/zxbasm/version.py index 419faa241..649e2f1b6 100644 --- a/src/zxbasm/version.py +++ b/src/zxbasm/version.py @@ -5,4 +5,4 @@ # See https://www.gnu.org/licenses/agpl-3.0.html for details. # -------------------------------------------------------------------- -VERSION = "1.18.5" +VERSION = "1.18.6" diff --git a/src/zxbc/version.py b/src/zxbc/version.py index c1a8bf10a..160f9a692 100755 --- a/src/zxbc/version.py +++ b/src/zxbc/version.py @@ -7,4 +7,4 @@ from typing import Final -VERSION: Final[str] = "1.18.5" +VERSION: Final[str] = "1.18.6" From 4a82c6281fe73f17da29d8a6d0a73f210a7529a1 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 8 Feb 2026 13:38:46 +0100 Subject: [PATCH 068/169] fix: cast f16 to long or ulong was broken --- src/arch/z80/backend/common.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/arch/z80/backend/common.py b/src/arch/z80/backend/common.py index 436413833..6f25dc8d2 100644 --- a/src/arch/z80/backend/common.py +++ b/src/arch/z80/backend/common.py @@ -385,19 +385,24 @@ 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"]) elif stype in (U32_t, I32_t): return [] From 4f362f4dba58a43d74088c736ffb87d5e437b9bc Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 8 Feb 2026 13:48:01 +0100 Subject: [PATCH 069/169] Add tests --- .../arch/zx48k/cast_f16_to_long.asm | 49 +++++++++++++++++++ .../arch/zx48k/cast_f16_to_long.bas | 5 ++ .../arch/zx48k/cast_f16_to_ulong.asm | 49 +++++++++++++++++++ .../arch/zx48k/cast_f16_to_ulong.bas | 5 ++ .../arch/zxnext/cast_f16_to_long.asm | 42 ++++++++++++++++ .../arch/zxnext/cast_f16_to_long.bas | 5 ++ .../arch/zxnext/cast_f16_to_ulong.asm | 42 ++++++++++++++++ .../arch/zxnext/cast_f16_to_ulong.bas | 5 ++ 8 files changed, 202 insertions(+) create mode 100644 tests/functional/arch/zx48k/cast_f16_to_long.asm create mode 100644 tests/functional/arch/zx48k/cast_f16_to_long.bas create mode 100644 tests/functional/arch/zx48k/cast_f16_to_ulong.asm create mode 100644 tests/functional/arch/zx48k/cast_f16_to_ulong.bas create mode 100644 tests/functional/arch/zxnext/cast_f16_to_long.asm create mode 100644 tests/functional/arch/zxnext/cast_f16_to_long.bas create mode 100644 tests/functional/arch/zxnext/cast_f16_to_ulong.asm create mode 100644 tests/functional/arch/zxnext/cast_f16_to_ulong.bas 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..547845bad --- /dev/null +++ b/tests/functional/arch/zx48k/cast_f16_to_long.asm @@ -0,0 +1,49 @@ + 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 (_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..547845bad --- /dev/null +++ b/tests/functional/arch/zx48k/cast_f16_to_ulong.asm @@ -0,0 +1,49 @@ + 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 (_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/zxnext/cast_f16_to_long.asm b/tests/functional/arch/zxnext/cast_f16_to_long.asm new file mode 100644 index 000000000..53c65b0ce --- /dev/null +++ b/tests/functional/arch/zxnext/cast_f16_to_long.asm @@ -0,0 +1,42 @@ + 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 (_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..53c65b0ce --- /dev/null +++ b/tests/functional/arch/zxnext/cast_f16_to_ulong.asm @@ -0,0 +1,42 @@ + 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 (_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 + From 2d91fa83fe9c6d7df5faeef698af11e8abb4c58b Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 9 Feb 2026 18:24:31 +0100 Subject: [PATCH 070/169] fix: use the same rounding scheme for constants ZX Spectrum ROM always rounds "towards -Infinity", so: * INT(1.9) == 1 * INT(-1.1) == -2 We do the same for Constant values so the results are consistent. --- src/symbols/typecast.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/symbols/typecast.py b/src/symbols/typecast.py index 664e801fb..31a2d30d2 100644 --- a/src/symbols/typecast.py +++ b/src/symbols/typecast.py @@ -4,6 +4,7 @@ # 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 @@ -91,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) From 82a0f79ae54067fd1bde0e17848b68f85f7943da Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 9 Feb 2026 18:45:02 +0100 Subject: [PATCH 071/169] feat: add test --- .../arch/zx48k/cast_const_to_int.asm | 212 ++++++++++++++++++ .../arch/zx48k/cast_const_to_int.bas | 25 +++ .../arch/zxnext/cast_const_to_int.asm | 205 +++++++++++++++++ .../arch/zxnext/cast_const_to_int.bas | 25 +++ 4 files changed, 467 insertions(+) create mode 100644 tests/functional/arch/zx48k/cast_const_to_int.asm create mode 100644 tests/functional/arch/zx48k/cast_const_to_int.bas create mode 100644 tests/functional/arch/zxnext/cast_const_to_int.asm create mode 100644 tests/functional/arch/zxnext/cast_const_to_int.bas 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..3da7fb210 --- /dev/null +++ b/tests/functional/arch/zx48k/cast_const_to_int.asm @@ -0,0 +1,212 @@ + 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 (_r_long), hl + ld (_r_long + 2), de + ld hl, (_var_neg) + ld de, (_var_neg + 2) + ex de, hl + ld de, 0 + 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 (_r_long), hl + ld (_r_long + 2), de + ld hl, (_var) + ld de, (_var + 2) + ex de, hl + ld de, 0 + 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 72 "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/zxnext/cast_const_to_int.asm b/tests/functional/arch/zxnext/cast_const_to_int.asm new file mode 100644 index 000000000..1120d559f --- /dev/null +++ b/tests/functional/arch/zxnext/cast_const_to_int.asm @@ -0,0 +1,205 @@ + 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 (_r_long), hl + ld (_r_long + 2), de + ld hl, (_var_neg) + ld de, (_var_neg + 2) + ex de, hl + ld de, 0 + 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 (_r_long), hl + ld (_r_long + 2), de + ld hl, (_var) + ld de, (_var + 2) + ex de, hl + ld de, 0 + 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 68 "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) From 223fede1be11fe3edb7edff397a2adac04457663 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 9 Feb 2026 19:00:52 +0100 Subject: [PATCH 072/169] refact: allow zxblex to be used from the cmdline --- src/zxbc/zxblex.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/zxbc/zxblex.py b/src/zxbc/zxblex.py index ffd54f741..9d739029f 100755 --- a/src/zxbc/zxblex.py +++ b/src/zxbc/zxblex.py @@ -14,8 +14,7 @@ from src.api import global_ 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 From 14662f53f8acf7eadd30baae444d1d9068bdf753 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 9 Feb 2026 19:20:11 +0100 Subject: [PATCH 073/169] fix: propagate SIGN in fixed to Long conversions --- src/arch/z80/backend/common.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/arch/z80/backend/common.py b/src/arch/z80/backend/common.py index 6f25dc8d2..c6eb7a969 100644 --- a/src/arch/z80/backend/common.py +++ b/src/arch/z80/backend/common.py @@ -402,7 +402,18 @@ def to_long(stype: DataType) -> list[str]: ) elif stype == F16_t: - output.extend(["ex de, hl", "ld de, 0"]) + 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 [] From f8b2e8fe7eb9807b1f69bcec1de023a41cda6f14 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 9 Feb 2026 19:22:13 +0100 Subject: [PATCH 074/169] tests: update tests --- .../arch/zx48k/cast_const_to_int.asm | 22 ++++++++++++++++++- .../arch/zx48k/cast_f16_to_long.asm | 5 +++++ .../arch/zx48k/cast_f16_to_ulong.asm | 5 +++++ .../arch/zxnext/cast_const_to_int.asm | 22 ++++++++++++++++++- .../arch/zxnext/cast_f16_to_long.asm | 5 +++++ .../arch/zxnext/cast_f16_to_ulong.asm | 5 +++++ 6 files changed, 62 insertions(+), 2 deletions(-) diff --git a/tests/functional/arch/zx48k/cast_const_to_int.asm b/tests/functional/arch/zx48k/cast_const_to_int.asm index 3da7fb210..2ccdf1e0a 100644 --- a/tests/functional/arch/zx48k/cast_const_to_int.asm +++ b/tests/functional/arch/zx48k/cast_const_to_int.asm @@ -50,12 +50,22 @@ _r_ulong: 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) @@ -68,12 +78,22 @@ _r_ulong: 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) @@ -208,5 +228,5 @@ __FTOU8: ; Converts float in C ED LH to Unsigned byte in A ld a, l ret pop namespace -#line 72 "arch/zx48k/cast_const_to_int.bas" +#line 92 "arch/zx48k/cast_const_to_int.bas" END diff --git a/tests/functional/arch/zx48k/cast_f16_to_long.asm b/tests/functional/arch/zx48k/cast_f16_to_long.asm index 547845bad..c83500765 100644 --- a/tests/functional/arch/zx48k/cast_f16_to_long.asm +++ b/tests/functional/arch/zx48k/cast_f16_to_long.asm @@ -29,6 +29,11 @@ _v2: 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 diff --git a/tests/functional/arch/zx48k/cast_f16_to_ulong.asm b/tests/functional/arch/zx48k/cast_f16_to_ulong.asm index 547845bad..c83500765 100644 --- a/tests/functional/arch/zx48k/cast_f16_to_ulong.asm +++ b/tests/functional/arch/zx48k/cast_f16_to_ulong.asm @@ -29,6 +29,11 @@ _v2: 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 diff --git a/tests/functional/arch/zxnext/cast_const_to_int.asm b/tests/functional/arch/zxnext/cast_const_to_int.asm index 1120d559f..035405911 100644 --- a/tests/functional/arch/zxnext/cast_const_to_int.asm +++ b/tests/functional/arch/zxnext/cast_const_to_int.asm @@ -47,12 +47,22 @@ _r_ulong: 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) @@ -65,12 +75,22 @@ _r_ulong: 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) @@ -201,5 +221,5 @@ __FTOU8: ; Converts float in C ED LH to Unsigned byte in A ld a, l ret pop namespace -#line 68 "arch/zxnext/cast_const_to_int.bas" +#line 88 "arch/zxnext/cast_const_to_int.bas" END diff --git a/tests/functional/arch/zxnext/cast_f16_to_long.asm b/tests/functional/arch/zxnext/cast_f16_to_long.asm index 53c65b0ce..6c0c77264 100644 --- a/tests/functional/arch/zxnext/cast_f16_to_long.asm +++ b/tests/functional/arch/zxnext/cast_f16_to_long.asm @@ -26,6 +26,11 @@ _v2: 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 diff --git a/tests/functional/arch/zxnext/cast_f16_to_ulong.asm b/tests/functional/arch/zxnext/cast_f16_to_ulong.asm index 53c65b0ce..6c0c77264 100644 --- a/tests/functional/arch/zxnext/cast_f16_to_ulong.asm +++ b/tests/functional/arch/zxnext/cast_f16_to_ulong.asm @@ -26,6 +26,11 @@ _v2: 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 From 0ad1a12e602a973c829a16fc9b80a14fbee5a699 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 9 Feb 2026 19:55:17 +0100 Subject: [PATCH 075/169] docs: Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 588256255..04320bd24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +[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 From 3ff7c83518204b6af500fc973a848737b2e99ec7 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 9 Feb 2026 19:55:54 +0100 Subject: [PATCH 076/169] =?UTF-8?q?Bump=20version:=201.18.6=20=E2=86=92=20?= =?UTF-8?q?1.18.7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- docs/archive.md | 12 ++++++------ pyproject.toml | 2 +- src/zxbasm/version.py | 2 +- src/zxbc/version.py | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 942d2c866..9da15245e 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.18.6 +current_version = 1.18.7 [bumpversion:file:src/zxbc/version.py] search = VERSION: Final[str] = "{current_version}" diff --git a/docs/archive.md b/docs/archive.md index 94ecbd05b..c57312e94 100644 --- a/docs/archive.md +++ b/docs/archive.md @@ -10,28 +10,28 @@ repository (git). You can contribute to ZX BASIC by reporting possible bugs or improvement suggestions at the [forum](https://forum.boriel.com/) or in social media. -The latest stable version is **1.18.6**. +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.6-win32.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.6-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.6-macos.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.6-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.6-linux64.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.6-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.6.zip](https://www.boriel.com/files/zxb/zxbasic-1.18.6.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.6.tar.gz](https://www.boriel.com/files/zxb/zxbasic-1.18.6.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/pyproject.toml b/pyproject.toml index 45e6df83d..acb9486c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "zxbasic" -version = "1.18.6" +version = "1.18.7" description = "Boriel's ZX BASIC Compiler" authors = ["Jose Rodriguez "] license = "AGPL-3.0-or-later" diff --git a/src/zxbasm/version.py b/src/zxbasm/version.py index 649e2f1b6..3323e8fdd 100644 --- a/src/zxbasm/version.py +++ b/src/zxbasm/version.py @@ -5,4 +5,4 @@ # See https://www.gnu.org/licenses/agpl-3.0.html for details. # -------------------------------------------------------------------- -VERSION = "1.18.6" +VERSION = "1.18.7" diff --git a/src/zxbc/version.py b/src/zxbc/version.py index 160f9a692..8ef2f3536 100755 --- a/src/zxbc/version.py +++ b/src/zxbc/version.py @@ -7,4 +7,4 @@ from typing import Final -VERSION: Final[str] = "1.18.6" +VERSION: Final[str] = "1.18.7" From 81e1e33a2822a2100c327f8714406218f34d09df Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 9 Feb 2026 22:16:22 +0100 Subject: [PATCH 077/169] feat: make zxbasm use new format file --- src/zxbasm/zxbasm.py | 43 ++++++++++++++++++++++++++++++++++++------- src/zxbpp/zxbpp.py | 18 ++++++++++++------ 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/zxbasm/zxbasm.py b/src/zxbasm/zxbasm.py index 66ecd28f0..c57396a1b 100755 --- a/src/zxbasm/zxbasm.py +++ b/src/zxbasm/zxbasm.py @@ -10,13 +10,24 @@ 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): @@ -50,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", @@ -59,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", @@ -68,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", @@ -114,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 @@ -127,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 = ( @@ -149,7 +178,7 @@ def main(args=None): 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/zxbpp/zxbpp.py b/src/zxbpp/zxbpp.py index a2bb00ce9..cbf58cbac 100755 --- a/src/zxbpp/zxbpp.py +++ b/src/zxbpp/zxbpp.py @@ -15,9 +15,7 @@ from enum import Enum, unique from typing import Any, Final, NamedTuple -from src import arch from src.api import config, global_, utils -from src.arch import AVAILABLE_ARCHITECTURES from src.ply import yacc from src.zxbpp import zxbasmpplex, zxbpplex from src.zxbpp.base_pplex import STDIN @@ -35,6 +33,9 @@ class PreprocMode(str, Enum): ASM = "ASM" +# List of available architectures +AVAILABLE_ARCHITECTURES: Final[list[str]] = [] + # Generated output OUTPUT = "" @@ -124,6 +125,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 = "" @@ -1014,9 +1020,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", @@ -1030,7 +1036,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 From f15703532c04b74c6217f5ceefa6bba12205030a Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Tue, 10 Feb 2026 10:59:17 +0100 Subject: [PATCH 078/169] build: update Python depencency annotation --- pyproject.toml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index acb9486c9..0f4eb6144 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,10 @@ classifiers = [ 'Intended Audience :: Developers', 'Topic :: Software Development :: Build Tools', 'License :: OSI Approved :: GNU Affero General Public License v3', - 'Programming Language :: Python :: 3.11', + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ] keywords = ['compiler', 'zxspectrum', 'BASIC', 'z80'] # arbitrary keywords @@ -29,7 +32,7 @@ zxbasm = 'src.zxbasm:main' zxbpp = 'src.zxbpp:entry_point' [tool.poetry.dependencies] -python = "^3.11" +python = ">=3.11" [tool.poetry.group.dev.dependencies] pytest = "*" From bb9a6b503a749d570d792435ada8fffb2af6f379 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Wed, 11 Feb 2026 16:53:46 +0100 Subject: [PATCH 079/169] refact: move Exception to its own class --- src/ast/ast.py | 20 +++++++++++++------- src/ast/exceptions.py | 18 ++++++++++++++++++ src/ast/tree.py | 17 +---------------- src/zxbasm/expr.py | 2 +- 4 files changed, 33 insertions(+), 24 deletions(-) create mode 100644 src/ast/exceptions.py diff --git a/src/ast/ast.py b/src/ast/ast.py index 2fb960f46..566b5074a 100644 --- a/src/ast/ast.py +++ b/src/ast/ast.py @@ -7,10 +7,12 @@ import types from collections.abc import Callable -from typing import Any +from typing import Any, Final from .tree import Tree +__all__: Final[tuple[str, ...]] = "Ast", "NodeVisitor" + # ---------------------------------------------------------------------- # Abstract Syntax Tree class @@ -34,12 +36,13 @@ def visit(self, node): while stack: try: - last = stack[-1] - if isinstance(last, types.GeneratorType): - stack.append(last.send(last_result)) + stack_top = stack[-1] + if isinstance(stack_top, types.GeneratorType): + stack.append(stack_top.send(last_result)) last_result = None - elif isinstance(last, self.node_type): - stack.append(self._visit(stack.pop())) + elif isinstance(stack_top, self.node_type): + stack.pop() + stack.append(self._visit(stack_top)) else: last_result = stack.pop() except StopIteration: @@ -55,7 +58,10 @@ 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 + 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. 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 index 389f9b648..d65e384e3 100644 --- a/src/ast/tree.py +++ b/src/ast/tree.py @@ -11,22 +11,7 @@ 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/zxbasm/expr.py b/src/zxbasm/expr.py index b0a779f8e..4c9444acc 100644 --- a/src/zxbasm/expr.py +++ b/src/zxbasm/expr.py @@ -7,7 +7,7 @@ from src.api.errmsg import error from src.ast import Ast -from src.ast.tree import NotAnAstError +from src.ast.exceptions import NotAnAstError from src.zxbasm.label import Label From 44e72aa8daf93c50ff82180769fe12fca639c554 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Wed, 11 Feb 2026 19:42:21 +0100 Subject: [PATCH 080/169] refact: Use a GenericVisitor class This class is then subclassed by the others. --- src/api/optimize.py | 97 ++++----- src/arch/z80/visitor/builtin_translator.py | 11 +- src/arch/z80/visitor/function_translator.py | 4 +- src/arch/z80/visitor/translator.py | 204 +++++++++--------- .../z80/visitor/translator_inst_visitor.py | 1 + src/arch/z80/visitor/translator_visitor.py | 10 +- src/arch/z80/visitor/unary_op_translator.py | 16 +- src/ast/__init__.py | 3 +- src/ast/ast.py | 63 ++---- src/ast/visitor.py | 42 ++++ src/symbols/arrayload.py | 1 - src/symbols/symbol_.py | 10 +- 12 files changed, 234 insertions(+), 228 deletions(-) create mode 100644 src/ast/visitor.py diff --git a/src/api/optimize.py b/src/api/optimize.py index ff744fa7d..41bc2442f 100644 --- a/src/api/optimize.py +++ b/src/api/optimize.py @@ -6,7 +6,7 @@ # -------------------------------------------------------------------- import symtable -from collections.abc import Generator +from collections.abc import Callable, Generator from typing import Any, NamedTuple import src.api.check as chk @@ -17,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.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 @@ -58,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, None, None]: + """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).""" @@ -107,7 +102,7 @@ def visit_FUNCTION(self, node: symbols.ID): if type_ is not None and type_ == self.TYPE(TYPE.string): 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 @@ -155,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): @@ -165,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): @@ -314,7 +310,7 @@ 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): @@ -370,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_): @@ -405,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): @@ -419,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): @@ -433,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 @@ -446,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 ): @@ -502,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: @@ -532,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/arch/z80/visitor/builtin_translator.py b/src/arch/z80/visitor/builtin_translator.py index ed8da017b..85e47f0d9 100644 --- a/src/arch/z80/visitor/builtin_translator.py +++ b/src/arch/z80/visitor/builtin_translator.py @@ -19,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) @@ -125,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_: @@ -141,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 143a77d4e..fc790f522 100644 --- a/src/arch/z80/visitor/function_translator.py +++ b/src/arch/z80/visitor/function_translator.py @@ -24,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) @@ -115,7 +115,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 e027a57f8..41841be81 100644 --- a/src/arch/z80/visitor/translator.py +++ b/src/arch/z80/visitor/translator.py @@ -55,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) @@ -83,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) @@ -99,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) @@ -107,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__( @@ -117,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 @@ -127,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 @@ -138,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) @@ -147,8 +150,8 @@ 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) @@ -159,8 +162,8 @@ def visit_BUILTIN(self, node): 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]] = { @@ -187,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()) @@ -209,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: @@ -221,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 @@ -260,13 +257,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) @@ -334,8 +331,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) @@ -347,14 +344,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) @@ -366,7 +363,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) @@ -376,10 +373,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] @@ -405,8 +402,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) @@ -415,9 +412,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] @@ -426,7 +423,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!?? @@ -446,18 +443,18 @@ def visit_LETARRAYSUBSTR(self, node): self.runtime_call(RuntimeLabel.LETSUBSTR, 0) def visit_ARRAYACCESS(self, node): - yield node.arglist + yield self.visit(node.arglist) 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": @@ -468,7 +465,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 @@ -509,7 +506,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) @@ -539,7 +536,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) @@ -561,10 +558,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() @@ -598,20 +595,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) @@ -624,13 +621,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) @@ -640,8 +637,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) @@ -657,7 +654,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:])) @@ -665,7 +662,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:])) @@ -678,7 +675,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() @@ -687,18 +684,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) @@ -717,10 +714,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() @@ -732,11 +729,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) @@ -754,10 +751,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 @@ -765,10 +762,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 @@ -776,12 +773,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 @@ -789,12 +786,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 @@ -806,25 +803,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) @@ -846,14 +836,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) @@ -861,22 +851,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) @@ -884,7 +874,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) @@ -895,14 +885,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) @@ -913,7 +903,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 = { diff --git a/src/arch/z80/visitor/translator_inst_visitor.py b/src/arch/z80/visitor/translator_inst_visitor.py index cc218d4c9..306c66512 100644 --- a/src/arch/z80/visitor/translator_inst_visitor.py +++ b/src/arch/z80/visitor/translator_inst_visitor.py @@ -16,6 +16,7 @@ class TranslatorInstVisitor(NodeVisitor): def __init__(self, backend: Backend): + super().__init__() self.backend = backend def emit(self, *args: str) -> None: diff --git a/src/arch/z80/visitor/translator_visitor.py b/src/arch/z80/visitor/translator_visitor.py index 7debfdd27..331ec4aa9 100644 --- a/src/arch/z80/visitor/translator_visitor.py +++ b/src/arch/z80/visitor/translator_visitor.py @@ -97,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 = { diff --git a/src/arch/z80/visitor/unary_op_translator.py b/src/arch/z80/visitor/unary_op_translator.py index e3486b7b1..f81499f76 100644 --- a/src/arch/z80/visitor/unary_op_translator.py +++ b/src/arch/z80/visitor/unary_op_translator.py @@ -6,28 +6,36 @@ # -------------------------------------------------------------------- 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/ast/__init__.py b/src/ast/__init__.py index 9edeeea6b..221568150 100644 --- a/src/ast/__init__.py +++ b/src/ast/__init__.py @@ -5,12 +5,11 @@ # See https://www.gnu.org/licenses/agpl-3.0.html for details. # -------------------------------------------------------------------- -from .ast import Ast, NodeVisitor, types +from .ast import Ast, NodeVisitor from .tree import Tree __all__ = ( "Ast", "NodeVisitor", "Tree", - "types", ) diff --git a/src/ast/ast.py b/src/ast/ast.py index 566b5074a..4c1094afd 100644 --- a/src/ast/ast.py +++ b/src/ast/ast.py @@ -4,12 +4,11 @@ # See the file CONTRIBUTORS.md for copyright details. # See https://www.gnu.org/licenses/agpl-3.0.html for details. # -------------------------------------------------------------------- - -import types -from collections.abc import Callable +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" @@ -27,53 +26,17 @@ def token(self): return self.__class__ -class NodeVisitor: - node_type: type = Ast - - def visit(self, node): - stack = [node] - last_result = None - - while stack: - try: - stack_top = stack[-1] - if isinstance(stack_top, types.GeneratorType): - stack.append(stack_top.send(last_result)) - last_result = None - elif isinstance(stack_top, self.node_type): - stack.pop() - stack.append(self._visit(stack_top)) - 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) +class NodeVisitor(GenericNodeVisitor[Ast]): + def _visit(self, node: Ast): + meth: Callable[[Ast], Generator[Ast | Any, Any, None]] = 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 generic_visit(self, node: Ast) -> Generator[Ast | Any, Any, None]: + for i, child in enumerate(node.children): + node.children[i] = yield self.visit(child) - 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]) + yield node diff --git a/src/ast/visitor.py b/src/ast/visitor.py new file mode 100644 index 000000000..106adbf56 --- /dev/null +++ b/src/ast/visitor.py @@ -0,0 +1,42 @@ +__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, Generic, NamedTuple, TypeVar + +__all__: Final[tuple[str, ...]] = ("GenericNodeVisitor",) + +_T = TypeVar("_T") + + +class ToVisit(NamedTuple, Generic[_T]): + obj: _T + + +class GenericNodeVisitor(Generic[_T]): + def visit(self, node: _T | None) -> _T | Generator[_T | None, None, 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, None, None]: ... diff --git a/src/symbols/arrayload.py b/src/symbols/arrayload.py index cbdb6ee55..222413fcc 100644 --- a/src/symbols/arrayload.py +++ b/src/symbols/arrayload.py @@ -11,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/symbol_.py b/src/symbols/symbol_.py index c1932c00b..f495b4977 100644 --- a/src/symbols/symbol_.py +++ b/src/symbols/symbol_.py @@ -79,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 @@ -90,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 From 63f87e46946da2017668d56e79ccd1f1593912f9 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 13 Feb 2026 12:03:59 +0100 Subject: [PATCH 081/169] Add extra tests --- tests/functional/arch/zx48k/print_at2.asm | 962 ++++++++++++++++++++ tests/functional/arch/zx48k/print_at2.bas | 3 + tests/functional/arch/zx48k/print_tab2.asm | 957 +++++++++++++++++++ tests/functional/arch/zx48k/print_tab2.bas | 4 + tests/functional/arch/zxnext/print_at2.asm | 955 +++++++++++++++++++ tests/functional/arch/zxnext/print_at2.bas | 3 + tests/functional/arch/zxnext/print_tab2.asm | 950 +++++++++++++++++++ tests/functional/arch/zxnext/print_tab2.bas | 4 + 8 files changed, 3838 insertions(+) create mode 100644 tests/functional/arch/zx48k/print_at2.asm create mode 100644 tests/functional/arch/zx48k/print_at2.bas create mode 100644 tests/functional/arch/zx48k/print_tab2.asm create mode 100644 tests/functional/arch/zx48k/print_tab2.bas create mode 100644 tests/functional/arch/zxnext/print_at2.asm create mode 100644 tests/functional/arch/zxnext/print_at2.bas create mode 100644 tests/functional/arch/zxnext/print_tab2.asm create mode 100644 tests/functional/arch/zxnext/print_tab2.bas 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/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_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; + From ff6d739121636933194f4303a17bdace810a2712 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 1 Feb 2026 15:55:35 +0100 Subject: [PATCH 082/169] refact: use #include [arch:zx48k] for duplicated zxnext lib files --- src/lib/arch/zxnext/runtime/SP/CharLeft.asm | 63 +- src/lib/arch/zxnext/runtime/SP/CharRight.asm | 58 +- .../arch/zxnext/runtime/SP/GetScrnAddr.asm | 66 +- src/lib/arch/zxnext/runtime/SP/PixelDown.asm | 61 +- src/lib/arch/zxnext/runtime/SP/PixelLeft.asm | 56 +- src/lib/arch/zxnext/runtime/SP/PixelRight.asm | 57 +- src/lib/arch/zxnext/runtime/SP/PixelUp.asm | 60 +- src/lib/arch/zxnext/runtime/abs16.asm | 11 +- src/lib/arch/zxnext/runtime/abs32.asm | 11 +- src/lib/arch/zxnext/runtime/abs8.asm | 16 +- src/lib/arch/zxnext/runtime/absf.asm | 14 +- .../arch/zxnext/runtime/array/arrayalloc.asm | 147 +---- .../arch/zxnext/runtime/array/arraybound.asm | 95 +-- .../zxnext/runtime/array/arraystrfree.asm | 71 +-- .../arch/zxnext/runtime/array/strarraycpy.asm | 76 +-- src/lib/arch/zxnext/runtime/asc.asm | 47 +- src/lib/arch/zxnext/runtime/attr.asm | 73 +-- .../arch/zxnext/runtime/bitwise/band16.asm | 30 +- .../arch/zxnext/runtime/bitwise/band32.asm | 47 +- .../arch/zxnext/runtime/bitwise/bnot16.asm | 30 +- .../arch/zxnext/runtime/bitwise/bnot32.asm | 38 +- src/lib/arch/zxnext/runtime/bitwise/bor16.asm | 30 +- src/lib/arch/zxnext/runtime/bitwise/bor32.asm | 47 +- .../arch/zxnext/runtime/bitwise/bxor16.asm | 30 +- .../arch/zxnext/runtime/bitwise/bxor32.asm | 47 +- src/lib/arch/zxnext/runtime/bitwise/shl32.asm | 20 +- .../arch/zxnext/runtime/bitwise/shra32.asm | 20 +- .../arch/zxnext/runtime/bitwise/shrl32.asm | 20 +- src/lib/arch/zxnext/runtime/bold.asm | 42 +- src/lib/arch/zxnext/runtime/bool/and16.asm | 25 +- src/lib/arch/zxnext/runtime/bool/and32.asm | 39 +- src/lib/arch/zxnext/runtime/bool/andf.asm | 32 +- src/lib/arch/zxnext/runtime/bool/not32.asm | 22 +- src/lib/arch/zxnext/runtime/bool/notf.asm | 32 +- src/lib/arch/zxnext/runtime/bool/or32.asm | 36 +- src/lib/arch/zxnext/runtime/bool/orf.asm | 32 +- src/lib/arch/zxnext/runtime/bool/xor16.asm | 9 +- src/lib/arch/zxnext/runtime/bool/xor32.asm | 35 +- src/lib/arch/zxnext/runtime/bool/xor8.asm | 36 +- src/lib/arch/zxnext/runtime/bool/xorf.asm | 42 +- src/lib/arch/zxnext/runtime/border.asm | 19 +- src/lib/arch/zxnext/runtime/break.asm | 43 +- src/lib/arch/zxnext/runtime/bright.asm | 54 +- src/lib/arch/zxnext/runtime/chr.asm | 82 +-- src/lib/arch/zxnext/runtime/circle.asm | 235 +------ src/lib/arch/zxnext/runtime/cls.asm | 46 +- src/lib/arch/zxnext/runtime/cmp/eq16.asm | 17 +- src/lib/arch/zxnext/runtime/cmp/eq32.asm | 37 +- src/lib/arch/zxnext/runtime/cmp/eqf.asm | 34 +- src/lib/arch/zxnext/runtime/cmp/gef.asm | 34 +- src/lib/arch/zxnext/runtime/cmp/gtf.asm | 33 +- src/lib/arch/zxnext/runtime/cmp/lef.asm | 33 +- src/lib/arch/zxnext/runtime/cmp/lei16.asm | 26 +- src/lib/arch/zxnext/runtime/cmp/lei32.asm | 46 +- src/lib/arch/zxnext/runtime/cmp/lei8.asm | 33 +- src/lib/arch/zxnext/runtime/cmp/ltf.asm | 33 +- src/lib/arch/zxnext/runtime/cmp/lti16.asm | 27 +- src/lib/arch/zxnext/runtime/cmp/lti32.asm | 37 +- src/lib/arch/zxnext/runtime/cmp/lti8.asm | 9 +- src/lib/arch/zxnext/runtime/cmp/nef.asm | 33 +- src/lib/arch/zxnext/runtime/copy_attr.asm | 88 +-- src/lib/arch/zxnext/runtime/draw.asm | 344 +--------- src/lib/arch/zxnext/runtime/draw3.asm | 515 +-------------- src/lib/arch/zxnext/runtime/error.asm | 52 +- src/lib/arch/zxnext/runtime/f16tofreg.asm | 60 +- src/lib/arch/zxnext/runtime/flash.asm | 55 +- src/lib/arch/zxnext/runtime/ftof16reg.asm | 54 +- src/lib/arch/zxnext/runtime/ftou32reg.asm | 102 +-- src/lib/arch/zxnext/runtime/iload32.asm | 29 +- src/lib/arch/zxnext/runtime/iloadf.asm | 40 +- src/lib/arch/zxnext/runtime/in_screen.asm | 38 +- src/lib/arch/zxnext/runtime/ink.asm | 55 +- src/lib/arch/zxnext/runtime/inverse.asm | 43 +- .../arch/zxnext/runtime/io/keyboard/inkey.asm | 71 +-- src/lib/arch/zxnext/runtime/io/sound/beep.asm | 32 +- .../arch/zxnext/runtime/io/sound/beeper.asm | 28 +- src/lib/arch/zxnext/runtime/istore16.asm | 28 +- src/lib/arch/zxnext/runtime/italic.asm | 44 +- src/lib/arch/zxnext/runtime/lddede.asm | 23 +- src/lib/arch/zxnext/runtime/letsubstr.asm | 165 +---- src/lib/arch/zxnext/runtime/load.asm | 343 +--------- src/lib/arch/zxnext/runtime/loadstr.asm | 55 +- src/lib/arch/zxnext/runtime/math/acos.asm | 22 +- src/lib/arch/zxnext/runtime/math/asin.asm | 22 +- src/lib/arch/zxnext/runtime/math/atan.asm | 22 +- src/lib/arch/zxnext/runtime/math/cos.asm | 22 +- src/lib/arch/zxnext/runtime/math/exp.asm | 22 +- src/lib/arch/zxnext/runtime/math/logn.asm | 22 +- src/lib/arch/zxnext/runtime/math/pow.asm | 38 +- src/lib/arch/zxnext/runtime/math/sin.asm | 22 +- src/lib/arch/zxnext/runtime/math/sqrt.asm | 22 +- src/lib/arch/zxnext/runtime/math/tan.asm | 22 +- src/lib/arch/zxnext/runtime/mem/calloc.asm | 53 +- src/lib/arch/zxnext/runtime/mem/free.asm | 197 +----- src/lib/arch/zxnext/runtime/mem/heapinit.asm | 133 +--- src/lib/arch/zxnext/runtime/mem/memcopy.asm | 49 +- src/lib/arch/zxnext/runtime/mem/realloc.asm | 164 +---- src/lib/arch/zxnext/runtime/neg16.asm | 27 +- src/lib/arch/zxnext/runtime/neg32.asm | 42 +- src/lib/arch/zxnext/runtime/negf.asm | 33 +- src/lib/arch/zxnext/runtime/ongoto.asm | 43 +- src/lib/arch/zxnext/runtime/over.asm | 57 +- src/lib/arch/zxnext/runtime/paper.asm | 58 +- src/lib/arch/zxnext/runtime/pause.asm | 16 +- src/lib/arch/zxnext/runtime/pistore32.asm | 9 +- src/lib/arch/zxnext/runtime/ploadf.asm | 25 +- src/lib/arch/zxnext/runtime/plot.asm | 100 +-- src/lib/arch/zxnext/runtime/print.asm | 591 +----------------- .../arch/zxnext/runtime/print_eol_attr.asm | 21 +- src/lib/arch/zxnext/runtime/printf.asm | 55 +- src/lib/arch/zxnext/runtime/printf16.asm | 69 +- src/lib/arch/zxnext/runtime/printi16.asm | 50 +- src/lib/arch/zxnext/runtime/printi32.asm | 58 +- src/lib/arch/zxnext/runtime/printi8.asm | 49 +- src/lib/arch/zxnext/runtime/printnum.asm | 44 +- src/lib/arch/zxnext/runtime/printstr.asm | 67 +- src/lib/arch/zxnext/runtime/printu16.asm | 8 +- src/lib/arch/zxnext/runtime/printu32.asm | 8 +- src/lib/arch/zxnext/runtime/printu8.asm | 8 +- src/lib/arch/zxnext/runtime/pstore32.asm | 20 +- src/lib/arch/zxnext/runtime/pstoref.asm | 29 +- src/lib/arch/zxnext/runtime/pstorestr.asm | 22 +- src/lib/arch/zxnext/runtime/pstorestr2.asm | 23 +- src/lib/arch/zxnext/runtime/pushf.asm | 40 +- src/lib/arch/zxnext/runtime/random.asm | 117 +--- src/lib/arch/zxnext/runtime/read_restore.asm | 370 +---------- src/lib/arch/zxnext/runtime/save.asm | 194 +----- .../zxnext/runtime/set_pixel_addr_attr.asm | 31 +- src/lib/arch/zxnext/runtime/sposn.asm | 66 +- src/lib/arch/zxnext/runtime/stackf.asm | 57 +- src/lib/arch/zxnext/runtime/store32.asm | 35 +- src/lib/arch/zxnext/runtime/storef.asm | 40 +- src/lib/arch/zxnext/runtime/storestr.asm | 52 +- src/lib/arch/zxnext/runtime/storestr2.asm | 50 +- src/lib/arch/zxnext/runtime/str.asm | 88 +-- src/lib/arch/zxnext/runtime/strcat.asm | 137 +--- src/lib/arch/zxnext/runtime/strcpy.asm | 107 +--- src/lib/arch/zxnext/runtime/strictbool.asm | 18 +- src/lib/arch/zxnext/runtime/strlen.asm | 28 +- src/lib/arch/zxnext/runtime/strslice.asm | 113 +--- src/lib/arch/zxnext/runtime/swap32.asm | 28 +- src/lib/arch/zxnext/runtime/table_jump.asm | 29 +- src/lib/arch/zxnext/runtime/u32tofreg.asm | 101 +-- src/lib/arch/zxnext/runtime/usr.asm | 25 +- src/lib/arch/zxnext/runtime/usr_str.asm | 81 +-- src/lib/arch/zxnext/runtime/val.asm | 128 +--- 146 files changed, 1080 insertions(+), 8117 deletions(-) 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/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/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/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] From 972211747821a9384ed31193b529a8ff71e7ef83 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 13 Feb 2026 12:41:34 +0100 Subject: [PATCH 083/169] build: Update pyproject.toml to be more PEP 621 compliant --- poetry.lock | 6 +++--- pyproject.toml | 54 ++++++++++++++++++++++++++------------------------ 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/poetry.lock b/poetry.lock index ccafe0a5c..947ceba7f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.3.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. [[package]] name = "bump2version" @@ -1050,5 +1050,5 @@ watchmedo = ["PyYAML (>=3.10)"] [metadata] lock-version = "2.1" -python-versions = "^3.11" -content-hash = "f58f04f170bd72f913d33992224112b9fc24a3bd06cd2dc2120e99d29e043955" +python-versions = ">=3.11" +content-hash = "d4834e154177290ee17b2c82c065ff8a39079065d02e18af40fe4bea4c629fb2" diff --git a/pyproject.toml b/pyproject.toml index 0f4eb6144..b37a408e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,39 +1,41 @@ -[tool.poetry] +[project] name = "zxbasic" version = "1.18.7" description = "Boriel's ZX BASIC Compiler" -authors = ["Jose Rodriguez "] +authors = [ + { name = "Jose Rodriguez", email = "zxbasic@boriel.com" } +] 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" - +requires-python = ">=3.11" +keywords = ["compiler", "zxspectrum", "BASIC", "z80"] 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", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - ] + "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", +] -keywords = ['compiler', 'zxspectrum', 'BASIC', 'z80'] # arbitrary keywords +[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.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" @@ -47,8 +49,8 @@ pytest-xdist = "*" setuptools = ">=70.1.1,<79.0.0" [build-system] -requires = ["poetry>=2.3.1"] -build-backend = "poetry.masonry.api" +requires = ["poetry-core>=2.0.0"] +build-backend = "poetry.core.masonry.api" [tool.poe.tasks] [[tool.poe.tasks.lint]] From 410f2975f1b9719cb888c707e994b257ae91c297 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 13 Feb 2026 12:54:10 +0100 Subject: [PATCH 084/169] build: Add clean task to Poe --- pyproject.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index b37a408e8..7c7a95d4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,6 +76,14 @@ shell = """ 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 = [ From ddbeb21edb1a52516de41e9306bbdc6d8554ba65 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 13 Feb 2026 13:54:03 +0100 Subject: [PATCH 085/169] build: Add LICENSE.txt file --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 7c7a95d4f..6e8cc9baa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,9 @@ authors = [ { name = "Jose Rodriguez", email = "zxbasic@boriel.com" } ] license = "AGPL-3.0-or-later" +license-files = [ + "LICENSE.txt" +] readme = "README.md" requires-python = ">=3.11" keywords = ["compiler", "zxspectrum", "BASIC", "z80"] From 3f107f71cbb5375c31a915d1ec18112e6c59f35a Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 13 Feb 2026 13:58:09 +0100 Subject: [PATCH 086/169] refact: rename Workflow and update build tools --- .github/workflows/{python-app.yml => run-tests.yml} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename .github/workflows/{python-app.yml => run-tests.yml} (93%) diff --git a/.github/workflows/python-app.yml b/.github/workflows/run-tests.yml similarity index 93% rename from .github/workflows/python-app.yml rename to .github/workflows/run-tests.yml index 44d531397..657fe4481 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: @@ -29,8 +29,8 @@ 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 From 5c09e9c3e4cb8cbe0065ba107bb200588fed22f1 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 13 Feb 2026 17:51:57 +0100 Subject: [PATCH 087/169] refact: consolidate redundant properties --- src/arch/z80/visitor/translator.py | 2 +- src/symbols/arrayaccess.py | 13 ++----------- src/symbols/call.py | 11 ++++++----- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/arch/z80/visitor/translator.py b/src/arch/z80/visitor/translator.py index 41841be81..b01bd9da0 100644 --- a/src/arch/z80/visitor/translator.py +++ b/src/arch/z80/visitor/translator.py @@ -443,7 +443,7 @@ def visit_LETARRAYSUBSTR(self, node): self.runtime_call(RuntimeLabel.LETSUBSTR, 0) def visit_ARRAYACCESS(self, node): - yield self.visit(node.arglist) + yield self.visit(node.args) def visit_STRSLICE(self, node): yield self.visit(node.string) diff --git a/src/symbols/arrayaccess.py b/src/symbols/arrayaccess.py index f5bbe90da..d6c79bc6f 100644 --- a/src/symbols/arrayaccess.py +++ b/src/symbols/arrayaccess.py @@ -52,15 +52,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 +71,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 +122,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/call.py b/src/symbols/call.py index f646b79cb..9cb789e20 100644 --- a/src/symbols/call.py +++ b/src/symbols/call.py @@ -33,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 @@ -55,7 +55,7 @@ def __init__(self, entry: SymbolID, arglist: Iterable[SymbolARGUMENT], lineno: i arg.value.ref.is_dynamically_accessed = True @property - def entry(self): + def entry(self) -> SymbolID: return self.children[0] @entry.setter @@ -67,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: @@ -104,6 +104,7 @@ def make_node(cls, id_: str, params, lineno: int, filename: str) -> Self | None: 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) From ec5c6cdb2ae1ee35035c4fc19ce167fc0bd5fc24 Mon Sep 17 00:00:00 2001 From: ConradoBadenas Date: Tue, 17 Feb 2026 16:06:10 +0100 Subject: [PATCH 088/169] Update doublesizeprint.bas.md Very minor bug report in https://t.me/c/2140206582/1/4015 --- docs/library/doublesizeprint.bas.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 From 11fc7719f59f191c80df6033b8f7e2d0cb3fc08b Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 13 Feb 2026 18:02:10 +0100 Subject: [PATCH 089/169] Fix typo. --- src/symbols/id_/ref/varref.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/symbols/id_/ref/varref.py b/src/symbols/id_/ref/varref.py index 36538833d..482916945 100644 --- a/src/symbols/id_/ref/varref.py +++ b/src/symbols/id_/ref/varref.py @@ -18,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 From 638d5d4c3043d7354f800e94b2efabbf63748e9a Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 13 Feb 2026 18:26:03 +0100 Subject: [PATCH 090/169] refact: Clean up annotations --- src/api/constants.py | 47 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/api/constants.py b/src/api/constants.py index 888a89b95..fb7c42a81 100644 --- a/src/api/constants.py +++ b/src/api/constants.py @@ -4,11 +4,12 @@ # 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 @@ -41,18 +42,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 @@ -69,7 +70,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,7 +83,7 @@ class TYPE(enum.IntEnum): boolean = 10 @classmethod - def type_size(cls, type_: "TYPE") -> int: + def type_size(cls, type_: TYPE) -> int: type_sizes = { cls.boolean: 1, cls.byte: 1, @@ -99,50 +100,50 @@ def type_size(cls, type_: "TYPE") -> int: 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 { @@ -158,12 +159,12 @@ 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 @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: @@ -181,11 +182,11 @@ 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 @@ -197,11 +198,11 @@ class CONVENTION(str, enum.Enum): 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 From 7ee3e8fd01f7b4edc901f89151839aa589ae763d Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 14 Feb 2026 10:16:59 +0100 Subject: [PATCH 091/169] feat: add InternalTypeError exception class --- src/api/exception.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/api/exception.py b/src/api/exception.py index a1c2d849b..ca353394a 100644 --- a/src/api/exception.py +++ b/src/api/exception.py @@ -5,6 +5,8 @@ # See https://www.gnu.org/licenses/agpl-3.0.html for details. # -------------------------------------------------------------------- +from __future__ import annotations + __all__ = [ "Error", "InternalError", @@ -57,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) From 11cf95f1bf2e4462803dd462745941227639df5e Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 15 Feb 2026 19:07:44 +0100 Subject: [PATCH 092/169] build: poe format not auto-fix more --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 6e8cc9baa..fa47935b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,7 @@ shell = "pytest tests --no-cov" [[tool.poe.tasks.format]] help = "Formats code" shell = """ + ruff check --fix ruff check --select I --fix . ruff format . """ From d45f5dcdb9912f395d35f694aca575b34525281d Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Tue, 17 Feb 2026 00:34:45 +0100 Subject: [PATCH 093/169] tests: add conftest.py Just a simple import to prevent circular imports! --- tests/__init__.py | 1 - tests/conftest.py | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 tests/conftest.py diff --git a/tests/__init__.py b/tests/__init__.py index ba3d20693..0b98abd16 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -6,7 +6,6 @@ # -------------------------------------------------------------------- import os -import os.path import sys path = os.path.realpath(os.path.join(os.path.dirname(__file__), os.pardir)) 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 From 6bf78fdab19f7b313451e4cd237c71b741dee624 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 14 Feb 2026 21:15:45 +0100 Subject: [PATCH 094/169] refact: fix Typing system Typing system was broken and legacy. This refact fixes that, and also fix a lot of typing annotations. Hopefully, this will open the door to Type (structs!) --- src/api/check.py | 84 ++-- src/api/constants.py | 11 +- src/api/decorator.py | 12 + src/api/symboltable/symboltable.py | 96 +++-- src/api/typing/__init__.py | 0 src/api/typing/system.py | 63 +++ src/arch/z80/visitor/translator.py | 8 +- .../z80/visitor/translator_inst_visitor.py | 13 +- src/symbols/argument.py | 6 +- src/symbols/arrayaccess.py | 6 +- src/symbols/arraydecl.py | 3 +- src/symbols/binary.py | 8 +- src/symbols/builtin.py | 11 +- src/symbols/funcdecl.py | 4 +- src/symbols/id_/_id.py | 23 +- src/symbols/id_/interface.py | 6 +- src/symbols/sym.py | 5 + src/symbols/type_.py | 270 ++++++------- src/symbols/typecast.py | 6 +- src/symbols/unary.py | 13 +- src/zxbc/zxbparser.py | 381 ++++++++++-------- tests/symbols/test_symbolARRAYACCESS.py | 16 +- tests/symbols/test_symbolBASICTYPE.py | 4 +- tests/symbols/test_symbolBINARY.py | 4 +- tests/symbols/test_symbolFUNCDECL.py | 4 +- tests/symbols/test_symbolSTRSLICE.py | 2 +- tests/symbols/test_symbolTYPE.py | 18 +- tests/symbols/test_symbolTYPEALIAS.py | 8 +- tests/symbols/test_symbolTYPECAST.py | 4 +- tests/symbols/test_symbolVAR.py | 6 +- tests/symbols/test_symbolVARARRAY.py | 2 +- 31 files changed, 665 insertions(+), 432 deletions(-) create mode 100644 src/api/typing/__init__.py create mode 100644 src/api/typing/system.py diff --git a/src/api/check.py b/src/api/check.py index b728d0511..c9425fcb1 100644 --- a/src/api/check.py +++ b/src/api/check.py @@ -4,6 +4,7 @@ # 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 @@ -82,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. @@ -128,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 @@ -151,7 +154,7 @@ 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 @@ -252,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') """ @@ -279,30 +287,30 @@ 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(*p: symbols.SYMBOL) -> bool: """A static value (does not change at runtime) which is known at compile time """ @@ -313,7 +321,12 @@ 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): @@ -330,8 +343,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 @@ -341,7 +354,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 @@ -350,8 +363,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 @@ -361,7 +374,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 @@ -373,7 +386,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 @@ -384,11 +397,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 @@ -401,17 +425,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? @@ -449,7 +473,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 """ @@ -468,7 +492,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/constants.py b/src/api/constants.py index fb7c42a81..6f1981b11 100644 --- a/src/api/constants.py +++ b/src/api/constants.py @@ -11,16 +11,14 @@ from enum import StrEnum 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)) # ---------------------------------------------------------------------- @@ -81,6 +79,7 @@ class TYPE(enum.IntEnum): float = 8 string = 9 boolean = 10 + nil = 11 @classmethod def type_size(cls, type_: TYPE) -> int: @@ -96,6 +95,7 @@ def type_size(cls, type_: TYPE) -> int: cls.float: 5, cls.string: 2, cls.unknown: 0, + cls.nil: 0, } return type_sizes[type_] @@ -163,6 +163,7 @@ def to_string(type_: TYPE) -> str: """Return ID representation (string) of a type""" return type_.name + @cached_function @staticmethod def to_type(typename: str) -> TYPE | None: """Converts a type ID to name. On error returns None""" diff --git a/src/api/decorator.py b/src/api/decorator.py index 8c1d57057..e2573df65 100644 --- a/src/api/decorator.py +++ b/src/api/decorator.py @@ -6,6 +6,7 @@ # -------------------------------------------------------------------- from collections.abc import Callable +from functools import wraps class ClassProperty: @@ -24,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/symboltable/symboltable.py b/src/api/symboltable/symboltable.py index 1deaf810a..70e66ba54 100644 --- a/src/api/symboltable/symboltable.py +++ b/src/api/symboltable/symboltable.py @@ -58,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: @@ -91,16 +91,18 @@ def get_existing_entry(self, id_: str, scope: Scope | None = None) -> symbols.ID 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 @@ -174,7 +176,15 @@ 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 @@ -328,8 +338,9 @@ 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, ): """Access a symbol by its identifier and checks if it exists. @@ -350,7 +361,7 @@ def access_id( 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 @@ -358,7 +369,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) @@ -475,7 +486,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. @@ -500,7 +513,7 @@ def declare_variable(self, id_: str, lineno: int, type_, default_value=None, cla entry = self.get_entry(id_, scope=self.current_scope) 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: @@ -522,7 +535,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 @@ -533,7 +547,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: @@ -546,23 +560,27 @@ 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): @@ -624,11 +642,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. @@ -641,23 +665,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 @@ -671,7 +707,7 @@ def declare_array(self, id_: str, lineno: int, type_, bounds, default_value=None entry = self.get_entry(id_, self.current_scope) 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: @@ -699,7 +735,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) @@ -740,7 +776,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: @@ -818,7 +854,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/typing/__init__.py b/src/api/typing/__init__.py new file mode 100644 index 000000000..e69de29bb 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/arch/z80/visitor/translator.py b/src/arch/z80/visitor/translator.py index b01bd9da0..d6eeddc5c 100644 --- a/src/arch/z80/visitor/translator.py +++ b/src/arch/z80/visitor/translator.py @@ -1017,12 +1017,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 306c66512..393139c27 100644 --- a/src/arch/z80/visitor/translator_inst_visitor.py +++ b/src/arch/z80/visitor/translator_inst_visitor.py @@ -26,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, @@ -42,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/symbols/argument.py b/src/symbols/argument.py index 7e3fb037e..28e0ca47b 100644 --- a/src/symbols/argument.py +++ b/src/symbols/argument.py @@ -9,6 +9,7 @@ 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 @@ -80,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 d6c79bc6f..d8c12e9e7 100644 --- a/src/symbols/arrayaccess.py +++ b/src/symbols/arrayaccess.py @@ -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 diff --git a/src/symbols/arraydecl.py b/src/symbols/arraydecl.py index a409dbd50..5ea367244 100644 --- a/src/symbols/arraydecl.py +++ b/src/symbols/arraydecl.py @@ -5,13 +5,14 @@ # 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/binary.py b/src/symbols/binary.py index 1d3c256fc..624b5f83f 100644 --- a/src/symbols/binary.py +++ b/src/symbols/binary.py @@ -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/builtin.py b/src/symbols/builtin.py index f23e24dc1..592cf16d7 100644 --- a/src/symbols/builtin.py +++ b/src/symbols/builtin.py @@ -4,6 +4,8 @@ # 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 @@ -62,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/funcdecl.py b/src/symbols/funcdecl.py index 3757abd92..4a7d763e1 100644 --- a/src/symbols/funcdecl.py +++ b/src/symbols/funcdecl.py @@ -4,12 +4,14 @@ # 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): @@ -68,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_/_id.py b/src/symbols/id_/_id.py index ca9fc8836..e233a9851 100644 --- a/src/symbols/id_/_id.py +++ b/src/symbols/id_/_id.py @@ -16,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 @@ -47,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 @@ -95,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 @@ -122,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 dc99fda37..fbe9ae467 100644 --- a/src/symbols/id_/interface.py +++ b/src/symbols/id_/interface.py @@ -9,7 +9,7 @@ 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): @@ -26,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/sym.py b/src/symbols/sym.py index 8f93a0239..faf9d5776 100644 --- a/src/symbols/sym.py +++ b/src/symbols/sym.py @@ -35,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", @@ -50,6 +53,7 @@ "BUILTIN", "CALL", "CONSTEXPR", + "EXPR", "FUNCCALL", "FUNCDECL", "ID", @@ -63,6 +67,7 @@ "TYPE", "TYPECAST", "TYPEREF", + "TYPING", "UNARY", "VARDECL", ] diff --git a/src/symbols/type_.py b/src/symbols/type_.py index be13c7f8e..d463d102a 100644 --- a/src/symbols/type_.py +++ b/src/symbols/type_.py @@ -4,29 +4,36 @@ # 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 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)) @@ -34,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 - - if len(self.children) != 1: - return False + 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 - 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, implicit: bool = False): + def __init__(self, type_: SymbolTYPE, lineno: int, filename: str = "", *, implicit: bool = False): assert isinstance(type_, SymbolTYPE) - super().__init__(type_.name, lineno, type_) - self.implicit = implicit + 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 + + @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: @@ -241,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 @@ -279,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: @@ -310,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 31a2d30d2..74047c553 100644 --- a/src/symbols/typecast.py +++ b/src/symbols/typecast.py @@ -35,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 @@ -43,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: @@ -84,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)) diff --git a/src/symbols/unary.py b/src/symbols/unary.py index 963232f38..a4651cdd0 100644 --- a/src/symbols/unary.py +++ b/src/symbols/unary.py @@ -4,6 +4,8 @@ # 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 @@ -53,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: @@ -73,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/zxbc/zxbparser.py b/src/zxbc/zxbparser.py index 41071ba0e..d8c9195c7 100755 --- a/src/zxbc/zxbparser.py +++ b/src/zxbc/zxbparser.py @@ -9,10 +9,11 @@ import math import sys +from collections.abc import Callable from math import pi as PI # typings -from typing import NamedTuple +from typing import NamedTuple, cast import src.api.config import src.api.dataref @@ -40,7 +41,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 @@ -53,7 +54,7 @@ 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 @@ -154,13 +155,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 # ---------------------------------------------------------------------- @@ -198,13 +203,20 @@ def make_number(value, lineno: int, type_=None): 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): @@ -212,7 +224,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 @@ -220,17 +238,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 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) @@ -267,12 +294,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_) @@ -314,7 +346,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, ) @@ -333,7 +365,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 @@ -383,7 +415,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 @@ -392,7 +424,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 @@ -418,9 +450,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): @@ -435,7 +474,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_ @@ -468,7 +507,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)) # ---------------------------------------------------------------------- @@ -704,8 +743,8 @@ def p_var_decl_ini(p): if typedef.implicit: typedef = sym.TYPEREF(expr.type_, p.lexer.lineno, implicit=True) - value = make_typecast(typedef, expr, p.lineno(4)) - defval = value if is_static(expr) and value.type_ != TYPE.string else None + value = make_typecast(typedef.type_, expr, p.lineno(4)) + 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) @@ -851,8 +890,8 @@ def check_bound(boundlist, remaining): 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) + if p[6] == Type.string or entry.type_ == Type.string: + errmsg.syntax_error_cannot_initialize_array_of_type(p.lineno(1), Type.string) return @@ -943,7 +982,7 @@ def p_staement_func_decl(p): 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))) + p[0] = make_sentence(p.lineno(1), "BORDER", make_typecast(Type.ubyte, p[2], p.lineno(1))) def p_statement_plot(p): @@ -951,8 +990,8 @@ def p_statement_plot(p): 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)), + make_typecast(Type.ubyte, p[2], p.lineno(3)), + make_typecast(Type.ubyte, p[4], p.lineno(3)), ) @@ -961,8 +1000,8 @@ def p_statement_plot_attr(p): 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)), + make_typecast(Type.ubyte, p[3], p.lineno(4)), + make_typecast(Type.ubyte, p[5], p.lineno(4)), p[2], ) @@ -972,9 +1011,9 @@ def p_statement_draw3(p): 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)), + 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)), ) @@ -983,9 +1022,9 @@ def p_statement_draw3_attr(p): 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)), + 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], ) @@ -995,8 +1034,8 @@ def p_statement_draw(p): 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)), + make_typecast(Type.integer, p[2], p.lineno(3)), + make_typecast(Type.integer, p[4], p.lineno(3)), ) @@ -1005,8 +1044,8 @@ def p_statement_draw_attr(p): 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)), + make_typecast(Type.integer, p[3], p.lineno(4)), + make_typecast(Type.integer, p[5], p.lineno(4)), p[2], ) @@ -1016,9 +1055,9 @@ def p_statement_circle(p): 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)), + 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)), ) @@ -1027,9 +1066,9 @@ def p_statement_circle_attr(p): 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)), + 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], ) @@ -1046,12 +1085,12 @@ def p_statement_asm(p): 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)) + 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))) + p[0] = make_sentence(p.lineno(1), "RANDOMIZE", make_typecast(Type.ulong, p[2], p.lineno(1))) def p_statement_beep(p): @@ -1059,8 +1098,8 @@ def p_statement_beep(p): 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)), + make_typecast(Type.float_, p[2], p.lineno(1)), + make_typecast(Type.float_, p[4], p.lineno(3)), ) @@ -1196,7 +1235,7 @@ def p_arr_assignment(p): if entry is None: return - if entry.type_ == TYPE.string: + if entry.type_ == Type.string: variable = gl.SYMBOL_TABLE.access_array(id_, p.lineno(i)) # variable is an array. If it has 0 bounds means they are undefined (param byref) if len(variable.ref.bounds) and len(variable.ref.bounds) + 1 == len(arg_list): @@ -1229,7 +1268,7 @@ def p_substr_assignment_no_let(p): if entry.class_ == CLASS.unknown: entry.class_ = CLASS.var - if p[6].type_ != TYPE.string: + if p[6].type_ != Type.string: errmsg.syntax_error_expected_string(p.lineno(5), p[6].type_) lineno = p.lineno(2) @@ -1262,11 +1301,11 @@ def p_substr_assignment(p): errmsg.syntax_error_cannot_assign_not_a_var(p.lineno(2), p[2]) return - if entry.type_ != TYPE.string: + if entry.type_ != Type.string: errmsg.syntax_error_expected_string(p.lineno(2), entry.type_) return - if p[5].type_ != TYPE.string: + if p[5].type_ != Type.string: errmsg.syntax_error_expected_string(p.lineno(4), p[5].type_) return @@ -1324,10 +1363,10 @@ def p_str_assign(p): p[0] = None return - if r.type_ != TYPE.string: + if r.type_ != Type.string: errmsg.syntax_error_expected_string(lineno, r.type_) - entry = SYMBOL_TABLE.access_var(q, lineno, default_type=TYPE.string) + entry = SYMBOL_TABLE.access_var(q, lineno, default_type=Type.string) if entry is None: p[0] = None return @@ -1611,7 +1650,7 @@ def p_for_sentence_start(p): "FOR start value is lower than end. This FOR loop is useless", ) - id_type = common_type(common_type(p[4], p[6]), p[7]) + id_type = common_type(common_type(p[4].type_, p[6].type_), p[7].type_) variable = SYMBOL_TABLE.access_var(p[2], p.lineno(2), default_type=id_type) if variable is None: return @@ -1651,7 +1690,7 @@ def p_error_raise(p): r = make_binary( p.lineno(1), "MINUS", - make_typecast(TYPE.ubyte, p[2], p.lineno(1)), + make_typecast(Type.ubyte, p[2], p.lineno(1)), q, lambda x, y: x - y, ) @@ -1667,7 +1706,7 @@ def p_stop_raise(p): r = make_binary( p.lineno(1), "MINUS", - make_typecast(TYPE.ubyte, q, p.lineno(1)), + make_typecast(Type.ubyte, q, p.lineno(1)), z, lambda x, y: x - y, ) @@ -1751,7 +1790,12 @@ def p_data(p): 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) + 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, p.lineno(1), type_=type_, class_=CLASS.function) if not entry: continue @@ -1811,9 +1855,9 @@ def p_read(p): return mark_entry_as_accessed(entry) - if entry.type_ == TYPE.auto: + if entry.type_ == Type.auto: entry.type_ = _TYPE(gl.DEFAULT_TYPE) - errmsg.warning_implicit_type(p.lineno(2), p[2], entry.type_) + errmsg.warning_implicit_type(p.lineno(2), p[2], entry.type_.name) reads.append(make_sentence(p.lineno(1), "READ", entry)) continue @@ -1986,8 +2030,8 @@ def p_print_sentence(p): 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)) + 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): @@ -1998,7 +2042,7 @@ def p_print_list_expr(p): | 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))) + p[0] = make_sentence(p.lineno(1), p[1] + "_TMP", make_typecast(Type.ubyte, p[2], p.lineno(1))) else: p[0] = p[1] @@ -2025,7 +2069,7 @@ def p_attr(p): # 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))) + 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): @@ -2063,19 +2107,19 @@ def p_print_list_at(p): 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)), + 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))) + 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)) + expr = make_typecast(Type.ubyte, p[2], p.lineno(1)) p[0] = make_sentence(p.lineno(1), "ON_" + p[3], expr, *p[4]) @@ -2130,7 +2174,7 @@ def p_return_expr(p): p[0] = None return - if is_numeric(p[2]) and FUNCTION_LEVEL[-1].type_ == TYPE.string: + if is_numeric(p[2]) and FUNCTION_LEVEL[-1].type_.final == Type.string: error( p.lineno(2), "Type Error: Function must return a string, not a numeric value", @@ -2138,7 +2182,7 @@ def p_return_expr(p): p[0] = None return - if not is_numeric(p[2]) and FUNCTION_LEVEL[-1].type_ != TYPE.string: + if not is_numeric(p[2]) and FUNCTION_LEVEL[-1].type_.final != Type.string: error( p.lineno(2), "Type Error: Function must return a numeric value, not a string", @@ -2156,7 +2200,7 @@ def p_return_expr(p): def p_pause(p): """statement : PAUSE expr""" - p[0] = make_sentence(p.lineno(1), "PAUSE", make_typecast(TYPE.uinteger, p[2], p.lineno(1))) + p[0] = make_sentence(p.lineno(1), "PAUSE", make_typecast(Type.uinteger, p[2], p.lineno(1))) def p_poke(p): @@ -2170,8 +2214,8 @@ def p_poke(p): 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)), + make_typecast(Type.uinteger, p[i], p.lineno(i + 1)), + make_typecast(Type.ubyte, p[i + 2], p.lineno(i + 1)), ) @@ -2186,7 +2230,7 @@ def p_poke2(p): p[0] = make_sentence( p.lineno(1), "POKE", - make_typecast(TYPE.uinteger, p[i + 1], p.lineno(i + 2)), + make_typecast(Type.uinteger, p[i + 1], p.lineno(i + 2)), make_typecast(p[i], p[i + 3], p.lineno(i + 3)), ) @@ -2202,7 +2246,7 @@ def p_poke3(p): p[0] = make_sentence( p.lineno(1), "POKE", - make_typecast(TYPE.uinteger, p[i + 2], p.lineno(i + 3)), + make_typecast(Type.uinteger, p[i + 2], p.lineno(i + 3)), make_typecast(p[i], p[i + 4], p.lineno(i + 5)), ) @@ -2212,8 +2256,8 @@ def p_out(p): 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)), + make_typecast(Type.uinteger, p[2], p.lineno(3)), + make_typecast(Type.ubyte, p[4], p.lineno(4)), ) @@ -2227,7 +2271,7 @@ def p_simple_instruction(p): | OVER expr | INVERSE expr """ - p[0] = make_sentence(p.lineno(1), p[1], make_typecast(TYPE.ubyte, p[2], p.lineno(1))) + p[0] = make_sentence(p.lineno(1), p[1], make_typecast(Type.ubyte, p[2], p.lineno(1))) def p_save_code(p): @@ -2236,7 +2280,7 @@ def p_save_code(p): | SAVE expr ARRAY_ID """ expr = p[2] - if expr.type_ != TYPE.string: + if expr.type_ != Type.string: errmsg.syntax_error_expected_string(p.lineno(1), expr.type_) if len(p) == 4: @@ -2248,8 +2292,8 @@ def p_save_code(p): 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)) + 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) @@ -2259,7 +2303,7 @@ def p_save_data(p): | SAVE expr DATA ID | SAVE expr DATA ID LP RP """ - if p[2].type_ != TYPE.string: + if p[2].type_ != Type.string: errmsg.syntax_error_expected_string(p.lineno(1), p[2].type_) if len(p) != 4: @@ -2270,7 +2314,7 @@ def p_save_data(p): mark_entry_as_accessed(entry) access = entry - start = make_unary(p.lineno(4), "ADDRESS", access, type_=TYPE.uinteger) + 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)) @@ -2278,10 +2322,10 @@ def p_save_data(p): 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) + 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) + length = make_unary(p.lineno(3), "ADDRESS", access, type_=Type.uinteger) p[0] = make_sentence(p.lineno(1), p[1], p[2], start, length) @@ -2299,7 +2343,7 @@ def p_load_code(p): | load_or_verify expr CODE expr | load_or_verify expr CODE expr COMMA expr """ - if p[2].type_ != TYPE.string: + if p[2].type_ != Type.string: errmsg.syntax_error_expected_string(p.lineno(3), p[2].type_) if len(p) == 4: @@ -2313,12 +2357,12 @@ def p_load_code(p): 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)) + 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)) + length = make_typecast(Type.uinteger, p[6], p.lineno(5)) p[0] = make_sentence(p.lineno(3), p[1], p[2], start, length) @@ -2328,7 +2372,7 @@ def p_load_data(p): | load_or_verify expr DATA ID | load_or_verify expr DATA ID LP RP """ - if p[2].type_ != TYPE.string: + if p[2].type_ != Type.string: errmsg.syntax_error_expected_string(p.lineno(1), p[2].type_) if len(p) != 4: @@ -2338,7 +2382,7 @@ def p_load_data(p): return mark_entry_as_accessed(entry) - start = make_unary(p.lineno(4), "ADDRESS", entry, type_=TYPE.uinteger) + 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)) @@ -2346,10 +2390,10 @@ def p_load_data(p): 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) + 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) + length = make_unary(p.lineno(3), "ADDRESS", entry, type_=Type.uinteger) p[0] = make_sentence(p.lineno(3), p[1], p[2], start, length) @@ -2397,8 +2441,8 @@ def p_expr_pow_expr(p): 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), + make_typecast(Type.float_, p[1], p.lineno(2)), + make_typecast(Type.float_, p[3], p.lexer.lineno), lambda x, y: x**y, ) @@ -2409,14 +2453,14 @@ def p_expr_shl_expr(p): p[0] = None return - if p[1].type_ in (TYPE.float_, TYPE.fixed): - p[1] = make_typecast(TYPE.ulong, p[1], p.lineno(2)) + 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)), + make_typecast(Type.ubyte, p[3], p.lineno(2)), lambda x, y: x << y, ) @@ -2427,14 +2471,14 @@ def p_expr_shr_expr(p): p[0] = None return - if p[1].type_ in (TYPE.float_, TYPE.fixed): - p[1] = make_typecast(TYPE.ulong, p[1], p.lineno(2)) + 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)), + make_typecast(Type.ubyte, p[3], p.lineno(2)), lambda x, y: x >> y, ) @@ -2531,7 +2575,7 @@ def p_number_expr(p): def p_expr_PI(p): """bexpr : PI""" - p[0] = make_number(PI, lineno=p.lineno(1), type_=TYPE.float_) + p[0] = make_number(PI, lineno=p.lineno(1), type_=Type.float_) def p_expr_string(p): @@ -2567,11 +2611,11 @@ def p_string_lp_expr_rp(p): 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": + 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) + entry = SYMBOL_TABLE.access_var(p[1], p.lineno(1), default_type=Type.string) p[0] = None if entry is None: return @@ -2587,10 +2631,10 @@ def p_string_substr(p): def p_string_expr_lp(p): """string : LP expr RP substr""" - if p[2].type_ != TYPE.string: + 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_), + "Expected a string type expression. Got %s type instead" % Type.to_string(p[2].type_), ) p[0] = None else: @@ -2600,25 +2644,25 @@ def p_string_expr_lp(p): 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)), + 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)), + 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, p[2], p.lineno(1)), make_typecast( - TYPE.uinteger, + Type.uinteger, make_number(gl.MAX_STRSLICE_IDX, lineno=p.lineno(4)), lineno=p.lineno(4), ), @@ -2629,9 +2673,9 @@ def p_subind_TOstr(p): 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(0, lineno=p.lineno(2)), p.lineno(1)), make_typecast( - TYPE.uinteger, + Type.uinteger, make_number(gl.MAX_STRSLICE_IDX, lineno=p.lineno(3)), p.lineno(2), ), @@ -2646,9 +2690,9 @@ def p_id_expr(p): 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_) + if entry.type_ == Type.auto: + entry.type_ = _TYPEREF(gl.DEFAULT_TYPE) + errmsg.warning_implicit_type(p.lineno(1), p[1], entry.type_.name) p[0] = entry @@ -2705,7 +2749,7 @@ def p_idcall_expr(p): 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: + 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 @@ -2958,10 +3002,10 @@ def p_function_header_pre(p): 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: + 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_) + errmsg.warning_implicit_type(p[3].lineno, p[0].entry.name, p[0].type_.name) if forwarded and previoustype_ != p[0].type_: errmsg.syntax_error_func_type_mismatch(lineno, p[0].entry) @@ -3271,14 +3315,14 @@ def p_preproc_pragma_pop(p): 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) + 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, + make_typecast(Type.uinteger, p[2], p.lineno(1)), + type_=Type.uinteger, ) @@ -3286,7 +3330,7 @@ def p_expr_rnd(p): """bexpr : RND %prec ID | RND LP RP """ - p[0] = make_builtin(p.lineno(1), "RND", None, type_=TYPE.float_) + p[0] = make_builtin(p.lineno(1), "RND", None, type_=Type.float_) def p_expr_peek(p): @@ -3294,14 +3338,23 @@ def p_expr_peek(p): p[0] = make_builtin( p.lineno(1), "PEEK", - make_typecast(TYPE.uinteger, p[2], p.lineno(1)), - type_=TYPE.ubyte, + 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]) + if p[3] is None or p[5] is None: + p[0] = None + return + + p[0] = make_builtin( + p.lineno(1), + "PEEK", + make_typecast(Type.uinteger, p[5], p.lineno(4)), + type_=cast(sym.TYPEREF, p[3]).type_, + ) def p_expr_in(p): @@ -3309,8 +3362,8 @@ def p_expr_in(p): p[0] = make_builtin( p.lineno(1), "IN", - make_typecast(TYPE.uinteger, p[2], p.lineno(1)), - type_=TYPE.ubyte, + make_typecast(Type.uinteger, p[2], p.lineno(1)), + type_=Type.ubyte, ) @@ -3326,10 +3379,10 @@ def p_expr_lbound(p): 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) + 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) + p[0] = make_number(len(entry.bounds), p.lineno(3), Type.uinteger) def p_expr_lbound_expr(p): @@ -3347,7 +3400,7 @@ def p_expr_lbound_expr(p): return mark_entry_as_accessed(entry) - num = make_typecast(TYPE.uinteger, expr, p.lineno(6)) + num = make_typecast(Type.uinteger, expr, p.lineno(6)) if num is None: p[0] = None return @@ -3363,11 +3416,11 @@ def p_expr_lbound_expr(p): return if not val: # 0 => number of dims - p[0] = make_number(len(entry.bounds), p.lineno(3), TYPE.uinteger) + 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) + 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) + p[0] = make_number(entry.bounds[val - 1].upper, p.lineno(3), Type.uinteger) return if p[1] == "LBOUND": @@ -3375,7 +3428,7 @@ def p_expr_lbound_expr(p): else: entry.ref.ubound_used = True - p[0] = make_builtin(p.lineno(1), p[1], [entry, num], type_=TYPE.uinteger) + p[0] = make_builtin(p.lineno(1), p[1], [entry, num], type_=Type.uinteger) def p_len(p): @@ -3385,13 +3438,13 @@ def p_len(p): 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_)) + 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) + p[0] = make_builtin(p.lineno(1), "LEN", arg, type_=Type.uinteger) def p_sizeof(p): @@ -3399,11 +3452,11 @@ def p_sizeof(p): | 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)) + 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)) + p[0] = make_number(Type.size(entry.type_), lineno=p.lineno(3)) def p_str(p): @@ -3414,21 +3467,21 @@ def p_str(p): p[0] = make_builtin( p.lineno(1), "STR", - make_typecast(TYPE.float_, p[2], p.lineno(1)), - type_=TYPE.string, + 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) + 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) + 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): @@ -3439,9 +3492,9 @@ def p_chr(p): 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[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) + p[0] = make_builtin(p.lineno(1), "CHR", p[2], type_=Type.string) def p_val(p): @@ -3455,11 +3508,11 @@ def val(s): 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_)) + 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_) + p[0] = make_builtin(p.lineno(1), "VAL", p[2], lambda x: val(x), type_=Type.float_) def p_code(p): @@ -3475,25 +3528,25 @@ def asc(x): p[0] = None return - if p[2].type_ != TYPE.string: - errmsg.syntax_error_expected_string(p.lineno(1), TYPE.to_string(p[2].type_)) + 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) + 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: + 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_) + p[0] = make_builtin(p.lineno(1), "SGN", p[2], lambda x: sgn(x), type_=Type.byte_) # ---------------------------------------- @@ -3504,7 +3557,7 @@ def p_expr_trig(p): p[0] = make_builtin( p.lineno(1), p[1], - make_typecast(TYPE.float_, p[2], p.lineno(1)), + make_typecast(Type.float_, p[2], p.lineno(1)), { "SIN": math.sin, "COS": math.cos, @@ -3516,7 +3569,7 @@ def p_expr_trig(p): "EXP": math.exp, "SQR": math.sqrt, }[p[1]], - type_=TYPE.float_, + type_=Type.float_, ) @@ -3539,7 +3592,7 @@ def p_math_fn(p): # ---------------------------------------- def p_expr_int(p): """bexpr : INT bexpr %prec UMINUS""" - p[0] = make_typecast(TYPE.long_, p[2], p.lineno(1)) + p[0] = make_typecast(Type.long_, p[2], p.lineno(1)) def p_abs(p): diff --git a/tests/symbols/test_symbolARRAYACCESS.py b/tests/symbols/test_symbolARRAYACCESS.py index 9ebd64ed7..c876e87e3 100644 --- a/tests/symbols/test_symbolARRAYACCESS.py +++ b/tests/symbols/test_symbolARRAYACCESS.py @@ -17,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 @@ -26,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), @@ -54,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) @@ -66,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 9eab67d54..b206750d2 100644 --- a/tests/symbols/test_symbolBASICTYPE.py +++ b/tests/symbols/test_symbolBASICTYPE.py @@ -52,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_) @@ -62,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 369395f8f..22c3cdead 100644 --- a/tests/symbols/test_symbolBINARY.py +++ b/tests/symbols/test_symbolBINARY.py @@ -10,14 +10,14 @@ 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_symbolFUNCDECL.py b/tests/symbols/test_symbolFUNCDECL.py index b477a3f25..f1ee0f21d 100644 --- a/tests/symbols/test_symbolFUNCDECL.py +++ b/tests/symbols/test_symbolFUNCDECL.py @@ -12,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_symbolSTRSLICE.py b/tests/symbols/test_symbolSTRSLICE.py index 151efceed..882e001af 100644 --- a/tests/symbols/test_symbolSTRSLICE.py +++ b/tests/symbols/test_symbolSTRSLICE.py @@ -59,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 3c691c82f..33d40e4ec 100644 --- a/tests/symbols/test_symbolTYPE.py +++ b/tests/symbols/test_symbolTYPE.py @@ -14,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) @@ -46,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 53715188b..0c3ea2dcf 100644 --- a/tests/symbols/test_symbolTYPEALIAS.py +++ b/tests/symbols/test_symbolTYPEALIAS.py @@ -15,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 f5213aa74..b5c5f8a25 100644 --- a/tests/symbols/test_symbolTYPECAST.py +++ b/tests/symbols/test_symbolTYPECAST.py @@ -10,7 +10,7 @@ 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 @@ -48,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 74f1d1235..24abfdeab 100644 --- a/tests/symbols/test_symbolVAR.py +++ b/tests/symbols/test_symbolVAR.py @@ -9,7 +9,7 @@ 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): @@ -21,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): @@ -43,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 440d2e173..1dfbd2d4c 100644 --- a/tests/symbols/test_symbolVARARRAY.py +++ b/tests/symbols/test_symbolVARARRAY.py @@ -23,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") From c67bb853e8db3700646125ca4bedbf30307f3cf1 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Thu, 26 Feb 2026 00:14:36 +0100 Subject: [PATCH 095/169] fix: Detect static expressions better. Now all non-static expressions are also allowed as initializers. They are just a hiden lazy LET --- src/api/check.py | 24 ++++++++++++++++++++++-- src/zxbc/zxbparser.py | 7 +++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/api/check.py b/src/api/check.py index c9425fcb1..b52092332 100644 --- a/src/api/check.py +++ b/src/api/check.py @@ -160,7 +160,7 @@ def check_call_arguments(lineno: int, id_: str, args: symbols.ARGLIST, filename: 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 ) @@ -310,11 +310,31 @@ def is_CONST(*p: symbols.SYMBOL) -> bool: return is_SYMBOL("CONSTEXPR", *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): diff --git a/src/zxbc/zxbparser.py b/src/zxbc/zxbparser.py index d8c9195c7..ab63938ac 100755 --- a/src/zxbc/zxbparser.py +++ b/src/zxbc/zxbparser.py @@ -246,7 +246,7 @@ def make_builtin( 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: @@ -736,9 +736,8 @@ def p_var_decl_ini(p): if expr is None: return - if not is_static(expr): - if isinstance(expr, sym.UNARY): - expr = make_constexpr(p.lineno(4), expr) # Delayed constant evaluation + if is_static(expr) and isinstance(expr, sym.UNARY): + expr = make_constexpr(p.lineno(4), expr) # Delayed constant evaluation if typedef.implicit: typedef = sym.TYPEREF(expr.type_, p.lexer.lineno, implicit=True) From 97fb33b3872c8a3773adc397e2dd75762230cd1c Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Thu, 26 Feb 2026 00:29:57 +0100 Subject: [PATCH 096/169] Add tests With this update tests must be also updated. --- tests/functional/arch/zx48k/dim_at0.bas | 2 + tests/functional/arch/zx48k/dim_at1.asm | 57 ++++++++++++++++++++ tests/functional/arch/zx48k/dim_at1.bas | 6 +++ tests/functional/arch/zx48k/dimconst2.bas | 3 +- tests/functional/arch/zx48k/dimconst2b.asm | 62 ++++++++++++++++++++++ tests/functional/arch/zx48k/dimconst2b.bas | 4 +- tests/functional/arch/zx48k/dimconst2c.bas | 4 +- tests/functional/arch/zx48k/dimconst2d.bas | 4 +- tests/functional/arch/zx48k/dimconst2f.asm | 50 +++++++++++++++++ tests/functional/arch/zx48k/dimconst2f.bas | 6 +++ tests/functional/arch/zx48k/dimconst2g.asm | 59 ++++++++++++++++++++ tests/functional/arch/zx48k/dimconst2g.bas | 7 +++ tests/functional/arch/zx48k/dimconst2h.asm | 51 ++++++++++++++++++ tests/functional/arch/zx48k/dimconst2h.bas | 6 +++ tests/functional/arch/zx48k/dimconst2i.asm | 53 ++++++++++++++++++ tests/functional/arch/zx48k/dimconst2i.bas | 6 +++ 16 files changed, 372 insertions(+), 8 deletions(-) create mode 100644 tests/functional/arch/zx48k/dim_at1.asm create mode 100644 tests/functional/arch/zx48k/dim_at1.bas create mode 100644 tests/functional/arch/zx48k/dimconst2b.asm create mode 100644 tests/functional/arch/zx48k/dimconst2f.asm create mode 100644 tests/functional/arch/zx48k/dimconst2f.bas create mode 100644 tests/functional/arch/zx48k/dimconst2g.asm create mode 100644 tests/functional/arch/zx48k/dimconst2g.bas create mode 100644 tests/functional/arch/zx48k/dimconst2h.asm create mode 100644 tests/functional/arch/zx48k/dimconst2h.bas create mode 100644 tests/functional/arch/zx48k/dimconst2i.asm create mode 100644 tests/functional/arch/zx48k/dimconst2i.bas 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 From 2b938389163bb46f894f2cbec876f746ef63d8f9 Mon Sep 17 00:00:00 2001 From: t-tang <7981810+t-tang@users.noreply.github.com> Date: Sun, 1 Mar 2026 09:30:07 +0000 Subject: [PATCH 097/169] Add udg support to print42 --- src/lib/arch/zx48k/stdlib/print42.bas | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) 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 From a1146f48ba19c7960dcdc0b0f3d9073cf2fbaa0b Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Tue, 3 Mar 2026 00:30:24 +0100 Subject: [PATCH 098/169] Update tests --- tests/functional/arch/zx48k/print42.asm | 32 +++++++++++++++++++------ 1 file changed, 25 insertions(+), 7 deletions(-) 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 From e6ebd84f69c93120dee28ef61bc3f7f366dcdb8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2026 02:37:27 +0000 Subject: [PATCH 099/169] build(deps): bump markdown from 3.7 to 3.8.1 in /docs Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.7 to 3.8.1. - [Release notes](https://github.com/Python-Markdown/markdown/releases) - [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md) - [Commits](https://github.com/Python-Markdown/markdown/compare/3.7...3.8.1) --- updated-dependencies: - dependency-name: markdown dependency-version: 3.8.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 2da055526..1a342c598 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -8,7 +8,7 @@ colorama==0.4.6 ghp-import==2.1.0 idna==3.10 Jinja2==3.1.6 -Markdown==3.7 +Markdown==3.8.1 MarkupSafe==3.0.2 mergedeep==1.3.4 mkdocs==1.6.1 From 5abc208168225c0498ad111874956264d339418a Mon Sep 17 00:00:00 2001 From: ConradoBadenas Date: Fri, 6 Mar 2026 18:45:26 +0100 Subject: [PATCH 100/169] examples.maskedsprites --- examples/maskedsprites.bas | 161 +++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 examples/maskedsprites.bas diff --git a/examples/maskedsprites.bas b/examples/maskedsprites.bas new file mode 100644 index 000000000..a9e0d45b5 --- /dev/null +++ b/examples/maskedsprites.bas @@ -0,0 +1,161 @@ +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyright (C) 2026 Conrado Badenas +' +' Example for +' Print Masked (AND+OR) Sprites, version 2026.03.06 +' ---------------------------------------------------------------- + +#include + +#include + +CONST NUMMAXSPRITES as UByte = 10 +CONST NUMSPRITES as UByte = 7 +if NUMSPRITES>NUMMAXSPRITES then stop + +dim i,temp as Integer +dim pointx1,pointx2,pointy1,pointy2 as Integer 'for subtractions +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 + +for i=0 to NUMSPRITES-1 + y0(i)=255:y1(i)=255:x1(i)=randomLimit(240) +next i:x1(0)=120 + +SetVisibleScreen(5):if GetVisibleScreen()<>5 then STOP +paper 1:ink 7:border 0:cls + +for i=0 to 127 + pointx1=randomLimit(255):pointy1=randomLimit(191) + pointx2=randomLimit(255):pointy2=randomLimit(191) + plot pointx1,pointy1:draw pointx2-pointx1,pointy2-pointy1 +next i + +CopyScreen5ToScreen7() +'print at 11,8;"Esto es Screen5" +SetDrawingScreen7() 'Bank7 is set at $c000 +'print at 13,8;"Esto es Screen7" +SetDrawingScreen5() 'Bank7 is still set at $c000 +'print at 15,8;"Esto es Screen5" + +ASM + xor a ; 4 Ts reseteamos el contador + ld (23672),a ; 13 Ts de FRAMES a 0 +END ASM + +' Empezamos con VisibleScreen = DrawingScreen = 5 +bucle: + + ' La nueva VisibleScreen está lista para que la ULA la saque por la tele + ' Pero vamos a esperar al principio de un frame para que no haya tearing + ' El 2 de luego significa que obtendremos 25 FPS como máximo + ASM + ld hl,23672 +wait: + ld a,(hl) ; A = contador de FRAMES + cp 2 ; 25 FPS como máximo + jr c,wait ; repetimos si A < 2 + xor a + ld (hl),a ; reseteamos el contador de FRAMES a 0 + END ASM + + ToggleVisibleScreen() + ' Ahora se puede modificar la DrawingScreen porque no está Visible + + ' Restauramos fondo en x0,y0 si está guardado en MaskedSpritesBackground + for i=0 to NUMSPRITES-1 + if y0(i)<>255 then RestoreBackground(x0(i),y0(i),MaskedSpritesBackground(i)) + next i + + ' Actualizamos x0,y0 y calculamos nuevos 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 + + 'Código especial para el prota + 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 + + ' Guardamos fondo en MaskedSpritesBackground y dibujamos sprites en x,y + for i=NUMSPRITES-1 to 1 step -1 + SaveBackgroundAndDrawSprite(x1(i),y1(i),MaskedSpritesBackground(i),@enemigo0) + next i + SaveBackgroundAndDrawSprite(x1(0),y1(0),MaskedSpritesBackground(0),@prota0) + + ' Cambiamos el Set de Backgrounds para la siguiente iteración + ChangeMaskedSpritesBackgroundSet() + + ' Hemos terminado de dibujar + ' DrawingScreen actual está lista para ser mostrada ahora mismo + ' Cambiamos de DrawingScreen para el siguiente ciclo de dibujo + ToggleDrawingScreen() + + ' Ahora VisibleScreen = DrawingScreen y + ' lo que se dibuje se verá mientras se dibuja + +goto bucle + +stop + +prota0: +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 + +enemigo0: +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 + From bf9d867c83f137e58acf6253f2c14dfc7b9c5797 Mon Sep 17 00:00:00 2001 From: ConradoBadenas Date: Fri, 6 Mar 2026 18:51:20 +0100 Subject: [PATCH 101/169] stdlib.cb.maskedsprites --- .../arch/zx48k/stdlib/cb/maskedsprites.bas | 456 ++++++++++++++++++ 1 file changed, 456 insertions(+) create mode 100644 src/lib/arch/zx48k/stdlib/cb/maskedsprites.bas 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..8a2feb376 --- /dev/null +++ b/src/lib/arch/zx48k/stdlib/cb/maskedsprites.bas @@ -0,0 +1,456 @@ +' ---------------------------------------------------------------- +' 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 +' by Benjamín (a.k.a. RetrobenSoft) +' +' Print Masked (AND+OR) Sprites, version 2026.03.06 +' ---------------------------------------------------------------- + +#ifndef __CB_MASKEDSPRITES__ + +REM Avoid recursive / multiple inclusion + +#define __CB_MASKEDSPRITES__ + +#include +#include +#include + + +' ---------------------------------------------------------------- +' 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 +' ---------------------------------------------------------------- +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 hl,$5b5c ; BANKM system variable + ld a,(hl) ; Read BANKM + and %11110111 ; Reset bit3 + or c ; Set screen in bank5/7 + ld bc,$7ffd ; Memory Bank control port + di ; Disable interrupts + ld (hl),a ; Update BANKM system variable + out (c),a ; Set the screen + ei ; Enable interrupts +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 + RETURN ((PEEK $5b5c bAND %1000)>>2) bOR 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. +' ---------------------------------------------------------------- +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 CopyScreen5ToScreen7() + DIM b AS UByte + + b = GetBank() + if b<>7 then SetBank(7) + MemCopy($4000,$c000,$1b00) + if b<>7 then SetBank(b) +END SUB + + +' ---------------------------------------------------------------- +' Copy contents of screen7 to screen5 (display file + attribs) +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB CopyScreen7ToScreen5() + DIM b AS UByte + + b = GetBank() + if b<>7 then SetBank(7) + MemCopy($c000,$4000,$1b00) + if b<>7 then SetBank(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 = GetBank() + if b<>7 then SetBank(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 Dir where Background i begins +' +' ChangeMaskedSpritesBackgroundSet() changes the Set of Backgrounds +' Returns: +' Byte: new value of MaskedSpritesBackgroundSet (IYW to use it) +' ---------------------------------------------------------------- +dim MaskedSpritesBackgroundSet as Byte = 0 +#define MaskedSpritesBackground(i) (56064+((i)*2+MaskedSpritesBackgroundSet)*48) +FUNCTION FASTCALL ChangeMaskedSpritesBackgroundSet() as Byte + MaskedSpritesBackgroundSet = MaskedSpritesBackgroundSet bXOR 1 + RETURN MaskedSpritesBackgroundSet +END FUNCTION + + +' ---------------------------------------------------------------- +' 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: Dir where background will be saved +' UInteger: Dir where sprite image begins +' ---------------------------------------------------------------- +SUB FASTCALL SaveBackgroundAndDrawSprite(X as UByte, Y as UByte, backgroundDir as UInteger, spriteImageDir as UInteger) +ASM + PROC + LOCAL shiftsprite, spriteOK, loop0, loopA, loopB + LOCAL branchA, branchB, nextA, nextB + ; A = X + pop de ; returnDir + 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 = screenDir where drawing will start + ld a,c; ; A = X + and 7 + jr z,spriteOK ; jump if X is a multiple of 8 (unlikely) + ; continue if sprite must be shifted +shiftsprite: + pop bc ; backgroundDir + exx + pop hl ; spriteImageDir + push de ; returnDir + push ix + ld ixh,16 ; 16 scanlines + ld ixl,a ; IXl = X MOD 8 = 1,2,...,7 +loopB: + ld a,(hl) ; mask1 + inc hl + ld c,(hl) ; graph1 + inc hl + ld d,(hl) ; mask2 + inc hl + ld e,(hl) ; graph1 + inc hl + push hl ; spriteImageDir + + ld hl,$FF00 ; H = 255 , L = 0 + ld b,ixl +loop0: + scf + rra ; SCF + RRA injects a 1 in bit7 of A + rr d + rr h + srl c ; ShiftRightLogical injects a 0 in bit7 of C + rr e + rr l + djnz loop0 + 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,branchB ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + exx + pop hl ; spriteImageDir + dec ixh + jp nz,loopB + pop ix + ret +branchB: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextB ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextB: + exx + pop hl ; spriteImageDir + dec ixh + jp nz,loopB + pop ix + ret + +spriteOK: + pop bc ; backgroundDir + pop hl ; spriteImageDir + exx + push de ; returnDir + exx + push ix + ld ixh,16 ; 16 scanlines +loopA: + 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,branchA ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + dec ixh + jp nz,loopA + pop ix + ret +branchA: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextA ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextA: + dec ixh + jp nz,loopA + 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: Dir where saved background begins +' ---------------------------------------------------------------- +SUB FASTCALL RestoreBackground(X as UByte, Y as UByte, backgroundDir as UInteger) +ASM + PROC + LOCAL loopC, loopD, branchC, branchD, nextC, nextD + ; A = X + pop de ; returnDir + 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 = screenDir where restoring will start + pop hl ; backgroundDir + exx + push de ; returnDir + 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,loopD ; jump if X is a multiple of 8 (unlikely) + ; continue if restoring 3 bytes per scanline +loopC: + 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,branchC ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + djnz loopC + ret +branchC: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextC ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextC: + djnz loopC + ret + +loopD: + 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,branchD ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + djnz loopD + ret +branchD: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextD ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextD: + djnz loopD + ret + ENDP +END ASM +END SUB + + +#endif + From d06a2856b3a655e5285cbdd12c2c125df9c15056 Mon Sep 17 00:00:00 2001 From: ConradoBadenas Date: Fri, 6 Mar 2026 18:52:24 +0100 Subject: [PATCH 102/169] stdlib.cb.maskedsprites --- .../arch/zxnext/stdlib/cb/maskedsprites.bas | 456 ++++++++++++++++++ 1 file changed, 456 insertions(+) create mode 100644 src/lib/arch/zxnext/stdlib/cb/maskedsprites.bas 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..8a2feb376 --- /dev/null +++ b/src/lib/arch/zxnext/stdlib/cb/maskedsprites.bas @@ -0,0 +1,456 @@ +' ---------------------------------------------------------------- +' 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 +' by Benjamín (a.k.a. RetrobenSoft) +' +' Print Masked (AND+OR) Sprites, version 2026.03.06 +' ---------------------------------------------------------------- + +#ifndef __CB_MASKEDSPRITES__ + +REM Avoid recursive / multiple inclusion + +#define __CB_MASKEDSPRITES__ + +#include +#include +#include + + +' ---------------------------------------------------------------- +' 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 +' ---------------------------------------------------------------- +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 hl,$5b5c ; BANKM system variable + ld a,(hl) ; Read BANKM + and %11110111 ; Reset bit3 + or c ; Set screen in bank5/7 + ld bc,$7ffd ; Memory Bank control port + di ; Disable interrupts + ld (hl),a ; Update BANKM system variable + out (c),a ; Set the screen + ei ; Enable interrupts +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 + RETURN ((PEEK $5b5c bAND %1000)>>2) bOR 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. +' ---------------------------------------------------------------- +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 CopyScreen5ToScreen7() + DIM b AS UByte + + b = GetBank() + if b<>7 then SetBank(7) + MemCopy($4000,$c000,$1b00) + if b<>7 then SetBank(b) +END SUB + + +' ---------------------------------------------------------------- +' Copy contents of screen7 to screen5 (display file + attribs) +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB CopyScreen7ToScreen5() + DIM b AS UByte + + b = GetBank() + if b<>7 then SetBank(7) + MemCopy($c000,$4000,$1b00) + if b<>7 then SetBank(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 = GetBank() + if b<>7 then SetBank(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 Dir where Background i begins +' +' ChangeMaskedSpritesBackgroundSet() changes the Set of Backgrounds +' Returns: +' Byte: new value of MaskedSpritesBackgroundSet (IYW to use it) +' ---------------------------------------------------------------- +dim MaskedSpritesBackgroundSet as Byte = 0 +#define MaskedSpritesBackground(i) (56064+((i)*2+MaskedSpritesBackgroundSet)*48) +FUNCTION FASTCALL ChangeMaskedSpritesBackgroundSet() as Byte + MaskedSpritesBackgroundSet = MaskedSpritesBackgroundSet bXOR 1 + RETURN MaskedSpritesBackgroundSet +END FUNCTION + + +' ---------------------------------------------------------------- +' 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: Dir where background will be saved +' UInteger: Dir where sprite image begins +' ---------------------------------------------------------------- +SUB FASTCALL SaveBackgroundAndDrawSprite(X as UByte, Y as UByte, backgroundDir as UInteger, spriteImageDir as UInteger) +ASM + PROC + LOCAL shiftsprite, spriteOK, loop0, loopA, loopB + LOCAL branchA, branchB, nextA, nextB + ; A = X + pop de ; returnDir + 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 = screenDir where drawing will start + ld a,c; ; A = X + and 7 + jr z,spriteOK ; jump if X is a multiple of 8 (unlikely) + ; continue if sprite must be shifted +shiftsprite: + pop bc ; backgroundDir + exx + pop hl ; spriteImageDir + push de ; returnDir + push ix + ld ixh,16 ; 16 scanlines + ld ixl,a ; IXl = X MOD 8 = 1,2,...,7 +loopB: + ld a,(hl) ; mask1 + inc hl + ld c,(hl) ; graph1 + inc hl + ld d,(hl) ; mask2 + inc hl + ld e,(hl) ; graph1 + inc hl + push hl ; spriteImageDir + + ld hl,$FF00 ; H = 255 , L = 0 + ld b,ixl +loop0: + scf + rra ; SCF + RRA injects a 1 in bit7 of A + rr d + rr h + srl c ; ShiftRightLogical injects a 0 in bit7 of C + rr e + rr l + djnz loop0 + 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,branchB ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + exx + pop hl ; spriteImageDir + dec ixh + jp nz,loopB + pop ix + ret +branchB: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextB ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextB: + exx + pop hl ; spriteImageDir + dec ixh + jp nz,loopB + pop ix + ret + +spriteOK: + pop bc ; backgroundDir + pop hl ; spriteImageDir + exx + push de ; returnDir + exx + push ix + ld ixh,16 ; 16 scanlines +loopA: + 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,branchA ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + dec ixh + jp nz,loopA + pop ix + ret +branchA: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextA ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextA: + dec ixh + jp nz,loopA + 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: Dir where saved background begins +' ---------------------------------------------------------------- +SUB FASTCALL RestoreBackground(X as UByte, Y as UByte, backgroundDir as UInteger) +ASM + PROC + LOCAL loopC, loopD, branchC, branchD, nextC, nextD + ; A = X + pop de ; returnDir + 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 = screenDir where restoring will start + pop hl ; backgroundDir + exx + push de ; returnDir + 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,loopD ; jump if X is a multiple of 8 (unlikely) + ; continue if restoring 3 bytes per scanline +loopC: + 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,branchC ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + djnz loopC + ret +branchC: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextC ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextC: + djnz loopC + ret + +loopD: + 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,branchD ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + djnz loopD + ret +branchD: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextD ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextD: + djnz loopD + ret + ENDP +END ASM +END SUB + + +#endif + From e44ce08051b8d1f22cea9659aecc6b12f85c5c5a Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Thu, 5 Mar 2026 23:36:17 +0100 Subject: [PATCH 103/169] dos: Improve DRAW document --- docs/draw.md | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) 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) From 469c2fa15688e943a36005045c697525d3d1976c Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 7 Mar 2026 00:10:21 +0100 Subject: [PATCH 104/169] refact: "merge" maskedpsrites[zxnext] into [zx48k] --- .../arch/zxnext/stdlib/cb/maskedsprites.bas | 454 +----------------- 1 file changed, 3 insertions(+), 451 deletions(-) diff --git a/src/lib/arch/zxnext/stdlib/cb/maskedsprites.bas b/src/lib/arch/zxnext/stdlib/cb/maskedsprites.bas index 8a2feb376..6099f7c56 100644 --- a/src/lib/arch/zxnext/stdlib/cb/maskedsprites.bas +++ b/src/lib/arch/zxnext/stdlib/cb/maskedsprites.bas @@ -1,456 +1,8 @@ ' ---------------------------------------------------------------- ' 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 -' by Benjamín (a.k.a. RetrobenSoft) -' -' Print Masked (AND+OR) Sprites, version 2026.03.06 -' ---------------------------------------------------------------- - -#ifndef __CB_MASKEDSPRITES__ - -REM Avoid recursive / multiple inclusion - -#define __CB_MASKEDSPRITES__ - -#include -#include -#include - - -' ---------------------------------------------------------------- -' 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 -' ---------------------------------------------------------------- -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 hl,$5b5c ; BANKM system variable - ld a,(hl) ; Read BANKM - and %11110111 ; Reset bit3 - or c ; Set screen in bank5/7 - ld bc,$7ffd ; Memory Bank control port - di ; Disable interrupts - ld (hl),a ; Update BANKM system variable - out (c),a ; Set the screen - ei ; Enable interrupts -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 - RETURN ((PEEK $5b5c bAND %1000)>>2) bOR 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. -' ---------------------------------------------------------------- -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 CopyScreen5ToScreen7() - DIM b AS UByte - - b = GetBank() - if b<>7 then SetBank(7) - MemCopy($4000,$c000,$1b00) - if b<>7 then SetBank(b) -END SUB - - -' ---------------------------------------------------------------- -' Copy contents of screen7 to screen5 (display file + attribs) -' Only works on 128K and compatible models. -' ---------------------------------------------------------------- -SUB CopyScreen7ToScreen5() - DIM b AS UByte - - b = GetBank() - if b<>7 then SetBank(7) - MemCopy($c000,$4000,$1b00) - if b<>7 then SetBank(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 - - +' Copyleft (k) 2008 +' by Jose Rodriguez-Rosa (a.k.a. Boriel) ' ---------------------------------------------------------------- -' 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 = GetBank() - if b<>7 then SetBank(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 Dir where Background i begins -' -' ChangeMaskedSpritesBackgroundSet() changes the Set of Backgrounds -' Returns: -' Byte: new value of MaskedSpritesBackgroundSet (IYW to use it) -' ---------------------------------------------------------------- -dim MaskedSpritesBackgroundSet as Byte = 0 -#define MaskedSpritesBackground(i) (56064+((i)*2+MaskedSpritesBackgroundSet)*48) -FUNCTION FASTCALL ChangeMaskedSpritesBackgroundSet() as Byte - MaskedSpritesBackgroundSet = MaskedSpritesBackgroundSet bXOR 1 - RETURN MaskedSpritesBackgroundSet -END FUNCTION - - -' ---------------------------------------------------------------- -' 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: Dir where background will be saved -' UInteger: Dir where sprite image begins -' ---------------------------------------------------------------- -SUB FASTCALL SaveBackgroundAndDrawSprite(X as UByte, Y as UByte, backgroundDir as UInteger, spriteImageDir as UInteger) -ASM - PROC - LOCAL shiftsprite, spriteOK, loop0, loopA, loopB - LOCAL branchA, branchB, nextA, nextB - ; A = X - pop de ; returnDir - 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 = screenDir where drawing will start - ld a,c; ; A = X - and 7 - jr z,spriteOK ; jump if X is a multiple of 8 (unlikely) - ; continue if sprite must be shifted -shiftsprite: - pop bc ; backgroundDir - exx - pop hl ; spriteImageDir - push de ; returnDir - push ix - ld ixh,16 ; 16 scanlines - ld ixl,a ; IXl = X MOD 8 = 1,2,...,7 -loopB: - ld a,(hl) ; mask1 - inc hl - ld c,(hl) ; graph1 - inc hl - ld d,(hl) ; mask2 - inc hl - ld e,(hl) ; graph1 - inc hl - push hl ; spriteImageDir - - ld hl,$FF00 ; H = 255 , L = 0 - ld b,ixl -loop0: - scf - rra ; SCF + RRA injects a 1 in bit7 of A - rr d - rr h - srl c ; ShiftRightLogical injects a 0 in bit7 of C - rr e - rr l - djnz loop0 - 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,branchB ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) - exx - pop hl ; spriteImageDir - dec ixh - jp nz,loopB - pop ix - ret -branchB: - ld a,e - add a,32 ; for 1 out of 8 values of D - ld e,a - jr c,nextB ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) - ld a,d - sub 8 - ld d,a -nextB: - exx - pop hl ; spriteImageDir - dec ixh - jp nz,loopB - pop ix - ret - -spriteOK: - pop bc ; backgroundDir - pop hl ; spriteImageDir - exx - push de ; returnDir - exx - push ix - ld ixh,16 ; 16 scanlines -loopA: - 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,branchA ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) - dec ixh - jp nz,loopA - pop ix - ret -branchA: - ld a,e - add a,32 ; for 1 out of 8 values of D - ld e,a - jr c,nextA ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) - ld a,d - sub 8 - ld d,a -nextA: - dec ixh - jp nz,loopA - 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: Dir where saved background begins -' ---------------------------------------------------------------- -SUB FASTCALL RestoreBackground(X as UByte, Y as UByte, backgroundDir as UInteger) -ASM - PROC - LOCAL loopC, loopD, branchC, branchD, nextC, nextD - ; A = X - pop de ; returnDir - 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 = screenDir where restoring will start - pop hl ; backgroundDir - exx - push de ; returnDir - 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,loopD ; jump if X is a multiple of 8 (unlikely) - ; continue if restoring 3 bytes per scanline -loopC: - 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,branchC ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) - djnz loopC - ret -branchC: - ld a,e - add a,32 ; for 1 out of 8 values of D - ld e,a - jr c,nextC ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) - ld a,d - sub 8 - ld d,a -nextC: - djnz loopC - ret - -loopD: - 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,branchD ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) - djnz loopD - ret -branchD: - ld a,e - add a,32 ; for 1 out of 8 values of D - ld e,a - jr c,nextD ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) - ld a,d - sub 8 - ld d,a -nextD: - djnz loopD - ret - ENDP -END ASM -END SUB - - -#endif +#include once [arch:zx48k] From 6335d08d530364edb382c5aecd040d8343d3d6f4 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Tue, 10 Mar 2026 00:11:33 +0100 Subject: [PATCH 105/169] fix: bxor16 with 0xFFFF was wrongly optimized. --- src/arch/z80/backend/_16bit.py | 5 ----- tests/functional/arch/zx48k/bxor16.asm | 25 +++++-------------------- 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/arch/z80/backend/_16bit.py b/src/arch/z80/backend/_16bit.py index f1086ffe3..7df6dc1d9 100644 --- a/src/arch/z80/backend/_16bit.py +++ b/src/arch/z80/backend/_16bit.py @@ -752,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/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 From f89b79488144ea459417d0822ee0e17b5814556b Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 22 Mar 2026 13:44:06 +0100 Subject: [PATCH 106/169] fix: Do not insert dummy ld hl, 0 in Fastcall String functions --- src/api/optimize.py | 2 +- tests/functional/arch/zx48k/stdlib_hex.asm | 25 +++--- tests/functional/arch/zxnext/stdlib_hex.asm | 95 +++++++++++++++++---- 3 files changed, 92 insertions(+), 30 deletions(-) diff --git a/src/api/optimize.py b/src/api/optimize.py index 41bc2442f..ed786e08f 100644 --- a/src/api/optimize.py +++ b/src/api/optimize.py @@ -99,7 +99,7 @@ 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 self.generic_visit(node) 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/zxnext/stdlib_hex.asm b/tests/functional/arch/zxnext/stdlib_hex.asm index 6089f0518..3237cabb7 100644 --- a/tests/functional/arch/zxnext/stdlib_hex.asm +++ b/tests/functional/arch/zxnext/stdlib_hex.asm @@ -31,7 +31,7 @@ ei ret _hex: -#line 30 "/zxbasic/src/lib/arch/zxnext/stdlib/hex.bas" +#line 30 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" push namespace core PROC LOCAL SUB_CHAR @@ -83,10 +83,7 @@ END_CHAR: ret ENDP pop namespace -#line 91 "/zxbasic/src/lib/arch/zxnext/stdlib/hex.bas" -#line 30 "/zxbasic/src/lib/arch/zxnext/stdlib/hex.bas" - ld hl, 0 -#line 33 "/zxbasic/src/lib/arch/zxnext/stdlib/hex.bas" +#line 91 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" _hex__leave: ret _hex16: @@ -169,6 +166,17 @@ _hex8__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 +#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,6 +237,13 @@ _hex8__leave: ; 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 @@ -262,8 +277,16 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 69 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +#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) @@ -370,7 +393,8 @@ __MEM_INIT2: ret ENDP pop namespace -#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +#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. @@ -401,9 +425,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/mem/alloc.asm" +#line 113 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ret z ; NULL -#line 115 "/zxbasic/src/lib/arch/zxnext/runtime/mem/alloc.asm" +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" ; HL = Pointer to Free block ld e, (hl) inc hl @@ -468,8 +492,16 @@ __MEM_SUBTRACT: ret ENDP pop namespace -#line 116 "/zxbasic/src/lib/arch/zxnext/stdlib/hex.bas" +#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) @@ -627,8 +659,16 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ret ENDP pop namespace -#line 117 "/zxbasic/src/lib/arch/zxnext/stdlib/hex.bas" +#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 @@ -636,6 +676,13 @@ __MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed ; 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 @@ -668,7 +715,8 @@ __STORE_STR2: 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" +#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 @@ -676,8 +724,16 @@ __PSTORE_STR2: add hl, bc jp __STORE_STR2 pop namespace -#line 118 "/zxbasic/src/lib/arch/zxnext/stdlib/hex.bas" +#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 @@ -692,6 +748,13 @@ __PSTORE_STR2: ; 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 @@ -706,7 +769,8 @@ __STRLEN: ; Direct FASTCALL entry ld l, a ret pop namespace -#line 18 "/zxbasic/src/lib/arch/zxnext/runtime/strslice.asm" +#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 @@ -778,5 +842,6 @@ __FREE_ON_EXIT: ret ENDP pop namespace -#line 119 "/zxbasic/src/lib/arch/zxnext/stdlib/hex.bas" +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/strslice.asm" +#line 177 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" END From 4c0c617e8442bdb1137ed9c1154f1ac792430975 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 16:18:31 +0000 Subject: [PATCH 107/169] build(deps): bump requests from 2.32.4 to 2.33.0 in /docs Bumps [requests](https://github.com/psf/requests) from 2.32.4 to 2.33.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.32.4...v2.33.0) --- updated-dependencies: - dependency-name: requests dependency-version: 2.33.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 1a342c598..f66aa2c97 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -25,7 +25,7 @@ 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.6.3 watchdog==6.0.0 From 056c5b8dbf8db34806825b35f3a4d1e04f31ddd5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 19:24:32 +0000 Subject: [PATCH 108/169] build(deps): bump pygments from 2.18.0 to 2.20.0 Bumps [pygments](https://github.com/pygments/pygments) from 2.18.0 to 2.20.0. - [Release notes](https://github.com/pygments/pygments/releases) - [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES) - [Commits](https://github.com/pygments/pygments/compare/2.18.0...2.20.0) --- updated-dependencies: - dependency-name: pygments dependency-version: 2.20.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index f66aa2c97..240737565 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -19,7 +19,7 @@ packaging==24.2 paginate==0.5.7 pathspec==0.12.1 platformdirs==4.3.6 -Pygments==2.18.0 +Pygments==2.20.0 pymdown-extensions==10.16.1 python-dateutil==2.9.0.post0 PyYAML==6.0.2 From f252ab6f158ed4ed48b6d255210c76fae43693ba Mon Sep 17 00:00:00 2001 From: ConradoBadenas Date: Sun, 5 Apr 2026 20:38:54 +0200 Subject: [PATCH 109/169] stdlib.cb.maskedsprites --- .../arch/zx48k/stdlib/cb/maskedsprites.bas | 1084 +++++++++++++++-- 1 file changed, 967 insertions(+), 117 deletions(-) diff --git a/src/lib/arch/zx48k/stdlib/cb/maskedsprites.bas b/src/lib/arch/zx48k/stdlib/cb/maskedsprites.bas index 8a2feb376..27f390869 100644 --- a/src/lib/arch/zx48k/stdlib/cb/maskedsprites.bas +++ b/src/lib/arch/zx48k/stdlib/cb/maskedsprites.bas @@ -4,13 +4,13 @@ ' 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), +' 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 -' by Benjamín (a.k.a. RetrobenSoft) +' 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.03.06 +' Print Masked (AND+OR) Sprites, version 2026.04.05 ' ---------------------------------------------------------------- #ifndef __CB_MASKEDSPRITES__ @@ -19,17 +19,119 @@ REM Avoid recursive / multiple inclusion #define __CB_MASKEDSPRITES__ -#include #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 @@ -38,15 +140,10 @@ ASM rla rla ; bit3 = 0/1 for bank5/7 ld c,a - ld hl,$5b5c ; BANKM system variable - ld a,(hl) ; Read BANKM + ld a,($5b5c) ; Read BANKM system variable and %11110111 ; Reset bit3 or c ; Set screen in bank5/7 - ld bc,$7ffd ; Memory Bank control port - di ; Disable interrupts - ld (hl),a ; Update BANKM system variable - out (c),a ; Set the screen - ei ; Enable interrupts + MaskedSprites_OUT_7FFD END ASM END SUB @@ -59,7 +156,13 @@ END SUB ' UByte: bank 5 or 7 ' ---------------------------------------------------------------- FUNCTION FASTCALL GetVisibleScreen() AS UByte - RETURN ((PEEK $5b5c bAND %1000)>>2) bOR 5 +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 @@ -78,13 +181,13 @@ END SUB ' Copy contents of screen5 to screen7 (display file + attribs) ' Only works on 128K and compatible models. ' ---------------------------------------------------------------- -SUB CopyScreen5ToScreen7() +SUB FASTCALL CopyScreen5ToScreen7() DIM b AS UByte - b = GetBank() - if b<>7 then SetBank(7) + b = GetBankPreservingRegs() + if b<>7 then SetBankPreservingINTs(7) MemCopy($4000,$c000,$1b00) - if b<>7 then SetBank(b) + if b<>7 then SetBankPreservingINTs(b) END SUB @@ -92,13 +195,13 @@ END SUB ' Copy contents of screen7 to screen5 (display file + attribs) ' Only works on 128K and compatible models. ' ---------------------------------------------------------------- -SUB CopyScreen7ToScreen5() +SUB FASTCALL CopyScreen7ToScreen5() DIM b AS UByte - b = GetBank() - if b<>7 then SetBank(7) + b = GetBankPreservingRegs() + if b<>7 then SetBankPreservingINTs(7) MemCopy($c000,$4000,$1b00) - if b<>7 then SetBank(b) + if b<>7 then SetBankPreservingINTs(b) END SUB @@ -123,8 +226,8 @@ END SUB FUNCTION FASTCALL SetDrawingScreen7() AS UByte DIM b AS UByte - b = GetBank() - if b<>7 then SetBank(7) + 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 @@ -146,35 +249,61 @@ END SUB ' ---------------------------------------------------------------- ' MaskedSpritesBackgroundSet = 0 or 1 is the Set of Backgrounds ' -' MaskedSpritesBackground(i) is the Dir where Background i begins +' 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 Byte = 0 -#define MaskedSpritesBackground(i) (56064+((i)*2+MaskedSpritesBackgroundSet)*48) -FUNCTION FASTCALL ChangeMaskedSpritesBackgroundSet() as Byte +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: Dir where background will be saved -' UInteger: Dir where sprite image begins +' UInteger: Address where background will be saved +' UInteger: Address where sprite image begins ' ---------------------------------------------------------------- -SUB FASTCALL SaveBackgroundAndDrawSprite(X as UByte, Y as UByte, backgroundDir as UInteger, spriteImageDir as UInteger) +SUB FASTCALL SaveBackgroundAndDrawSprite(X AS UByte, Y AS UByte, backgroundAddr AS UInteger, spriteImageAddr AS UInteger) ASM PROC - LOCAL shiftsprite, spriteOK, loop0, loopA, loopB - LOCAL branchA, branchB, nextA, nextB + LOCAL shiftright, shiftleft, noshift + LOCAL loopSR, loopSL, loopNS, loopR, loopL, branchSR, branchSL, branchNS ; A = X - pop de ; returnDir + pop de ; returnAddr exx pop bc ; B = Y ld c,a ; C = X @@ -200,41 +329,44 @@ ASM ; END code from https://skoolkid.github.io/rom/asm/22AA.html ld hl,(.core.SCREEN_ADDR) add hl,de - ex de,hl ; DE = screenDir where drawing will start + ex de,hl ; DE = screenAddr where drawing will start ld a,c; ; A = X and 7 - jr z,spriteOK ; jump if X is a multiple of 8 (unlikely) + jr z,noshift ; jump if X is a multiple of 8 (unlikely) ; continue if sprite must be shifted -shiftsprite: - pop bc ; backgroundDir + 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 ; spriteImageDir - push de ; returnDir + pop hl ; spriteImageAddr + push de ; returnAddr push ix ld ixh,16 ; 16 scanlines - ld ixl,a ; IXl = X MOD 8 = 1,2,...,7 -loopB: + 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) ; graph1 + ld e,(hl) ; graph2 inc hl - push hl ; spriteImageDir + push hl ; spriteImageAddr ld hl,$FF00 ; H = 255 , L = 0 ld b,ixl -loop0: - scf - rra ; SCF + RRA injects a 1 in bit7 of A - rr d - rr h - srl c ; ShiftRightLogical injects a 0 in bit7 of C - rr e - rr l - djnz loop0 +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 @@ -272,38 +404,31 @@ loop0: inc d ld a,d and 7 - jr z,branchB ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + jr z,branchSR ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) exx - pop hl ; spriteImageDir + pop hl ; spriteImageAddr dec ixh - jp nz,loopB + jp nz,loopSR pop ix ret -branchB: - ld a,e - add a,32 ; for 1 out of 8 values of D - ld e,a - jr c,nextB ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) - ld a,d - sub 8 - ld d,a -nextB: +branchSR: + MaskedSprites_NEXT_ROW exx - pop hl ; spriteImageDir + pop hl ; spriteImageAddr dec ixh - jp nz,loopB + jp nz,loopSR pop ix ret -spriteOK: - pop bc ; backgroundDir - pop hl ; spriteImageDir +noshift: + pop bc ; backgroundAddr + pop hl ; spriteImageAddr exx - push de ; returnDir + push de ; returnAddr exx push ix ld ixh,16 ; 16 scanlines -loopA: +loopNS: ld a,(de) ; screen ld (bc),a; ; save inc bc @@ -327,22 +452,99 @@ loopA: inc d ld a,d and 7 - jr z,branchA ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + 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,loopA + jp nz,loopNS pop ix ret -branchA: - ld a,e - add a,32 ; for 1 out of 8 values of D - ld e,a - jr c,nextA ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + +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 - sub 8 - ld d,a -nextA: + 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,loopA + jp nz,loopSL pop ix ret ENDP @@ -355,14 +557,14 @@ END SUB ' Parameters: ' UByte: X coordinate (0:left to 240:right) ' UByte: Y coordinate (0:up to 176:down) -' UInteger: Dir where saved background begins +' UInteger: Address where saved background begins ' ---------------------------------------------------------------- -SUB FASTCALL RestoreBackground(X as UByte, Y as UByte, backgroundDir as UInteger) +SUB FASTCALL RestoreBackground(X AS UByte, Y AS UByte, backgroundAddr AS UInteger) ASM PROC - LOCAL loopC, loopD, branchC, branchD, nextC, nextD + LOCAL loop2b, loop3b, branch2b, branch3b ; A = X - pop de ; returnDir + pop de ; returnAddr exx pop bc ; B = Y ld c,a ; C = X @@ -388,17 +590,18 @@ ASM ; END code from https://skoolkid.github.io/rom/asm/22AA.html ld hl,(.core.SCREEN_ADDR) add hl,de - ex de,hl ; DE = screenDir where restoring will start - pop hl ; backgroundDir + ex de,hl ; DE = screenAddr where restoring will start + pop hl ; backgroundAddr exx - push de ; returnDir + 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,loopD ; jump if X is a multiple of 8 (unlikely) + jr z,loop2b ; jump if X is a multiple of 8 (unlikely) ; continue if restoring 3 bytes per scanline -loopC: +; 3bytes per scanline +loop3b: ldi ; 16 Ts vs 7+7+6+4=24 Ts ldi ldi ; 3 bytes background restored to screen @@ -409,22 +612,15 @@ loopC: inc d ld a,d and 7 - jr z,branchC ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) - djnz loopC + jr z,branch3b ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + djnz loop3b ret -branchC: - ld a,e - add a,32 ; for 1 out of 8 values of D - ld e,a - jr c,nextC ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) - ld a,d - sub 8 - ld d,a -nextC: - djnz loopC +branch3b: + MaskedSprites_NEXT_ROW + djnz loop3b ret - -loopD: +; 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... @@ -433,19 +629,673 @@ loopD: inc d ld a,d and 7 - jr z,branchD ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) - djnz loopD + 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 -branchD: - ld a,e - add a,32 ; for 1 out of 8 values of D - ld e,a - jr c,nextD ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) +#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 - sub 8 - ld d,a -nextD: - djnz loopD + 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 From 6cc9afd47781671e055a5f9df9ede3b876f471e9 Mon Sep 17 00:00:00 2001 From: ConradoBadenas Date: Sun, 5 Apr 2026 20:41:00 +0200 Subject: [PATCH 110/169] examples.maskedsprites --- examples/maskedsprites.bas | 737 ++++++++++++++++++++++++++++++++++--- 1 file changed, 680 insertions(+), 57 deletions(-) diff --git a/examples/maskedsprites.bas b/examples/maskedsprites.bas index a9e0d45b5..dbf80a092 100644 --- a/examples/maskedsprites.bas +++ b/examples/maskedsprites.bas @@ -4,74 +4,215 @@ ' Copyright (C) 2026 Conrado Badenas ' ' Example for -' Print Masked (AND+OR) Sprites, version 2026.03.06 +' Print Masked (AND+OR) Sprites, version 2026.04.05 ' ---------------------------------------------------------------- -#include +' 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 NUMMAXSPRITES as UByte = 10 -CONST NUMSPRITES as UByte = 7 -if NUMSPRITES>NUMMAXSPRITES then stop +CONST NUMSPRITES as UByte = NumberofMaskedSprites -dim i,temp as Integer -dim pointx1,pointx2,pointy1,pointy2 as Integer 'for subtractions +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 -SetVisibleScreen(5):if GetVisibleScreen()<>5 then STOP +'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 127 +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 -CopyScreen5ToScreen7() -'print at 11,8;"Esto es Screen5" -SetDrawingScreen7() 'Bank7 is set at $c000 -'print at 13,8;"Esto es Screen7" -SetDrawingScreen5() 'Bank7 is still set at $c000 -'print at 15,8;"Esto es Screen5" +#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 -ASM - xor a ; 4 Ts reseteamos el contador - ld (23672),a ; 13 Ts de FRAMES a 0 -END ASM + 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 -' Empezamos con VisibleScreen = DrawingScreen = 5 -bucle: +for i=0 to NUMSPRITES-1 + back(i)=MaskedSpritesBackground(i) +' print back(i) +next i +' print:pause 0 - ' La nueva VisibleScreen está lista para que la ULA la saque por la tele - ' Pero vamos a esperar al principio de un frame para que no haya tearing - ' El 2 de luego significa que obtendremos 25 FPS como máximo - ASM - ld hl,23672 -wait: - ld a,(hl) ; A = contador de FRAMES - cp 2 ; 25 FPS como máximo - jr c,wait ; repetimos si A < 2 - xor a - ld (hl),a ; reseteamos el contador de FRAMES a 0 - END ASM +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 - ToggleVisibleScreen() - ' Ahora se puede modificar la DrawingScreen porque no está Visible + 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 - ' Restauramos fondo en x0,y0 si está guardado en MaskedSpritesBackground - for i=0 to NUMSPRITES-1 - if y0(i)<>255 then RestoreBackground(x0(i),y0(i),MaskedSpritesBackground(i)) +' 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 - ' Actualizamos x0,y0 y calculamos nuevos x1,y1 + ' We update x0,y0 and compute new x1,y1 for i=1 to NUMSPRITES-1 temp=x1(i): x0(i)=temp temp=temp-1 @@ -85,7 +226,7 @@ wait: y1(i)=temp next i - 'Código especial para el prota + '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 @@ -98,28 +239,109 @@ wait: if temp>176 then temp=176 y1(0)=temp - ' Guardamos fondo en MaskedSpritesBackground y dibujamos sprites en x,y - for i=NUMSPRITES-1 to 1 step -1 - SaveBackgroundAndDrawSprite(x1(i),y1(i),MaskedSpritesBackground(i),@enemigo0) - next i - SaveBackgroundAndDrawSprite(x1(0),y1(0),MaskedSpritesBackground(0),@prota0) + ' 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 - ' Cambiamos el Set de Backgrounds para la siguiente iteración - ChangeMaskedSpritesBackgroundSet() +#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 - ' Hemos terminado de dibujar - ' DrawingScreen actual está lista para ser mostrada ahora mismo - ' Cambiamos de DrawingScreen para el siguiente ciclo de dibujo - ToggleDrawingScreen() + ' After restoring background in a Spectrum 48K, you should do NOTHING + ' ... + ' You have ABSOLUTELY NO TIME to do anything in a Spectrum 48K - ' Ahora VisibleScreen = DrawingScreen y - ' lo que se dibuje se verá mientras se dibuja + ' 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 -goto bucle + ' 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 -stop + ' We change the Set of Backgrounds (128K) for next iteration + if memoryPaging then ChangeMaskedSpritesBackgroundSet() -prota0: + ' 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 @@ -139,7 +361,7 @@ ASM defb %11000000,%00000000,%00001111,%00000000;1 END ASM -enemigo0: +foe00: ASM defb %00000011,%00000000,%11000000,%00000000;1 defb %00000011,%01111000,%11000000,%00011110;2 @@ -159,3 +381,404 @@ ASM 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 + From 5ecc454b89804b7bcb85d27d8c3a7a77fe697c51 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 11 Apr 2026 18:21:04 +0200 Subject: [PATCH 111/169] fix: Correctly initalize LBOUND and UBOUND pts for local arrays Even if they are not accessed, they must be enabled if --debug-array is used. --- src/arch/z80/visitor/function_translator.py | 12 +- src/arch/z80/visitor/translator_visitor.py | 9 +- .../arch/zx48k/local_array_with_bounds2.asm | 917 ++++++++++++++++++ .../arch/zx48k/local_array_with_bounds2.bas | 15 + 4 files changed, 944 insertions(+), 9 deletions(-) create mode 100644 tests/functional/arch/zx48k/local_array_with_bounds2.asm create mode 100644 tests/functional/arch/zx48k/local_array_with_bounds2.bas diff --git a/src/arch/z80/visitor/function_translator.py b/src/arch/z80/visitor/function_translator.py index fc790f522..26257203e 100644 --- a/src/arch/z80/visitor/function_translator.py +++ b/src/arch/z80/visitor/function_translator.py @@ -69,21 +69,19 @@ def visit_FUNCTION(self, node): 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 or local_var.lbound_used + 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"] - bound_ptrs = [lbound_label if lbound_needed else "0", "0"] - if local_var.ubound_used: - bound_ptrs[1] = ubound_label - - 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)) diff --git a/src/arch/z80/visitor/translator_visitor.py b/src/arch/z80/visitor/translator_visitor.py index 331ec4aa9..6cb8790f6 100644 --- a/src/arch/z80/visitor/translator_visitor.py +++ b/src/arch/z80/visitor/translator_visitor.py @@ -128,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 @@ -254,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/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 From 8d8d53577cbf3e4f15c9e59aded7ca2e81bc32e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 02:52:33 +0000 Subject: [PATCH 112/169] build(deps-dev): bump pytest from 9.0.2 to 9.0.3 Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to 9.0.3. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3) --- updated-dependencies: - dependency-name: pytest dependency-version: 9.0.3 dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- poetry.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 947ceba7f..edcded4ab 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "bump2version" @@ -46,7 +46,7 @@ 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 = "sys_platform == \"win32\" or platform_system == \"Windows\"" +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"}, @@ -736,14 +736,14 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "9.0.2" +version = "9.0.3" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b"}, - {file = "pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11"}, + {file = "pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9"}, + {file = "pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c"}, ] [package.dependencies] From 743ae865babf61dade6ccfaa59aa85b025ce8546 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Thu, 16 Apr 2026 23:54:04 +0200 Subject: [PATCH 113/169] fix: Fixes zxbasm options --- src/zxbasm/zxbasm.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/zxbasm/zxbasm.py b/src/zxbasm/zxbasm.py index c57396a1b..8ee1451c6 100755 --- a/src/zxbasm/zxbasm.py +++ b/src/zxbasm/zxbasm.py @@ -169,12 +169,8 @@ 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 From aabe840702e47cdeb3dc7346ef5afc7e93d1a2dd Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 1 May 2026 12:37:42 +0200 Subject: [PATCH 114/169] fix: Access to array elem by ref broken The access byRef to an array element with no constant address was broken because it was being considered constant always. --- src/arch/z80/visitor/translator.py | 4 +- .../arch/zx48k/opt2_arr_elem_by_ref04.asm | 246 ++++++++++++++++++ .../arch/zx48k/opt2_arr_elem_by_ref04.bas | 11 + 3 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 tests/functional/arch/zx48k/opt2_arr_elem_by_ref04.asm create mode 100644 tests/functional/arch/zx48k/opt2_arr_elem_by_ref04.bas diff --git a/src/arch/z80/visitor/translator.py b/src/arch/z80/visitor/translator.py index d6eeddc5c..66725c0d0 100644 --- a/src/arch/z80/visitor/translator.py +++ b/src/arch/z80/visitor/translator.py @@ -245,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)), 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) + + From e47f6ec219dda72f2da7f253c61a1cc1bf304a88 Mon Sep 17 00:00:00 2001 From: "Oleg S. Kostenko" Date: Sun, 3 May 2026 20:45:30 +0300 Subject: [PATCH 115/169] Add Play library --- src/lib/arch/zx48k/stdlib/play.bas | 493 +++++++++++++++++++++++++++++ 1 file changed, 493 insertions(+) create mode 100644 src/lib/arch/zx48k/stdlib/play.bas diff --git a/src/lib/arch/zx48k/stdlib/play.bas b/src/lib/arch/zx48k/stdlib/play.bas new file mode 100644 index 000000000..61da99e1a --- /dev/null +++ b/src/lib/arch/zx48k/stdlib/play.bas @@ -0,0 +1,493 @@ +' ---------------------------------------------------------------------- +' 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 +' - 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, 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 + 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 _ +} + +' 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 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 = 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 + + ' 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 + + asm + ei + end asm +end sub + +#pragma pop(explicit) +#pragma pop(strict) From 7707e2ca554524f9c94290153c69fe995e38ab3b Mon Sep 17 00:00:00 2001 From: "Oleg S. Kostenko" Date: Sun, 3 May 2026 21:00:49 +0300 Subject: [PATCH 116/169] Add Play examples --- examples/play/chaos.bas | 24 ++++++++++++++++++++++++ examples/play/evolution.bas | 26 ++++++++++++++++++++++++++ examples/play/readme.md | 4 ++++ 3 files changed, 54 insertions(+) create mode 100644 examples/play/chaos.bas create mode 100644 examples/play/evolution.bas create mode 100644 examples/play/readme.md diff --git a/examples/play/chaos.bas b/examples/play/chaos.bas new file mode 100644 index 000000000..a9da8bf33 --- /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 V8dV7dV6dV5d V8cV7cV6cV5c V8cV7cV6cV5c V8dV7dV6dV5d 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..5affa4498 --- /dev/null +++ b/examples/play/evolution.bas @@ -0,0 +1,26 @@ +rem Evolution +rem (C) 2026 by Ollibony + +#include + +cls + +let z$ = "T160 O3 1e&&Ee&e&&&e&&&e& 1d&&Dd&d&&&d&&&d& 1c&&Cc&c&&&c&&&c& O2 1a&&Aa&a&&&a&&&a& " +let x$ = "O3 1e&&Ee&e&&&e&&&e& 1g&&Gg&g&&&d&4d1& 1c&&Cc&c&&&c&&&c& O2 1a&&Aa&a&&&a&a&a& O5 3g" + +let y$ = "O5 1gab&&&ab&&e&&&d& 1gab&&&ab&&e&&&d& 1abC&&&bC&&e&&&d& 1ega&5&&& " +let w$ = "O5 1gab&&&ab&&e&&&d& 1gab&&&ab&&e&&&d& 1abC&&&bC&&e&&&d& 1ega&5&&& 3e" + +let c$ = "O5 9&&&&& 1bCD&&&CD&&b&&&g& 1CDE&&&DE&&C&&&g& 1Cba&5&&& O4 3b" + +let a$ = z$ + x$ +let b$ = y$ + w$ + +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/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. From bbe6a4bebcba4fcef2f2c4cb8657a4c5ea3f3d54 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Thu, 7 May 2026 00:31:12 +0200 Subject: [PATCH 117/169] fix: add missing parse rule in assembler --- poetry.lock | 624 +++++++++++++++------------- src/api/utils.py | 4 +- src/parsetab/tabs.dbm.bak | 6 +- src/parsetab/tabs.dbm.dat | Bin 1205950 -> 1207998 bytes src/parsetab/tabs.dbm.dir | 6 +- src/zxbasm/asmparse.py | 4 +- tests/functional/asm/ld_hl_expr.asm | 5 + tests/functional/asm/ld_hl_expr.bin | 1 + 8 files changed, 353 insertions(+), 297 deletions(-) create mode 100644 tests/functional/asm/ld_hl_expr.asm create mode 100644 tests/functional/asm/ld_hl_expr.bin diff --git a/poetry.lock b/poetry.lock index edcded4ab..f8fb948fc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.4.0 and should not be changed by hand. [[package]] name = "bump2version" @@ -26,14 +26,14 @@ files = [ [[package]] name = "click" -version = "8.3.1" +version = "8.3.3" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"}, - {file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"}, + {file = "click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613"}, + {file = "click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2"}, ] [package.dependencies] @@ -46,7 +46,7 @@ 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\"" +markers = "sys_platform == \"win32\" or platform_system == \"Windows\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -54,104 +54,118 @@ files = [ [[package]] name = "coverage" -version = "7.13.1" +version = "7.13.5" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "coverage-7.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1fa280b3ad78eea5be86f94f461c04943d942697e0dac889fa18fff8f5f9147"}, - {file = "coverage-7.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c3d8c679607220979434f494b139dfb00131ebf70bb406553d69c1ff01a5c33d"}, - {file = "coverage-7.13.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:339dc63b3eba969067b00f41f15ad161bf2946613156fb131266d8debc8e44d0"}, - {file = "coverage-7.13.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:db622b999ffe49cb891f2fff3b340cdc2f9797d01a0a202a0973ba2562501d90"}, - {file = "coverage-7.13.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1443ba9acbb593fa7c1c29e011d7c9761545fe35e7652e85ce7f51a16f7e08d"}, - {file = "coverage-7.13.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c832ec92c4499ac463186af72f9ed4d8daec15499b16f0a879b0d1c8e5cf4a3b"}, - {file = "coverage-7.13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:562ec27dfa3f311e0db1ba243ec6e5f6ab96b1edfcfc6cf86f28038bc4961ce6"}, - {file = "coverage-7.13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4de84e71173d4dada2897e5a0e1b7877e5eefbfe0d6a44edee6ce31d9b8ec09e"}, - {file = "coverage-7.13.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:a5a68357f686f8c4d527a2dc04f52e669c2fc1cbde38f6f7eb6a0e58cbd17cae"}, - {file = "coverage-7.13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:77cc258aeb29a3417062758975521eae60af6f79e930d6993555eeac6a8eac29"}, - {file = "coverage-7.13.1-cp310-cp310-win32.whl", hash = "sha256:bb4f8c3c9a9f34423dba193f241f617b08ffc63e27f67159f60ae6baf2dcfe0f"}, - {file = "coverage-7.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:c8e2706ceb622bc63bac98ebb10ef5da80ed70fbd8a7999a5076de3afaef0fb1"}, - {file = "coverage-7.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a55d509a1dc5a5b708b5dad3b5334e07a16ad4c2185e27b40e4dba796ab7f88"}, - {file = "coverage-7.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d010d080c4888371033baab27e47c9df7d6fb28d0b7b7adf85a4a49be9298b3"}, - {file = "coverage-7.13.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d938b4a840fb1523b9dfbbb454f652967f18e197569c32266d4d13f37244c3d9"}, - {file = "coverage-7.13.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bf100a3288f9bb7f919b87eb84f87101e197535b9bd0e2c2b5b3179633324fee"}, - {file = "coverage-7.13.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef6688db9bf91ba111ae734ba6ef1a063304a881749726e0d3575f5c10a9facf"}, - {file = "coverage-7.13.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0b609fc9cdbd1f02e51f67f51e5aee60a841ef58a68d00d5ee2c0faf357481a3"}, - {file = "coverage-7.13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c43257717611ff5e9a1d79dce8e47566235ebda63328718d9b65dd640bc832ef"}, - {file = "coverage-7.13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e09fbecc007f7b6afdfb3b07ce5bd9f8494b6856dd4f577d26c66c391b829851"}, - {file = "coverage-7.13.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:a03a4f3a19a189919c7055098790285cc5c5b0b3976f8d227aea39dbf9f8bfdb"}, - {file = "coverage-7.13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3820778ea1387c2b6a818caec01c63adc5b3750211af6447e8dcfb9b6f08dbba"}, - {file = "coverage-7.13.1-cp311-cp311-win32.whl", hash = "sha256:ff10896fa55167371960c5908150b434b71c876dfab97b69478f22c8b445ea19"}, - {file = "coverage-7.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:a998cc0aeeea4c6d5622a3754da5a493055d2d95186bad877b0a34ea6e6dbe0a"}, - {file = "coverage-7.13.1-cp311-cp311-win_arm64.whl", hash = "sha256:fea07c1a39a22614acb762e3fbbb4011f65eedafcb2948feeef641ac78b4ee5c"}, - {file = "coverage-7.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6f34591000f06e62085b1865c9bc5f7858df748834662a51edadfd2c3bfe0dd3"}, - {file = "coverage-7.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b67e47c5595b9224599016e333f5ec25392597a89d5744658f837d204e16c63e"}, - {file = "coverage-7.13.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e7b8bd70c48ffb28461ebe092c2345536fb18bbbf19d287c8913699735f505c"}, - {file = "coverage-7.13.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c223d078112e90dc0e5c4e35b98b9584164bea9fbbd221c0b21c5241f6d51b62"}, - {file = "coverage-7.13.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:794f7c05af0763b1bbd1b9e6eff0e52ad068be3b12cd96c87de037b01390c968"}, - {file = "coverage-7.13.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0642eae483cc8c2902e4af7298bf886d605e80f26382124cddc3967c2a3df09e"}, - {file = "coverage-7.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f5e772ed5fef25b3de9f2008fe67b92d46831bd2bc5bdc5dd6bfd06b83b316f"}, - {file = "coverage-7.13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:45980ea19277dc0a579e432aef6a504fe098ef3a9032ead15e446eb0f1191aee"}, - {file = "coverage-7.13.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e4f18eca6028ffa62adbd185a8f1e1dd242f2e68164dba5c2b74a5204850b4cf"}, - {file = "coverage-7.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8dca5590fec7a89ed6826fce625595279e586ead52e9e958d3237821fbc750c"}, - {file = "coverage-7.13.1-cp312-cp312-win32.whl", hash = "sha256:ff86d4e85188bba72cfb876df3e11fa243439882c55957184af44a35bd5880b7"}, - {file = "coverage-7.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:16cc1da46c04fb0fb128b4dc430b78fa2aba8a6c0c9f8eb391fd5103409a6ac6"}, - {file = "coverage-7.13.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d9bc218650022a768f3775dd7fdac1886437325d8d295d923ebcfef4892ad5c"}, - {file = "coverage-7.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cb237bfd0ef4d5eb6a19e29f9e528ac67ac3be932ea6b44fb6cc09b9f3ecff78"}, - {file = "coverage-7.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1dcb645d7e34dcbcc96cd7c132b1fc55c39263ca62eb961c064eb3928997363b"}, - {file = "coverage-7.13.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3d42df8201e00384736f0df9be2ced39324c3907607d17d50d50116c989d84cd"}, - {file = "coverage-7.13.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa3edde1aa8807de1d05934982416cb3ec46d1d4d91e280bcce7cca01c507992"}, - {file = "coverage-7.13.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9edd0e01a343766add6817bc448408858ba6b489039eaaa2018474e4001651a4"}, - {file = "coverage-7.13.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:985b7836931d033570b94c94713c6dba5f9d3ff26045f72c3e5dbc5fe3361e5a"}, - {file = "coverage-7.13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ffed1e4980889765c84a5d1a566159e363b71d6b6fbaf0bebc9d3c30bc016766"}, - {file = "coverage-7.13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8842af7f175078456b8b17f1b73a0d16a65dcbdc653ecefeb00a56b3c8c298c4"}, - {file = "coverage-7.13.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:ccd7a6fca48ca9c131d9b0a2972a581e28b13416fc313fb98b6d24a03ce9a398"}, - {file = "coverage-7.13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0403f647055de2609be776965108447deb8e384fe4a553c119e3ff6bfbab4784"}, - {file = "coverage-7.13.1-cp313-cp313-win32.whl", hash = "sha256:549d195116a1ba1e1ae2f5ca143f9777800f6636eab917d4f02b5310d6d73461"}, - {file = "coverage-7.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:5899d28b5276f536fcf840b18b61a9fce23cc3aec1d114c44c07fe94ebeaa500"}, - {file = "coverage-7.13.1-cp313-cp313-win_arm64.whl", hash = "sha256:868a2fae76dfb06e87291bcbd4dcbcc778a8500510b618d50496e520bd94d9b9"}, - {file = "coverage-7.13.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:67170979de0dacac3f3097d02b0ad188d8edcea44ccc44aaa0550af49150c7dc"}, - {file = "coverage-7.13.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f80e2bb21bfab56ed7405c2d79d34b5dc0bc96c2c1d2a067b643a09fb756c43a"}, - {file = "coverage-7.13.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f83351e0f7dcdb14d7326c3d8d8c4e915fa685cbfdc6281f9470d97a04e9dfe4"}, - {file = "coverage-7.13.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb3f6562e89bad0110afbe64e485aac2462efdce6232cdec7862a095dc3412f6"}, - {file = "coverage-7.13.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77545b5dcda13b70f872c3b5974ac64c21d05e65b1590b441c8560115dc3a0d1"}, - {file = "coverage-7.13.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4d240d260a1aed814790bbe1f10a5ff31ce6c21bc78f0da4a1e8268d6c80dbd"}, - {file = "coverage-7.13.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d2287ac9360dec3837bfdad969963a5d073a09a85d898bd86bea82aa8876ef3c"}, - {file = "coverage-7.13.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0d2c11f3ea4db66b5cbded23b20185c35066892c67d80ec4be4bab257b9ad1e0"}, - {file = "coverage-7.13.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:3fc6a169517ca0d7ca6846c3c5392ef2b9e38896f61d615cb75b9e7134d4ee1e"}, - {file = "coverage-7.13.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d10a2ed46386e850bb3de503a54f9fe8192e5917fcbb143bfef653a9355e9a53"}, - {file = "coverage-7.13.1-cp313-cp313t-win32.whl", hash = "sha256:75a6f4aa904301dab8022397a22c0039edc1f51e90b83dbd4464b8a38dc87842"}, - {file = "coverage-7.13.1-cp313-cp313t-win_amd64.whl", hash = "sha256:309ef5706e95e62578cda256b97f5e097916a2c26247c287bbe74794e7150df2"}, - {file = "coverage-7.13.1-cp313-cp313t-win_arm64.whl", hash = "sha256:92f980729e79b5d16d221038dbf2e8f9a9136afa072f9d5d6ed4cb984b126a09"}, - {file = "coverage-7.13.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:97ab3647280d458a1f9adb85244e81587505a43c0c7cff851f5116cd2814b894"}, - {file = "coverage-7.13.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8f572d989142e0908e6acf57ad1b9b86989ff057c006d13b76c146ec6a20216a"}, - {file = "coverage-7.13.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d72140ccf8a147e94274024ff6fd8fb7811354cf7ef88b1f0a988ebaa5bc774f"}, - {file = "coverage-7.13.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3c9f051b028810f5a87c88e5d6e9af3c0ff32ef62763bf15d29f740453ca909"}, - {file = "coverage-7.13.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f398ba4df52d30b1763f62eed9de5620dcde96e6f491f4c62686736b155aa6e4"}, - {file = "coverage-7.13.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:132718176cc723026d201e347f800cd1a9e4b62ccd3f82476950834dad501c75"}, - {file = "coverage-7.13.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e549d642426e3579b3f4b92d0431543b012dcb6e825c91619d4e93b7363c3f9"}, - {file = "coverage-7.13.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:90480b2134999301eea795b3a9dbf606c6fbab1b489150c501da84a959442465"}, - {file = "coverage-7.13.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e825dbb7f84dfa24663dd75835e7257f8882629fc11f03ecf77d84a75134b864"}, - {file = "coverage-7.13.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:623dcc6d7a7ba450bbdbeedbaa0c42b329bdae16491af2282f12a7e809be7eb9"}, - {file = "coverage-7.13.1-cp314-cp314-win32.whl", hash = "sha256:6e73ebb44dca5f708dc871fe0b90cf4cff1a13f9956f747cc87b535a840386f5"}, - {file = "coverage-7.13.1-cp314-cp314-win_amd64.whl", hash = "sha256:be753b225d159feb397bd0bf91ae86f689bad0da09d3b301478cd39b878ab31a"}, - {file = "coverage-7.13.1-cp314-cp314-win_arm64.whl", hash = "sha256:228b90f613b25ba0019361e4ab81520b343b622fc657daf7e501c4ed6a2366c0"}, - {file = "coverage-7.13.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:60cfb538fe9ef86e5b2ab0ca8fc8d62524777f6c611dcaf76dc16fbe9b8e698a"}, - {file = "coverage-7.13.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:57dfc8048c72ba48a8c45e188d811e5efd7e49b387effc8fb17e97936dde5bf6"}, - {file = "coverage-7.13.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3f2f725aa3e909b3c5fdb8192490bdd8e1495e85906af74fe6e34a2a77ba0673"}, - {file = "coverage-7.13.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ee68b21909686eeb21dfcba2c3b81fee70dcf38b140dcd5aa70680995fa3aa5"}, - {file = "coverage-7.13.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:724b1b270cb13ea2e6503476e34541a0b1f62280bc997eab443f87790202033d"}, - {file = "coverage-7.13.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:916abf1ac5cf7eb16bc540a5bf75c71c43a676f5c52fcb9fe75a2bd75fb944e8"}, - {file = "coverage-7.13.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:776483fd35b58d8afe3acbd9988d5de592ab6da2d2a865edfdbc9fdb43e7c486"}, - {file = "coverage-7.13.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b6f3b96617e9852703f5b633ea01315ca45c77e879584f283c44127f0f1ec564"}, - {file = "coverage-7.13.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:bd63e7b74661fed317212fab774e2a648bc4bb09b35f25474f8e3325d2945cd7"}, - {file = "coverage-7.13.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:933082f161bbb3e9f90d00990dc956120f608cdbcaeea15c4d897f56ef4fe416"}, - {file = "coverage-7.13.1-cp314-cp314t-win32.whl", hash = "sha256:18be793c4c87de2965e1c0f060f03d9e5aff66cfeae8e1dbe6e5b88056ec153f"}, - {file = "coverage-7.13.1-cp314-cp314t-win_amd64.whl", hash = "sha256:0e42e0ec0cd3e0d851cb3c91f770c9301f48647cb2877cb78f74bdaa07639a79"}, - {file = "coverage-7.13.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eaecf47ef10c72ece9a2a92118257da87e460e113b83cc0d2905cbbe931792b4"}, - {file = "coverage-7.13.1-py3-none-any.whl", hash = "sha256:2016745cb3ba554469d02819d78958b571792bb68e31302610e898f80dd3a573"}, - {file = "coverage-7.13.1.tar.gz", hash = "sha256:b7593fe7eb5feaa3fbb461ac79aac9f9fc0387a5ca8080b0c6fe2ca27b091afd"}, + {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] @@ -186,14 +200,14 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "filelock" -version = "3.20.3" +version = "3.29.0" description = "A platform independent file lock." optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1"}, - {file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"}, + {file = "filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258"}, + {file = "filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90"}, ] [[package]] @@ -216,14 +230,14 @@ dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "identify" -version = "2.6.16" +version = "2.6.19" description = "File identification library for Python" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "identify-2.6.16-py2.py3-none-any.whl", hash = "sha256:391ee4d77741d994189522896270b787aed8670389bfd60f326d677d64a6dfb0"}, - {file = "identify-2.6.16.tar.gz", hash = "sha256:846857203b5511bbe94d5a352a48ef2359532bc8f6727b5544077a0dcfb24980"}, + {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] @@ -261,101 +275,115 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "librt" -version = "0.7.8" +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.7.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b45306a1fc5f53c9330fbee134d8b3227fe5da2ab09813b892790400aa49352d"}, - {file = "librt-0.7.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:864c4b7083eeee250ed55135d2127b260d7eb4b5e953a9e5df09c852e327961b"}, - {file = "librt-0.7.8-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6938cc2de153bc927ed8d71c7d2f2ae01b4e96359126c602721340eb7ce1a92d"}, - {file = "librt-0.7.8-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:66daa6ac5de4288a5bbfbe55b4caa7bf0cd26b3269c7a476ffe8ce45f837f87d"}, - {file = "librt-0.7.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4864045f49dc9c974dadb942ac56a74cd0479a2aafa51ce272c490a82322ea3c"}, - {file = "librt-0.7.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a36515b1328dc5b3ffce79fe204985ca8572525452eacabee2166f44bb387b2c"}, - {file = "librt-0.7.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b7e7f140c5169798f90b80d6e607ed2ba5059784968a004107c88ad61fb3641d"}, - {file = "librt-0.7.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff71447cb778a4f772ddc4ce360e6ba9c95527ed84a52096bd1bbf9fee2ec7c0"}, - {file = "librt-0.7.8-cp310-cp310-win32.whl", hash = "sha256:047164e5f68b7a8ebdf9fae91a3c2161d3192418aadd61ddd3a86a56cbe3dc85"}, - {file = "librt-0.7.8-cp310-cp310-win_amd64.whl", hash = "sha256:d6f254d096d84156a46a84861183c183d30734e52383602443292644d895047c"}, - {file = "librt-0.7.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ff3e9c11aa260c31493d4b3197d1e28dd07768594a4f92bec4506849d736248f"}, - {file = "librt-0.7.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddb52499d0b3ed4aa88746aaf6f36a08314677d5c346234c3987ddc506404eac"}, - {file = "librt-0.7.8-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e9c0afebbe6ce177ae8edba0c7c4d626f2a0fc12c33bb993d163817c41a7a05c"}, - {file = "librt-0.7.8-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:631599598e2c76ded400c0a8722dec09217c89ff64dc54b060f598ed68e7d2a8"}, - {file = "librt-0.7.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c1ba843ae20db09b9d5c80475376168feb2640ce91cd9906414f23cc267a1ff"}, - {file = "librt-0.7.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b5b007bb22ea4b255d3ee39dfd06d12534de2fcc3438567d9f48cdaf67ae1ae3"}, - {file = "librt-0.7.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dbd79caaf77a3f590cbe32dc2447f718772d6eea59656a7dcb9311161b10fa75"}, - {file = "librt-0.7.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:87808a8d1e0bd62a01cafc41f0fd6818b5a5d0ca0d8a55326a81643cdda8f873"}, - {file = "librt-0.7.8-cp311-cp311-win32.whl", hash = "sha256:31724b93baa91512bd0a376e7cf0b59d8b631ee17923b1218a65456fa9bda2e7"}, - {file = "librt-0.7.8-cp311-cp311-win_amd64.whl", hash = "sha256:978e8b5f13e52cf23a9e80f3286d7546baa70bc4ef35b51d97a709d0b28e537c"}, - {file = "librt-0.7.8-cp311-cp311-win_arm64.whl", hash = "sha256:20e3946863d872f7cabf7f77c6c9d370b8b3d74333d3a32471c50d3a86c0a232"}, - {file = "librt-0.7.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b6943885b2d49c48d0cff23b16be830ba46b0152d98f62de49e735c6e655a63"}, - {file = "librt-0.7.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46ef1f4b9b6cc364b11eea0ecc0897314447a66029ee1e55859acb3dd8757c93"}, - {file = "librt-0.7.8-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:907ad09cfab21e3c86e8f1f87858f7049d1097f77196959c033612f532b4e592"}, - {file = "librt-0.7.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2991b6c3775383752b3ca0204842743256f3ad3deeb1d0adc227d56b78a9a850"}, - {file = "librt-0.7.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03679b9856932b8c8f674e87aa3c55ea11c9274301f76ae8dc4d281bda55cf62"}, - {file = "librt-0.7.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3968762fec1b2ad34ce57458b6de25dbb4142713e9ca6279a0d352fa4e9f452b"}, - {file = "librt-0.7.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bb7a7807523a31f03061288cc4ffc065d684c39db7644c676b47d89553c0d714"}, - {file = "librt-0.7.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad64a14b1e56e702e19b24aae108f18ad1bf7777f3af5fcd39f87d0c5a814449"}, - {file = "librt-0.7.8-cp312-cp312-win32.whl", hash = "sha256:0241a6ed65e6666236ea78203a73d800dbed896cf12ae25d026d75dc1fcd1dac"}, - {file = "librt-0.7.8-cp312-cp312-win_amd64.whl", hash = "sha256:6db5faf064b5bab9675c32a873436b31e01d66ca6984c6f7f92621656033a708"}, - {file = "librt-0.7.8-cp312-cp312-win_arm64.whl", hash = "sha256:57175aa93f804d2c08d2edb7213e09276bd49097611aefc37e3fa38d1fb99ad0"}, - {file = "librt-0.7.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4c3995abbbb60b3c129490fa985dfe6cac11d88fc3c36eeb4fb1449efbbb04fc"}, - {file = "librt-0.7.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44e0c2cbc9bebd074cf2cdbe472ca185e824be4e74b1c63a8e934cea674bebf2"}, - {file = "librt-0.7.8-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d2f1e492cae964b3463a03dc77a7fe8742f7855d7258c7643f0ee32b6651dd3"}, - {file = "librt-0.7.8-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:451e7ffcef8f785831fdb791bd69211f47e95dc4c6ddff68e589058806f044c6"}, - {file = "librt-0.7.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3469e1af9f1380e093ae06bedcbdd11e407ac0b303a56bbe9afb1d6824d4982d"}, - {file = "librt-0.7.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f11b300027ce19a34f6d24ebb0a25fd0e24a9d53353225a5c1e6cadbf2916b2e"}, - {file = "librt-0.7.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4adc73614f0d3c97874f02f2c7fd2a27854e7e24ad532ea6b965459c5b757eca"}, - {file = "librt-0.7.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:60c299e555f87e4c01b2eca085dfccda1dde87f5a604bb45c2906b8305819a93"}, - {file = "librt-0.7.8-cp313-cp313-win32.whl", hash = "sha256:b09c52ed43a461994716082ee7d87618096851319bf695d57ec123f2ab708951"}, - {file = "librt-0.7.8-cp313-cp313-win_amd64.whl", hash = "sha256:f8f4a901a3fa28969d6e4519deceab56c55a09d691ea7b12ca830e2fa3461e34"}, - {file = "librt-0.7.8-cp313-cp313-win_arm64.whl", hash = "sha256:43d4e71b50763fcdcf64725ac680d8cfa1706c928b844794a7aa0fa9ac8e5f09"}, - {file = "librt-0.7.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:be927c3c94c74b05128089a955fba86501c3b544d1d300282cc1b4bd370cb418"}, - {file = "librt-0.7.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7b0803e9008c62a7ef79058233db7ff6f37a9933b8f2573c05b07ddafa226611"}, - {file = "librt-0.7.8-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:79feb4d00b2a4e0e05c9c56df707934f41fcb5fe53fd9efb7549068d0495b758"}, - {file = "librt-0.7.8-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9122094e3f24aa759c38f46bd8863433820654927370250f460ae75488b66ea"}, - {file = "librt-0.7.8-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e03bea66af33c95ce3addf87a9bf1fcad8d33e757bc479957ddbc0e4f7207ac"}, - {file = "librt-0.7.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f1ade7f31675db00b514b98f9ab9a7698c7282dad4be7492589109471852d398"}, - {file = "librt-0.7.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a14229ac62adcf1b90a15992f1ab9c69ae8b99ffb23cb64a90878a6e8a2f5b81"}, - {file = "librt-0.7.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5bcaaf624fd24e6a0cb14beac37677f90793a96864c67c064a91458611446e83"}, - {file = "librt-0.7.8-cp314-cp314-win32.whl", hash = "sha256:7aa7d5457b6c542ecaed79cec4ad98534373c9757383973e638ccced0f11f46d"}, - {file = "librt-0.7.8-cp314-cp314-win_amd64.whl", hash = "sha256:3d1322800771bee4a91f3b4bd4e49abc7d35e65166821086e5afd1e6c0d9be44"}, - {file = "librt-0.7.8-cp314-cp314-win_arm64.whl", hash = "sha256:5363427bc6a8c3b1719f8f3845ea53553d301382928a86e8fab7984426949bce"}, - {file = "librt-0.7.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ca916919793a77e4a98d4a1701e345d337ce53be4a16620f063191f7322ac80f"}, - {file = "librt-0.7.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:54feb7b4f2f6706bb82325e836a01be805770443e2400f706e824e91f6441dde"}, - {file = "librt-0.7.8-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:39a4c76fee41007070f872b648cc2f711f9abf9a13d0c7162478043377b52c8e"}, - {file = "librt-0.7.8-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac9c8a458245c7de80bc1b9765b177055efff5803f08e548dd4bb9ab9a8d789b"}, - {file = "librt-0.7.8-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b67aa7eff150f075fda09d11f6bfb26edffd300f6ab1666759547581e8f666"}, - {file = "librt-0.7.8-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:535929b6eff670c593c34ff435d5440c3096f20fa72d63444608a5aef64dd581"}, - {file = "librt-0.7.8-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:63937bd0f4d1cb56653dc7ae900d6c52c41f0015e25aaf9902481ee79943b33a"}, - {file = "librt-0.7.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf243da9e42d914036fd362ac3fa77d80a41cadcd11ad789b1b5eec4daaf67ca"}, - {file = "librt-0.7.8-cp314-cp314t-win32.whl", hash = "sha256:171ca3a0a06c643bd0a2f62a8944e1902c94aa8e5da4db1ea9a8daf872685365"}, - {file = "librt-0.7.8-cp314-cp314t-win_amd64.whl", hash = "sha256:445b7304145e24c60288a2f172b5ce2ca35c0f81605f5299f3fa567e189d2e32"}, - {file = "librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06"}, - {file = "librt-0.7.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c7e8f88f79308d86d8f39c491773cbb533d6cb7fa6476f35d711076ee04fceb6"}, - {file = "librt-0.7.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:389bd25a0db916e1d6bcb014f11aa9676cedaa485e9ec3752dfe19f196fd377b"}, - {file = "librt-0.7.8-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:73fd300f501a052f2ba52ede721232212f3b06503fa12665408ecfc9d8fd149c"}, - {file = "librt-0.7.8-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d772edc6a5f7835635c7562f6688e031f0b97e31d538412a852c49c9a6c92d5"}, - {file = "librt-0.7.8-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde8a130bd0f239e45503ab39fab239ace094d63ee1d6b67c25a63d741c0f71"}, - {file = "librt-0.7.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fdec6e2368ae4f796fc72fad7fd4bd1753715187e6d870932b0904609e7c878e"}, - {file = "librt-0.7.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:00105e7d541a8f2ee5be52caacea98a005e0478cfe78c8080fbb7b5d2b340c63"}, - {file = "librt-0.7.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c6f8947d3dfd7f91066c5b4385812c18be26c9d5a99ca56667547f2c39149d94"}, - {file = "librt-0.7.8-cp39-cp39-win32.whl", hash = "sha256:41d7bb1e07916aeb12ae4a44e3025db3691c4149ab788d0315781b4d29b86afb"}, - {file = "librt-0.7.8-cp39-cp39-win_amd64.whl", hash = "sha256:e90a8e237753c83b8e484d478d9a996dc5e39fd5bd4c6ce32563bc8123f132be"}, - {file = "librt-0.7.8.tar.gz", hash = "sha256:1a4ede613941d9c3470b0368be851df6bb78ab218635512d0370b27a277a0862"}, + {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.1" +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.1-py3-none-any.whl", hash = "sha256:867d788939fe33e4b736426f5b9f651ad0c0ae0ecf89df0ca5d1176c70812fe3"}, - {file = "markdown-3.10.1.tar.gz", hash = "sha256:1c19c10bd5c14ac948c53d0d762a04e2fa35a6d58a6b7b1e6bfcbe6fefc0001a"}, + {file = "markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36"}, + {file = "markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950"}, ] [package.extras] @@ -506,14 +534,14 @@ min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform [[package]] name = "mkdocs-get-deps" -version = "0.2.0" -description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" +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.8" +python-versions = ">=3.9" groups = ["dev"] 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"}, + {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] @@ -523,63 +551,73 @@ pyyaml = ">=5.1" [[package]] name = "mypy" -version = "1.19.1" +version = "1.20.2" description = "Optional static typing for Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec"}, - {file = "mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b"}, - {file = "mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6"}, - {file = "mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74"}, - {file = "mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1"}, - {file = "mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac"}, - {file = "mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288"}, - {file = "mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab"}, - {file = "mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6"}, - {file = "mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331"}, - {file = "mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925"}, - {file = "mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042"}, - {file = "mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1"}, - {file = "mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e"}, - {file = "mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2"}, - {file = "mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8"}, - {file = "mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a"}, - {file = "mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13"}, - {file = "mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250"}, - {file = "mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b"}, - {file = "mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e"}, - {file = "mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef"}, - {file = "mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75"}, - {file = "mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd"}, - {file = "mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1"}, - {file = "mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718"}, - {file = "mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b"}, - {file = "mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045"}, - {file = "mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957"}, - {file = "mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f"}, - {file = "mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3"}, - {file = "mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a"}, - {file = "mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67"}, - {file = "mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e"}, - {file = "mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376"}, - {file = "mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24"}, - {file = "mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247"}, - {file = "mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba"}, + {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.6.2", markers = "platform_python_implementation != \"PyPy\""} +librt = {version = ">=0.8.0", markers = "platform_python_implementation != \"PyPy\""} mypy_extensions = ">=1.0.0" -pathspec = ">=0.9.0" -typing_extensions = ">=4.6.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]] @@ -608,14 +646,14 @@ files = [ [[package]] name = "packaging" -version = "26.0" +version = "26.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529"}, - {file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"}, + {file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"}, + {file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"}, ] [[package]] @@ -632,39 +670,33 @@ files = [ [[package]] name = "pathspec" -version = "1.0.3" +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.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c"}, - {file = "pathspec-1.0.3.tar.gz", hash = "sha256:bac5cf97ae2c2876e2d25ebb15078eb04d76e4b98921ee31c6f85ade8b59444d"}, + {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)"] -tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] [[package]] name = "platformdirs" -version = "4.5.1" +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.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31"}, - {file = "platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda"}, + {file = "platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917"}, + {file = "platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a"}, ] -[package.extras] -docs = ["furo (>=2025.9.25)", "proselint (>=0.14)", "sphinx (>=8.2.3)", "sphinx-autodoc-typehints (>=3.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.4.2)", "pytest-cov (>=7)", "pytest-mock (>=3.15.1)"] -type = ["mypy (>=1.18.2)"] - [[package]] name = "pluggy" version = "1.6.0" @@ -683,14 +715,14 @@ testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "poethepoet" -version = "0.40.0" +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.40.0-py3-none-any.whl", hash = "sha256:afd276ae31d5c53573c0c14898118d4848ccee3709b6b0be6a1c6cbe522bbc8a"}, - {file = "poethepoet-0.40.0.tar.gz", hash = "sha256:91835f00d03d6c4f0e146f80fa510e298ad865e7edd27fe4cb9c94fdc090791b"}, + {file = "poethepoet-0.45.0-py3-none-any.whl", hash = "sha256:8e25f6e834ecf25fe2ddca676a4e0207eeb2e19def0a8709fc5c7f18e86cd68c"}, + {file = "poethepoet-0.45.0.tar.gz", hash = "sha256:cf2c2df4c185d33b619c2771de2b28c2b81c733f072226030c7fa4c273c040f8"}, ] [package.dependencies] @@ -702,14 +734,14 @@ poetry-plugin = ["poetry (>=1.2.0,<3.0.0) ; python_version < \"4.0\""] [[package]] name = "pre-commit" -version = "4.5.1" +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.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77"}, - {file = "pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61"}, + {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] @@ -721,14 +753,14 @@ virtualenv = ">=20.10.0" [[package]] name = "pygments" -version = "2.19.2" +version = "2.20.0" description = "Pygments is a syntax highlighting package written in Python." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, - {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, + {file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"}, + {file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"}, ] [package.extras] @@ -758,14 +790,14 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests [[package]] name = "pytest-cov" -version = "7.0.0" +version = "7.1.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861"}, - {file = "pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1"}, + {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] @@ -812,6 +844,26 @@ files = [ [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" @@ -912,31 +964,30 @@ pyyaml = "*" [[package]] name = "ruff" -version = "0.14.14" +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.14.14-py3-none-linux_armv6l.whl", hash = "sha256:7cfe36b56e8489dee8fbc777c61959f60ec0f1f11817e8f2415f429552846aed"}, - {file = "ruff-0.14.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6006a0082336e7920b9573ef8a7f52eec837add1265cc74e04ea8a4368cd704c"}, - {file = "ruff-0.14.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:026c1d25996818f0bf498636686199d9bd0d9d6341c9c2c3b62e2a0198b758de"}, - {file = "ruff-0.14.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f666445819d31210b71e0a6d1c01e24447a20b85458eea25a25fe8142210ae0e"}, - {file = "ruff-0.14.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c0f18b922c6d2ff9a5e6c3ee16259adc513ca775bcf82c67ebab7cbd9da5bc8"}, - {file = "ruff-0.14.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1629e67489c2dea43e8658c3dba659edbfd87361624b4040d1df04c9740ae906"}, - {file = "ruff-0.14.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:27493a2131ea0f899057d49d303e4292b2cae2bb57253c1ed1f256fbcd1da480"}, - {file = "ruff-0.14.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ff589aab3f5b539e35db38425da31a57521efd1e4ad1ae08fc34dbe30bd7df"}, - {file = "ruff-0.14.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc12d74eef0f29f51775f5b755913eb523546b88e2d733e1d701fe65144e89b"}, - {file = "ruff-0.14.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb8481604b7a9e75eff53772496201690ce2687067e038b3cc31aaf16aa0b974"}, - {file = "ruff-0.14.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:14649acb1cf7b5d2d283ebd2f58d56b75836ed8c6f329664fa91cdea19e76e66"}, - {file = "ruff-0.14.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8058d2145566510790eab4e2fad186002e288dec5e0d343a92fe7b0bc1b3e13"}, - {file = "ruff-0.14.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e651e977a79e4c758eb807f0481d673a67ffe53cfa92209781dfa3a996cf8412"}, - {file = "ruff-0.14.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cc8b22da8d9d6fdd844a68ae937e2a0adf9b16514e9a97cc60355e2d4b219fc3"}, - {file = "ruff-0.14.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:16bc890fb4cc9781bb05beb5ab4cd51be9e7cb376bf1dd3580512b24eb3fda2b"}, - {file = "ruff-0.14.14-py3-none-win32.whl", hash = "sha256:b530c191970b143375b6a68e6f743800b2b786bbcf03a7965b06c4bf04568167"}, - {file = "ruff-0.14.14-py3-none-win_amd64.whl", hash = "sha256:3dde1435e6b6fe5b66506c1dff67a421d0b7f6488d466f651c07f4cab3bf20fd"}, - {file = "ruff-0.14.14-py3-none-win_arm64.whl", hash = "sha256:56e6981a98b13a32236a72a8da421d7839221fa308b223b9283312312e5ac76c"}, - {file = "ruff-0.14.14.tar.gz", hash = "sha256:2d0f819c9a90205f3a867dbbd0be083bee9912e170fd7d9704cc8ae45824896b"}, + {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]] @@ -986,24 +1037,21 @@ files = [ [[package]] name = "virtualenv" -version = "20.36.1" +version = "21.3.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f"}, - {file = "virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba"}, + {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.20.1,<4", markers = "python_version >= \"3.10\""} +filelock = {version = ">=3.24.2,<4", markers = "python_version >= \"3.10\""} 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) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] +python-discovery = ">=1.2.2" [[package]] name = "watchdog" diff --git a/src/api/utils.py b/src/api/utils.py index 0d9378ac2..cf6173ebc 100644 --- a/src/api/utils.py +++ b/src/api/utils.py @@ -31,7 +31,7 @@ SHELVE_PATH = os.path.join(constants.ZXBASIC_ROOT, "parsetab", "tabs.dbm") -SHELVE = shelve.open(SHELVE_PATH) +SHELVE = shelve.open(SHELVE_PATH, protocol=5, flag="c") T = TypeVar("T") @@ -178,7 +178,7 @@ def eval_to_num(expr: str) -> int | float | None: except (NameError, SyntaxError, ValueError): return None - if isinstance(result, (int, float)): + if isinstance(result, int | float): return result return None diff --git a/src/parsetab/tabs.dbm.bak b/src/parsetab/tabs.dbm.bak index f55990c88..881659c7d 100644 --- a/src/parsetab/tabs.dbm.bak +++ b/src/parsetab/tabs.dbm.bak @@ -1,4 +1,4 @@ 'zxbpp', (0, 71563) -'asmparse', (71680, 233956) -'zxnext_asmparse', (305664, 259034) -'zxbparser', (564736, 641214) +'asmparse', (71680, 234798) +'zxnext_asmparse', (306688, 259879) +'zxbparser', (566784, 641214) diff --git a/src/parsetab/tabs.dbm.dat b/src/parsetab/tabs.dbm.dat index 4c673d3934144bc75fe490681f227cdf9e7812e4..dfbdc2e07ebf34c4b7ed4c8f834d2e48f7908c25 100644 GIT binary patch literal 1207998 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
LNQH{>XfsA&B}B#_x06jm2ZzE*=Up;Sa;ge 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

@vPiG>lF6#*v-*A`14ago3>m2!sfn1$>e_A< zGMN=-GPz1LgC=wI=#nj3=FP-HhquO~#of_i-fh37bh%BO+$K(L6DPNcliS3}ZQ|rM za&Vg}e{moZLnZZWHOXUUHi%`mDa+$bgYSBSS{2MrtDFHg#>c3b~CH<~DMb z=x~^fdz&A%p+$Q2m)+Y`MV}q+H!@&k(8!RHs*##VB|5^Se#E4T^tO?hR8{n;BJMXb zU}Vt9kddm9nn)!&(xm>8NfqgBBQdF}=u-ux>aPJKgGPpoRE^X`D$!9U^}{Arq_>U4 zq^hD%6_BdG28;|E88T8eQWL2}vrOtAnpBbAHWHJniau39s{R@q0SQZ-T&sYJ(_)PFOnBE4-S zCRG)Es(@7eHDF}W$dHk$k(x*)I^LxI*rbZ|wvm`rRrIL>QuWt>kwGIvMyf_?B9-U_ zllmi*D$?6VVp3JnrwT~bUjs%4jSLy78mWm?q7y0g<+cw}>MKTGHS(H~UL$=*`i%@2 z88k9vq-vxll8oW~af(gGdTyNO#(Qpp=O%h?lIJF;TqQb*n)(aslkt~bEdGXLXm%B+ z*;SloS8<2UT~(pkRh(v5ahhGlX?7)t*;S<1dTDl5MW5C88yPS% zXk^Gp)ksamW>w+hX!tgzXYTqQb%Cf~`E(p6jh&}QVHIGsvupQE-{{~l<28nwNE z_gAh>R*<~85}l5dIlp=zb#zB(fafR6qwBh(GkxlPlMzztiBsx{Q|gIR>WNe8iBsx{ zQ|ggJ>ZwAhCr+s+PN^qOsYec}C(>)ZlzOV@v-*A`14ago3>m2!sfk$XscXAcDD_xj zsYk97ot2vW|MyeQvk@hJ=`32(9i0Q7Uvi6>s_3)&ej@`$28|3EsT!$? zSoGAj-6|A4R#^1J&8OvEmFPU?F!|-((fQ!HMRByEJDTG?@@L*7;^Yx=@`yNjM4UV# zP96~_kC1~$R3VRulSjnKBjV%{a`1>qul15gRMBVk{YD0i3>q0SQZ-T&F^{NgyH&^| ztT2y|t3(%glfS_uN&;~*S)5E3CzHj=WN|WCoJ=MMlT{&;#mQuGGFhBVCI^#6daajC zRz;uH_Zt~7GH7JTNYzM9#7tJ#cB_!dtT2M$$!TqGFhBV7AKR%$z*XdS)5E3 zCzHv+WL3yyaWYw)Ocp1T$-!iiUh5^3Rncel{YD0i3>q0SQZ-T&F_YD`-6~`da@E7p6mQ&Fht)-&12^Uh) zx`eB!=!S$#sAzq{^;6WHaPbsvNVsx}DwSvf>D;%sM+&5_exfqN8_?77LO0>|A_&GnKIE^SyBZ||A;xwW-jVMkdiqnYVG$Of5 zbg>`lvwkFT8cCc+5~q>GX(VwPNt{L!r;)^IByyE#5k_hp=1Zu9o?84Tbr7z^30LBT zD{;b=IN?g1a3xN-A_rWlLbwtqT!|B|#0gjAkmDk~)=RijMW5C88yPS%Xk^Gp)ksam zaHX#8Rv}!m!f-{d5-s*7{|ArAWN|WCoJ z^cv|i(r;wI$e@uSBUK|c5kn@$8ZyNxd&Mbx#VLEmDSO2!d&Mbx$yK7usHwk9x}2dp z2vb4ZGaTbY&;5ea!%f3Kw7kNMexDgF@@kvNYessF^cm?lGGJuT$dHk$k(!7ZO+jX~ zI2kQYMvIft;$*Zq87)pmldD8mQq%URw#8Q(|0^%EUB#ei%J^6MK==_S{D>2N#0fv* zgdcIjk2v9n9PpzG;YXbCBTo1cC;X5Denfh$m++&CKCACHGGJuT$dHk$k(!9%M_t>k zLik~Y;fGu$y4sukFFYcX#mQuGGFhBV7AKR%$z*XdnH)@3g-jMFlf}toaWa`4Ocv?2 zUNTt~eOBLZWWdOvks%{hBQ+5-SzX($LMF4qOeR-}uA#|0^P2J2-;cDs*2m~OG(|BI zrx=M-jKnEM;uIrsijg?Qh#X?13dKmAVkAy65~moELySavt(Rh?iax9FH!@&k(8!RH zs*#$A#YkP-twJ$kg~f91FWYEZvk*blJh?%dh?N%Z4Sz+drt3=m% zlmDJaWU@G!EKVkilgZ*_vN)M6P9~Fs$*Pda;$*TonJi8wlY_}3z1B-6tD?{9`;813 z88k9vq-vxlVkWC=yH&_!R+!1;Dp4m*=8@=jtNt<@a5CPKTO0o?r;Q~(T7OEN6)ka! zmN-RAoT4R8(GsUm2! zsfk#$)V19z6fIU*SjbhPrK!nV5}c90*SRcJv+Y|nK#>ur$cR&9#3?f36d7@fj5tMx z93rC%MMj(=BTkVKr^t{)WJG$cmm;HzKCACHGGJuT$dHk$k(!7_MqS&jLXlyGMTT4@ z>Y~Z~l54bCpX-d?s*fB-cUGe1m{j?Bx9G0!Xa#tFw<@~3JBrZ-YJQB`{=fg^fp61E zRwY^~_w%CsUhT#g-B*bczGT<-1B}%jUC)8r+WrZDyz%SkRTOetRF7|u?yp3vA^yJT z?cLEDRyTZ|ZEe#d>zW?9q3MzJO^I4&rp-AWjz!;&kC4P8Sa1bm2g* z65T{|FZWkPTXubPGduI*Dtb&;R?)jE(Z;l{?OCcRf#fRDCV%9;{)o75QNk0$Uy9wr z&TQEB9(2Z&S&@Dt14ago3>m2!sfpMnAp4j`Lwp)cvW`&tdt`gnmP5w5I$YgOcS)5E3CzHj=WN|WCoJ=MMlT{&;#mQuGGFhBV zCI^#6daajCRz;uH_Zt~7GH7JTNYzM9#7tJ#cB_!dtT2%vc2&rBak5>UY!@fn$-#D!Uh5^>Rncel{YD0i3>q0S zQZ-T&G27L(-6~`|E6jFsmFNzJn~&DozQiNH7whHCK1KhMIx9@#6ee*BlQ@M*oWdkd zVG^e>kwchNp)iS4n8Ybe;uI!w2$M*!^-`Er(P#DjMh1)w8W}QDHBu9?FsW<1RVYlX zurQIUL~r#b{{@f8WN|WCoJdW-_@- zbQc!Y{QhwCWOsBocz#$lda66Rhpy1N_;01v7W_WtUUY>Sf6acqBKD|E6@6CUZ)Cv8 zpphXXRUnbDp4KQ@E0*{-^YRO=>GK2 z@9EFCW6@oY-rF5LKmmHD^(PddXIkR87HsLcm_1ojA3fzESBV}><+>+rZJ(j|EflIg z5=yQTy(1Oc_J6#x;U;xv*tjU-MZiPK2pG!nT=^spc4Q+_0I8cCc+5~q>GX(VwPNt{L!r;)^IByyFg z$B*<$Kax0&Bu*oV(@5epk~obdP9uraNa8dSxk~hiAL$c*Byk!^oJJC-k;G{vaT-aS zMiQry#AzgQmFQ7F(#QQs;xv*tjU-MZiPK2pG?F-tBu*oV(@5kh(L4P}f9yvRr;)^I zByk!^oJJC-k;G{vaT-aSMj}^<{(vL#LMnR3Ukg2kI$Q=mj|R956nWLiYessF^cm?l zGGJuT$dHk$k(!7>pYja);$)FHStL#tiIYX*WRW;oM6MFOi`u`@$7GMQv#!maq@{gr zBK<}Nj0_qXGEy~C6R~mG$F8KvRibyNLUnES1O@lEi3}JSG%{qQYNRG&64}@!s++FO zo}kpLcp3SOKPs-3$AM3>vo7rZoSk*16{j<;IGt(5=}aq5XIgPO(~8rXmK@Hss?eEM zoX)i3bfy)jGc7rsqC|SFm(H}R=(GBMBLhYTjSLy78mWocnO0rftwLv7R@j-ATqSzS zoBR@w$YgOcS)5E3CzHj=WN|WCoJ=MMlT{&;#mQuGGFhBVCI^#6daajCRz;uH_Zt~7 zGH7JTNYzM9#7tJ#cB_!dtT2k7#TD&WTa}OCSsY+K9=d^ zD$%nPDl|XmkC3ZG&+!QJKYG8PrJiRUS14^?Wov!{LR=+!ACBN>uir&Ae)cM|$;d56 zszz!emFWGHBxByqE`4nx{YD0i3>q0SQZ-T&F_qcJj3HO4)F-T1yJ0O)gW78*o{FuD zj=*k2r$^(HQ~Bs>Lh=yF#N@{$laihJ(&OahHzZT^z4T}sef&AvR$qmUw$pdMqV2WO zf3!n9H=3F}!v`QcCi}C~uF2a;cGIpgwp$FhT#pXWrsL6p+NV02rk#zWgR~WHWILxu zZ_+-S(ZSkKF*-!M_eFMAHnWOm4l~~PY7e(P;XWYT7=t@SaO(u_CBPro^VjD3 z`L({b79Fn7N=8TM%W%=r`jIt%T+QEA^Y_&J9W{S9&EHGwchV2gth4nyY5qQ%zkud1 zpZSYt{?a+VI-1+f-!1d^%KDvhavgijkG~Tws6+>#@<9BXhJOd)-*o(Y6aF2He}~}T zq4+n08NiRYm6e-`!-wJD;rMq1{^9p0{Iv*w+reLABvYA{mv-~#6a29Qf11D_67aQu zzNF9B-;+=CzU&p<(aHGx6ecG^7e~>Rzq@2;k_e~LE|()aG0m>|-3_UHC?wu#Fz|Hz zI|Kjly0yK3?Ajc_m~{S*$9J}+iGMc6JO}^I#lPA3cOL$okAHLU?*jb05dSVhK=_rA zDDJj50lyqwZ<*4@nT~*3)g47>i5LC&c3(6f{}u$HUWmUh#=k}M?rH4s787s@Di`A) z-a6v@JjtVs`;FbvZe+znR3DqvJ;W+r$n#5fYClGfCcEiEcqX9%uMj6b~VI zSDv@2dEeIVU;a;JLm@z$G2UD8?@s)?3;*uMzk8^2;}ZjJz3?sx?~3sL2XA^rxYNO# z9K0dH`v<(T=QTI4hIyUJD@b13@v19&4FBFm@tP5yZ89S~j#lpu9Hc9Ov*^7|$=M{& zrOq!*C0O-y8O(JdmvCIVan;5}7MD}e^Z55Z{Cj`3r&8ZW`wuU#tXbL<&%zd=9s0ZH z;U0ZlwX?EnL)Wqn?2x#js~e`wjN0|?b2P8*i&*PKiJsR+b>01m?r-}Z7eMvNlCk#s zo_i`wX4%d65iW0=@W?pcZNh)`?Yq}6owaV&&9iRqT)MQUKBaxp!p`;GUF%`vVYkyJ zd{TV<+GQ2(5ZK*wEl$#7>f0{#Rav_UYdTkV;oE+f%)sY5*{7#I(Ke5~v!_03O=a~G zNR7tSC&-=i>r<9?u3FW(1fL$7k2-#Ws6GZgwRbO@ty|v(g(j|Fxgy?xes}fM$FJ&I z(-V!YPpov`jE{m|+Y?XitWR0BzGLNvF8xj2Cbch`i$A?y@pjw-vp#kfT5C(_G1g_x zvaXw`&bF)8uD!l9?&@5|5T+Yrs!iwe4N#dTXP+f^CWRZu>O+2Gu{$jC!wimhsqad^ zZoFwpXZPw^_=gW%b>pjkv$#cU{fwjIwX3_1Ub1%m%C1#MqoHGuLhF@Fk5(^G9xw?< zrxIqZ!*?rpkLKW;&U-;seF`u>viKc9Y#WMiF2zU6T<$WOert()ejdj?KSPWB#<#G^ zyf1?$!=P>acDMYl-qG<{+Tr$CXmDjtgELARoGuN{kOq?r8k|$qz(&Omxc1T4@pCS| z6p7Gv)4KIN*&e*wcXk^nWuLh3KvpS8pe=XESw`4%zerR!5K z&0CAFTqD$7%Ym!02+`iyf>-`p5yvH8EjM4`P`r{_a)``TaCE#D+Yv8?eJgYJsox5I z(lzWSzs#I>_gID}u8*I+U=j46eTB%v_De4T;^)nsPda|yrR^xspL-ek1+a7c!UdOO zDrvvuqPZ6=2D8X9?vs>RuGGh!x4^zKicgKsM#n`97xr|`yVpjIH^!UdTjFY5i*Jo@ zi*Jwbh&RV?jqi-_itmo^iSO;2cVDXw?{Br?1FbfEN2?7VZnfbftu}mTs|_D(wc+C} zHmo28T_#~tzXH1^he&am6eE{{3`~I%W?$WO{WR?x~agA6o;B2$NeVJ zaV35veyj^)-cTRky#bTe9)uk?O%^3M#c3WnrlWJ&vaV%4^L86o$46suM#UcyzH*+O zTw+-2pdD{?j8J_9P#s^7DR>n=)Q0JI-Z~&SUY!T;bMeMz;KiZ%d4U&_^5Fe|1Ml-? z*F6V^=lbQr`?|gx#oO?+%>O>#5Pae7xkka?i0}tV6XGpRnvIS7J@4?QWHY?AI`yV_ zOEp7UW_bHZ{KfcBiMJ2MPt|wNV{O4AgIfF^04#oQb6{->fpugNST{5P)>q?A&47hN z@z(?_NXi52Zyd0`R`$_X;n`fjQD9+}@VaN^-=h;5f-hiwwNdalA^btoguqIZW>e#S zUvt1xvKg>iU8s*!XG3}h^#beZfV@_ zHx5}!HbYjcYx@*gsb)ya3|Rxo4!F^h?2Ll=daZ;nUi0MfwL`NdVKv0ptRlX;%lJz6 z!LV_)3B=sdWM4EA&>%7gwB!JG*R`D*l{{w>rpMKSj8V{SU7&M`K-^i#i&tkZmGFh2 zd@=<943Y^~-c1Bpn(6EF^OTK-#8xk!9TG!zsZW5%~64Rqp~w4VR~FE$QXs? zRt8H;1i?}vFQLj(39!s3QwYl}nQ%L-i2!wXCrD+Z!Lrp`RVge3buDlGX)=kB$H~f}rH1)DkFhgT6&j-WG!L*di$3(g2hXaKPJ|fzpm952BGk36XhF zKFsb0N;WFHQWB=ew-scJf^sVZr6qzusgRd2WvK*E=94J|WtL2MXw*c2x;s!R8x54L z-nmLa8K`S{n}PB%AxD+8q^fl;NZM%CzkTD9%tqhcw z2m+--Uc!{65VCcN;}M1ZHWOq3`p^>=sitk`HH8#nU|bOJx}8}cNgc9orwvPF;m4F4lm|zmy)u`QgS6< zE~~qD{}jmI&Fh50i-2i1XS(7I!R+Z?GKW!_%VItXrt&#-@Kg0&6@I=6*1k|FJZ;g! zp7c^|=gN{!IFDHu#(-~s%~ZZ206s6XjB``^6D__|H}9!d8@{L2hR?Lx@VQnSzOU7W zA857Vhgxm;;Z_@dq}7HWYq6nw+cELe#uXOz00CJpC=-wn%$6({LLiwK;gMO>IHj_L zazN4~WfVRUEm1VJJ=$43(Nub_1^+Z@p>r)gm^uL6}w4OPKXJg@j(B4YWo}UBl?$m)DlRczJ z<6D&ZR&pPkJ1Z|+V%U7~3yhEBo_R0cePcK6D zHbVCL9ArFEf~=xm zf~?OeBm}c1%Y_iKCPt8D)-+D3ETJ4^m7E6I4{jl31HCPWGsvC~EtAD8u0TP^o&#Ej zYzuFgrjWg=0NFE&kiDIdeJBUnPHMo8$r7p27_#|RaxR4DA-jy~d&p8+*}s&72>J0c zNu!Y6${=g$Ajm4}CCK`mLP9WGvRnuuYhnahW=-Ri$`Z;!R>^6Q{lhJUY@oO0a0b}~ zS|%G=)KCzz%R$SK#eOaMNB8CTPgBTVU4ZPFMabSk$bKdV*}JI$J0|x?jmD78w~})q zJP+BoQGE|tN-O)9au6XuUM6W2vRfHsEgb||MZE-BpHoN(W=oa}A!JRAAj_<2oKjgr zImjwG4YGfOe^y`})!vx<=2OvwIo4zeGn z2JD#pk<@4m*?cQG7sB(9{V3J>ZzcaicpkO?Knpx-DXr{c%0ZC) zc$uV8)NW;{wR8|_74;HoeNG`Em@Qc@gs3$!LM^kVaY|(gePT)3tv-LG9(#xVxiZljozLney z$$8KY&;Wb=oTAF^r4R(j50;5>h;=(XzW(k`t?m#ux;f+UyUMlt3yOKIzS5n(u4uU+ z#473~#QKCnLNHshTnG_sV$yf&m^h8ID$6McS|z7}_MR;SZJ@fPaR%D)*vVVlc_({< zBWQ=HQt*6Mr(C_TsFSXLjko;9qJyk$Y8oQ;+#+J{B4UGA7N=2*gIG+L7L5^`ZzcCa zavreSZLoN|a&avEaawuOidRJSzF5PLWj(AOoBv%nFthf<~Bl|@4=-dJoA zux|h15V5n1h`pPL4PIKjkXl^CVy?7kjM#iDxfhc2h>fVdontAg>{|*!fc#*Ym{G)T zWr($05MmYe5@LNqAt9J8SuTW#H8Ck-nK+HJD$6N{SS6<+w!Vdk4OF)@&JcSs6i7N* zbfF+(=TW87IhOA%whCFdSGYCTviQ6rWbYwlgVz?GNP2W9h_6k(KmHZ3IdC2k` zX%4ddgj(6T6oLr(!7?$Uklo54Yq=oED(WT3`h-G4Fk7-*2q9}?QphrK8fR6OQx399 zPJ`^*w-B;{>XybCWcdldcXQ+J% z3MBu);=3q_+AmY3;uS65T+Cy({8B}F3rb(j2{C&?5wrIZv-x)ye@GR6#Nx-&qcLXl zt>j<`&tvu{)ZZ{mQDyH^2twos%fyUgb}PfI<$^G)sFyJ76AB5zY{_yV#H@))G0Vhh zoK;y)Im{|K4YNI4h}l4OOXCc)KZgRz0E@BsEGRMiA5^J`S-!g1DrWVKs}Qpn7BO2V zX7evEZi~^A?O1FtJsM*+-%1XK@H}Suc~gg3iYj}TLJ%T9SSDr^vs)QvEf<7YMZJVs zpHN5$W=oa}A!bcXidiO3_;&|!N7_<3SaxjGFF?%BQxAQDTmAy+L2$3Hw z6Elk0tqil43&O0TUc#(TC?o{4CCh~nvnD3REEA`3R%JQmFstM=%>Ka^Vm46S(m2EH z=};hvSS&z6%$`h@isxCryx1aU{r4k7*v>7&_U(ji{td<}sKu2mu97N^VViFyA47N^ zw%5=EgDpjs9ZVsJk{>J+GYZ?S47QdFf~}%nf~`*|Bm}c1%Y_iOCMJa~6Q^-jWjWA;zH19%8>nt+oWXVp6iC*ycnb=`_RUl&co#}HqTCCNdE_>Iu_F86t98(0pSB`$ zA0Tq`Z!z9VMQ&qpyR>PH+<%KO)j z*{WE=M)kmpH(4d^I65jX`EGAPC3jf zKMk|3CRMuaVxYREafaECGIL*G@gfRh_7AC2@YqKnt+oFVpG%-o-__$dk^_8U|wcqPgq zwuM)sQouHNjd5NPu@4ck!E21ap$fm13XKu#8ze8Il@JS|dBna-)jeX_uIyXNL4Y_^ zLDDEh*aZa<+e?+o=h#->V)T$Lzs5Mf2-$}T+2A$CeW}BK(xEYAeS>6wv=U??G!NMW zaU_E*+m)S5IfxL4Do7fI>{bR@O9w$#Q7=K(=M)kmpH(4b^I65jX`EGAPC3XbKMk_2 zZZe-jHc;KtID_mV%-j=MoQi^wJ%}omAluSgj45O%>gB1Py8B!ApcWJ{+XKuNUSph1 zJBYJG#`dbAR1AvcfO)l}c3mhH;^r5pr_Llq>AqIN4o zt)+udtEiVy>vIYTkZJqBc<7(l|qHH#2uLi@Q+} zwQH$T>58_Mw-`NUORq6rT*U07#BBaG#s{gyJETNo%=!k&Lue(;LT(AVsvIYTk#`Qks=_%RZs#_)JRj5BGrbSBB0m+0g+;R zDo@3VU4LWDxz^lst-bci3BT`qf4uMU@y&g6_S$2PHP%>Vm$Ogkv*J(fv)%p_QtY#i zy4twzvscr(pJKQTfqnK0Qfl!_=Z>puH6I`?}FPav?*eua!rAWgH~dy4C(*6 z?fw&p1qB6XGTwyCRVoU*HGPlawP{?u;U?ay__ZtJM4jq7f^E}gqI z!!`))wj)Vt`QT>q@FITQRWC$!`4pq|Tzz&by@Wh7@3~jA=cbP_?m{ZN3YGGNYXz#^ zP)d3(aMPaqI#$6ZT$XEUOdK>6Q)Nj1*K_}u^<1@s^jvB6M$a`*!4Rpde9ui?MdOrR zReLJ*T=A#&+-`qAH}+gdU2Rlo~yE#ZNJYpX|X8dB+<+`W4#? zmJO`eHw&G1aP9C*-lr^>zwnf$`di+IA)Eb{_ovX42VUJX42NvmGh|>h{A_h~D7noP zZfRGpW{~LG5Zj8#EheO@`!RkLygHoa_@m>q1joxAFa!ZWOjBb>57rmF1`K(7CbP9+ zXoAr-hO~U&)+_PQX_0YguWAl*SMw0qP)Cx;^1%(_5L@_>CH#PP{Zs3qvV*JXt#sEq zv(Y*gTVh~?Y8E#e4(u_oao^1xSZDL!)*1`(uWAuOz9gz!!-|LsfKFTEB;sjn5T~Rh z5zrdkU1RvxPz-4eDeJmoY*Aw|psgW1v^BO|$<}Bvj;(Pz2v+YzU~4QT>E(m#$JUTr zrTmAz2C>)}v9E+FPddxt<>t7h*&OGS!3Dygt#eorkrL2pb6iN;+8o3wH3t#U9Nb-F z_~uZI_*}LWFSLje2`wU2q(ye@8`Wi#^sP38KV?6JzY#xBdQP2f@0dgBx1t==F`$s0s~H@LgT@ZM02=nW}e@P-iy-ViFm8(VejjiGF{>MAm7@y6;> z;+hU6MoNi~bSSZolvv(gB6_2tUi8LoIJUYIf!_Eef!rWoc)}vFMm@_8pETvri3d%b zzU+KXut#!&T{BFu^A}=8yTQQj)z^V;v+G@)T{nSqSLMuFeHHuW3}n{$3h8i8Y*i>NDDC~<`zE(Z^ct`8SO^jwJMDYwkPc?0JUTrlvSfeQ!TJCBQ&Vjj`2 z`X+KxxCfbg`QW;-Q$;lfuI<}s$iQ_y$!J@vHe}KS73?SqwpI}A#5r^4ov4yM30#xx zbE#xcU|&PB$4P{eaj(=F(5YlkvxrK@LWyL0xEvfLlfzZA_pY2|jXWyZ3&=^yo?-6g zgKL^(6UuW_t=7=qY15X)f4*0G_|aas#dDU-Td2DI2H>XKm!fVMB-trn#Lnu!nf|?v zg~PA-@!C5=Kvzo6$dIbmaWz=)B^DJO2i}{A7%&6@K$@w>hyzlMA!`TbYlTse(F5u=l>WiJ(m#M)w=28)+AN~|gN2ed(ZehGT!$1nT>HnxE7w07d9;6Qgq##^UFKds zxK1ATN#&j{2_S{OzdHn7>iA;GY{Z3p(LH{`(8 z2DUqMS>Ib|jlrQ4r=WjM9vs#`Y09KSmkru@KRBd!0y^ci=~KsJgHq_C!+NKVAKQz6 zPMCP$INi+9&0)(1hfJKcY;fq*=?5(v95H2V-^3Zy#*Uwez0+pSI2b#oO(nqC-bn{d zSvI)(q$%UaO~M~T@dx6BiHJicqr}h|qlNtPE zQXd1crq0B_hfZOlp%Z73&G_*LBFU*(DBn)^Ml9sGU-GV9U@3bN(^jx^wdLnG&X`b3EHxX+=IWl2mjy(mEnij zkofdoq*-lx?|5dLK3)DXUH`#Gz(1}X$JW5=?Cccq4730A)JyqV+3kl zoehjIMi8#in*<1Bf11^x;uMfUk!Tr8fPiNeFsL{U)KGDG%iJB%1PhSlZ)k3;@O5O` z6+sD57(Ld+u%n~L0USN5?Rfm;YI|&|9T;|G*ok3hhSxIe!mumDZVbCKypCZHhSxLf z$*>p0-VFONyn$g1!y6g)Wq1?AehhDB*q`Ao3Qwv@JF{qK0)#_31zfv-@ZnQs+S9JNO|^FGz&r=YB$F|2Sr@=EqE{z~E5I zC#ly_O{E_EP}WXg_O&=U)Iyg|U&OA)(&=83prG6m7l@}KuuMq=`CQhrJv>^MvLj;6WlmJW1i@&F`}+@-=~Ho-dG*` z8r~R&52b@}uXy9TnK#yC39S~(B-PTREBRc#!K2k1C&L?}uZ&yKAPqD%$ofF0LDpgh zbPd*sedA!B`)$TcBSXD8Dos4NYOm_>J#-CL9bIDtH{n)5TK~}`kzv)e*;bHzYUOC|E-w8)xWoM{n-u1jh`@4Rrr2UgYS@{$>3UmP2|V&9!9oiQakZ(uTx+T1I29?R(L z#}e8oER!^f9xca)QJY6=qr9`&C=E1il<`1jqwLQNIK>W?qAzh7zUWKMJ-F(4RpW8A z2x|{tzKI+4MS{mO38oNGC19D71oFB1l1HlqXBH)Bps55iflLXe zF+(cB_!UT?mn?gpC`xd5D#6i!Z?X363Evm4IbR63FK&0gqM*-c^*Kfu<6? z4ak&WE;FPO$fd}_=oDQL`sELb65NwYuo&>|5^%3b@PkZ(C1j-%uuMq;`CKL7(JH}N zMF|>cD#6=Aj&m^3IAeE4)46W6tR@W2pD0JPdb^AFf$1!_Mk*hAR*ni<-;up)P9Dg2H#a zvlg!wU(Ld5ErKPr4?q=RAfsh8AZcGLhch54?=L%hAb|lIvl&oqj|_+zTN%}S!i5G_ zO|M|jlgpH08EGq%3|y-#t~+HeE>~ZRWYxzRZbo2F_%PGs?kGKoWhkeF-f5!_?#1P5 zc8I-KZLC7W&ZBwC z9B)B5WUXu6tY1ah>H&sF5ZJ8u^60Et$4Rrwgddxg!wW}_gl1!SsRx(9bpvqK%U$Eh z)o2z^9*idZ_iyB?-b12;4BrcE|3(V9q!P}idMAvMAyH0|*GtR;EWa@{c^b7+JuYQEkaA(K|2NX(&RO!jobASmJC1O!& zVgWMEZD`S_R9DfeOL@R7;m`^wa6q-n7+~~k4Jdcgn7%WbU;&a9F*H9j1bRc19y50V zC~(198=A8YZaAxfWUt60=7+OaiFWs)rGUNlXm^5D6mn{S?^1o z^$$||C&RylS9&czni#^S1*?CfbjXsLYLzunEgkn*rYJJ80BLDcG;bEaen-Z zzTf-6=$=_N*59rNZM`^;A} zy{c%YOHwn9C6{pw;~6F(bTCt)v`G9ah_VW=CS#$@S>-^$1Tzt_C^50nOlfXI^Ja2r za!|=k#te{F6GQW6a%jxm%B^MwNY=v8yqO}JKNpIvAa?;~=>?SMaRRi4T;OE;$kn7d zz_jz9N7Iw&e(Y3#1aw0OI!kcKv%$8>r|jS9-$fLU;F--f*8IV`#4sYGytWVTB40(?p> zusEB`jh;4qI~C+q>}eN-dqt2JGC@u!pbEk=B|+qKJ)h^%D##f;pI^iyc+YS#{-{nu zu*c&XxT?lkECV&(!6Q>OW_vZb?xsrPGmOrgn?wmLRFgO5X#gGz&sF$1L4#L)cUd9>E|Ib2O zRFIl)Qnb?tQajyADt9s5%|L-W*r`xDq)O+q&j2RaskO<7g@#IV8=AM1qQxeY<4TS) zazM4p7@&8QqsR0`0~nfM0g@FlH18;f#wR9*8;i;ntW>Zr%b<_U%$Fs#5)h0-GOrr7}X6sx6^ zQmdngg@#IV8=7~NqQy%Ba$Lz#Mh>V}83Xi=a`c$K=mA3$Ebk~*);r3fF>M!U-cdx; z0cujiQ4YE3sOOQZ`YFRN8GeI6NBt1nbjG8j>gxerm`dnHKV|ptH!GUzveZ;BlMoqK ze_{A5LWg0hP+D4Tv&>X~2TU*(5sNw#E3{Re+t9qJ9GVC{v+8m4koRYpHdtvV9zwK~H{hIJ8Ws(+F~ay`J$ zdR<++OiU&Jkg9K9^wYA`Pn(d7_-Rui)OvL(E$FkPGv4NaS<(1JtY{^sP0_rS9GV$)UAc$%NochN-TswOi`WjGXpE;<0)vWq6Ic*P`@jFVg4r?)KH=!(=vN09|-R!1{X z*behrp|ps*WoDyeSe+FtmWUM^sLpL@-bM~h4l3Emm;usiVrbq*4z1PVh9+2mq%MZ$ zZRF6HwrHaiO|XKEsGHj8sD_OkvfAC-=xuDQ6B$loI2D05nu~2}qe?5@cDO&A-kOI<13Ip|ohbRpz31vo`7?A{MnJR%o9(x1o6#IW#$_88bjyO$^Q3$f0TT6l`Q@f(1zGVrbq*4vlGxHcHV1E7*v- zsf~_q*vKKP-Mx)&VPoCF@EL}C5on`NV4K=#`YRobhVg_i+#wlNbkbF+lkO)GGOZrq zUk?hU)&Z$dTJ#;2Iq4zRNCzY$7WE}oXrnr}p?N1cG&!i`Bx43htBIj`Cpk22p@NeP zO|SsTS{RylQbhB2NJ1mv2FcJ#v%(|bPvQ>AaD415|!jGwcF%FZ&0?0U2u8*Y&BXpy<+)a(X{-3Vv^ikqun1DwYF1(WEF zfHC2&h&&1Y`sQ<0nx2A$XThI>J1GI~i~Nimy)W{`REL+br(Fl`6&;?+boeU)RR@+S z=^&r$eGwk5I-JJ)BERPm@zErz+CWng{)2K9;qNS;iXe}JcM@TCaK+O_5x$g)u*O=Q zMBrW#;pt3-HL)Xzz%nHf^@7e#2GsR-)>nIf#k42cM+B5sg%6d^d_heZ*- zoQkj+60|#kdqspFW+H4sUg`vvDTyGTs|Y+=MR?TSzzNe02UneL-rAUM;_w4x9cYCSpN7QqXx~Au>T!mr8FmgdA0Y+2^|E;) zu9L@#?}~*1PP$4x$-bsI-&~!}H+zCawHE{XnH=0<s=rY05EjK()#kpr3LaJ*F?VzoF>>O%9N(h@p8)1$x2p%-jW_zy()PJ$2QDhN~P^Rnm`9#~@dA9K(Eu1qgK25!k|2 zo7H0!UFGh!ViBcZ#!SAA7#}v&CuW0_;yISw&5*BQi3Zygz4hVLTc?uLX$+?`yd9x~ zw+f|0s%?`8h|T~^@D>q^S`!ORmgY7z?=3}(!<8IY@|KYUs#V4Sy|)}arf;QJlLI6x zVrbr5f!GV?gCKYg15FYZ_R0V%TZM&y|>NKc?5skhC>tdC$v@D?%hIN3 z-dBnieI>`0d}ZW-%6g=L-dBzu)3?&A$pJE{toKz)kD0pw6u96ks;6UCf5TUfsw(My zbpva86T__xcOcML%dv&8HgEM6PRvWL(r@c%G~DRL47agjY~Ttiz*GGU zH@Y&zC6!hkG8F)B?P9oMPOM2YT(amES5N`YHZj~`;aS!UmjqS{S5QM|+76v*b$P?o zX7&;cezzyfq26$vNlsKALFVc^439HB&F~z6wT4#W$s_&V3P@%nWxc{fA4 z90)C0>{$GQmv*di+Of9Bn9a_FD-vJlM!!aLQ`)0`jXm0Szu{lMWq66c*K)XI(Jii^0-SAPxV~#SToPC*TtN+8 zi|wXeYl}wLayir+dDr?IGFSg*7`isX8VqYQY=FS7^(RtGR@&CJ#!1)WSL#->Yw-)R zc-uieXYEkzTAxh2*5;%o4&H))ZOO0|!`28Lx>m>Iq-A!$*)SSegV4Lyo}&>ppUqerW#cd?TF0Pt_-hdcmuYFfAuo-F-$?|(4#sYCoQvM)}y8YF!U&5 z7h8s?Xx%nGhUT6M@&0Jyb_ z;rbrsaLJ-uTtNjm+r)5vk8-#quu`~!8hRA{pgn5KMvrni)Ejw^It-bsBN^r}9M5n9 z!(s&XsOi|Y^r&&tqq>-*umwA>LC7+^` z3|CMAp6X|~zDGG+Qt1L$Pyuf3Vz|CXIb5>n7FSRKZas?a=6jUGC4rT~^*xHXI!A5W z=us|5xkufD%+(hdzQS-n!@~^UMqrP+9owu&h4ZJC>QVmGsoA4mTkKJ{rakIO(t3*F zX@(y%Jj1Yp9@X(UY4O))J?dGYuBb;56|LLG$8dd*a=3DMsYe-GfEVg%xV}d@T=FS8 z$#4Y~;HiFw>wA>LC6z941r^}dE{5xSl*1*9ZgB+_;MSwqZoWr3ToPC*T;HRJt37J# zMvrni%0235$Xxx3;kOKbWcVw?zYy4?p2s%pQQ`b)rFs`}WEd(>@dk6L{l z&>GIL2Ez!3H5pdWqdFcZExt?Eqt*iIih2}L(YkGX4A=K4hbxDddX%vRc%hz#>wA>L zC7+^`3|CMAp6X|~zDGG+Qt1L$Pyuf3Vz|CXIb5>n7FSRKZas?a=6jUGC4rT~^*xHX z+M~8>^eC63+@m%`=4um$Eg42L?7*-K0(;at*k(N{oIiE!QSkF(+G&*v7d%aboxw&#*iA){AIT6A zdH`i2d?68m2Q@^P#MBgldqsrZG7b>*Oy*tQ#7lm_XGz0FW2Ki@1A0BmAO| zYJ`p6oW2NUOr~!KnZipj--CSuQeB9@8W&G4z=wJ_IxQ&U`W{X^z7=wig?FeHa5GEr z68!>JMI^KVnM)zVinge+-PW0ZqGSxI|ISC$;MXwd0i9M)8pe{?O27}0rzS-}wbOa(ob*m7`Mxa~Bihb3Jh{Ip+JjW{D!D8BB36lt>5CQ-k z?ly>MH4ouOAXh1Lx7BAHI(^cVgY;cOd!v4@jz24hfw%woC5smHFFkq5{Dp7R{`Pfb zvI_qy8J50^U5#PsD3P6X zq-O{pehSt^VmDwHZb(#Vay&02MMRu8;Eaa~;4z1%xY5TPUP@&6C3dvSz`Y{F8!{PQ zB%b}MIg6E)kk3^K0;m$_Hz8Md(V&v@yo1 z$ie+TEV)DVMQ&yZSfXM8ssv_qNM}HCbg6+vKq>4evIONkc$=gR#m{0&hu%haZii5! zqYXmmuk+-|x_TJMXfdguyoRldF?mgfbr9Ift73~*evOUv92Dw*=me&%Q}Vx7)3fdtd_Mg@ru$C32kFZ z4a+yPVo5Vg`BEDhC7>~z0r{2=r1lg4SV*%0(UE3XnRA$4cf(^szG%>0pgiAIo9Zv1Ic(Eel`aPZZxDxGWFOnXlWJkwb&Iq&p?+&G?)UYUHTz=V$7=Si8egxk*OC@uUt?XefP7g) z?v-`Pn_T6%hF!uEx;$fQW9}Wl4aU{n35<3rlga&(eK9&N%c4vd)oEjUeRN}Opo&jXzMf< zD@h`s>pG19D#;~~q&kyF#Pd-Ktbn42qNyGip&a$NfaUS4!I>U5hBpLBy90ca_(<~m zHQSqEC9L-yeE3^C&WDiR9JNMNFs#{@VW$9AmkZmh`&>rqvTREW%2@ekrz=-BSF!L8 zRmIIL!Ao=vSQU|ASE?w53@i4g8cQUV@_qpKK-w7iZ4aOskRQO6l$@JLyLALo(z=Rc z?Q{UYTdqPRt)5Y2cE6BY&br;ea1#POa1Bc=AKcD068nVzXS$ecn4;2Zr(THNGxi-B znf6EDVWXq(T*;00oe^2f-+^7#oeXy&G^Xw#G8!r^C?fBlwfsFyV=Yg-V#^Z=fR@y- ze9J4Aw7irrHJDKX8nYRYZ}~tftml}tl~heB%-*b^HvT>3#&;~Wy7dHpFEUkMWw;-K zjsIEZUOu?JZ|!2^=R*SLa86taaW@~yY!v2jPH8%azb~J|8?#%S!;dG+Zw8iglx6&4 zG-qD#&(5>>Zsl2gFPp^=)4}LTB#DDj&(G3XoEX&;!X_Jr9w#-RhUrauxA3v~NcwzHf&@Oy;DF!39Fs1rn?v~LlZD_H*Jeps1MJ2#1 zJoY#Vzl*MGuj;DdVYrwGO?X#h;c-+=)?RC@=hWCXQ)4yktcEeH&On@o8bh&1)hLt} z{u47b*5nbY1`!iA0$N2ZsFCJ2G_Qt3lY{)&>CprWkX92zivuXNRWzA8nY)!+%?yxE z-!O!R*9tgpOxp#TV1)shy2+jFz9GCbERM7_SWQ-@4HeP`WSnKMkA&5$7&c?r8iBoT zEo|BAwC8X>#$$k!X6aA4xBf;}5Heb8wpr_%XsrV=0nLRFhH_|fP{~ln43Jh6L-U4GG%*x&w{okQ z0n(|O8p?5F+Ah!pD;SEpsiB6I4dvifNN=b;k+2%Wuph%%1R82LY@3Ffy>f=)ST(J8 z<_wJFx*n*%NOVxqSldTqp&zhsOeUoshF*p~hA9Xg+|_o7G~+?eV)$llH4Rw7VuURU zPe3$YfrUXvpJkA8WXWg75x8l^3^JN6gCvVqmNiGc~uw>pwx4#PZ#3W1)Ujx9XB zWj;vKqNBv1?LQ}&4V7^Au)axCiq<~3Xzd*`YcD0UlNnB7IF;cvgbvnjJ4D*z;LO@@ z2Uf5)VT)}+K(uFpg+Y32J4iXQWNqUJ+_Yi_>8QT&%hSe-YU1Y zcyuMb&1FE#s6DA@?Hw~~f0)cZ!thasPxW!An9Si#zaEw%*#(Vhhs z2I;NsAmzxCwT&Zi(~23Sx3+^Mi&mC3N8p-P2I;M>kkQ)Y&;_bs0-918s<*Zy%Jkhr z3Sh7{TS%=vx@>KiLz^tM_9v0Ix{cv3hR-s55rNje0o&%NJ$uwE7`5T-)=_(M(b_v@ z)_#D@9%T3i!$S-YBXqEK+ab~xlQV076Ij98ge|rO0nwfX76$39?I7jIlC_N^aMOwz zq_?(%B#Ty-HAmo@RtD*&_8LGFoBg*vMLJDB8Hd{!oy-nHLE{8T* zYVGeMZ}lX@j~HHH_&EZt{V2B0QG52Nl^nJCV7$97@R4}()>E|f&e78G+6WoF#PB-? z9^JvszipNl<$Iiwc@p~vlnX{CVo`Zwq4m<-h88`Xp~*od4;wQ;T1^Zs+P6lNlQ-sW zM(%Gx}8xmIkW*D+Q!f*uo>QCg6 zUJV#mUkylpT6tiNoz7WyCZ6ybS=CEiF;smuV0koDZ_!Y%%?!03sjSbi0RxZjV5mZA zX|&$VP#Xa&7>bBRnTds_N^={UH%w@X(8g0-g>t-V`j?eoa&e1;1c-otPqLI-QN z9U^TpHM90bzzWtTY_Tl}i1sY7Fi3B02PsFEtZf{Dn^w#qy|o=AS+ugOIRe+TGDvT2 zg^boFhb~YB6VQ~(P`$MsQKs(}QUHUs*+OdV9n02sIkd@AYkvTFt1B3;VOY*^0|Ko* zh;4J!POk=Z@9fr5ds@-jyJyzEmCSBq_%y@q40j-Ouy)%a(iYP)Yu^Q|U~R${+k${- z&jJgB^wxHea%9Qc#u2z_#SGG0+d+~=E6bWAa7`3H7Kx^NFZFAJlt_CFD=0i1nSRtv2 zfAT<{JAfqi)Pv~4=NMuG@)XlO$H3mwIV-s+#oRXt8gpJsSA==v8@XuQt1KmJY_FPRsg2&#gTxII2$ zS>vIU`YYrIV65QNC%8xcCvNl}`Fe4z2$xy@i;v2gK|Njs zRv01ltK{xCaH0{21rMaT4J{51HCnX_FCWaR05;Z~HnHI}M^%;d1K6vPtJ<7l6vMU% zblL{kHV3fqrUx}$c>oJa$^a%^q3pDA7{F*M3}EYWqXXEhGiU9JT>?_=#=mw)=-{kE zX%Tx|Hh}HHgz79J7L_Je7#Hf?hUT5+(BzxH5BVWiy$Bttg!iV6z*xa#U4DZ>urLRlS*E9K(SKwAG&2rUO`fVQ>de*#K7U z&gW!@j6JY^OjG_bbnLXL`Wo&-Fo3PeFJSi1IUfJ(;58FT?J>>C(P9{5c!7_Q)iiQE zgkc6k!(vnMp$=YI>FMzhQU4Iv0`z&p&32 zwW*w!aZT@Nq^yo*7+_eyumpjYo{cRmy=j<}C(ar*_Q3c?oC8PEnfTVu9DHjhdAq!M zdT_`@T%?wlOvOi!haB9iZk}##K5m};Ryw~!d=B!No2N!MhZWyD$?QyqcQKrW&~Wn^ z_)y(kC@tbocShz^{B9nhMkZn+en6{;g$7J>8(Q>mh9(F30n?)i79g!Ah8FFcp)q$W zx0)FsSqnpp&dt!6whJ`D3J#-gI$D0VFbPlU7Layo(hGh&_A<$aqV+(6-Tw4oo zfYzlREYitV)$2G?>Ogp$eY_2{cQ=9!7WXecW$8e!{!2(?>d(#8Cs_3xp_HlrYx1M|Rz#$GYo`A1SxnU@Od_2^ z2mo-n+aSF94uZMbAOs+&PZp~FmAU#3O$kNy|BC$8zYwVYAIY^I!>7%haqy^G($W}S zX>A5qW7%cC0qPfK9fmQ6a$}779XC4092+&qkO#Ka>KlT{aE3Jy8Uu~y*%6AR1!ZPD z%*l_%MYSdng8T$4Xs7vrW*H4AHbMr(w5>n_12kqcAZZFbPo4oWV`m_N3DY#ICX;^O z4#_l~foe5Fqw;GoNK(CuVRHnw?OIG*Uye!Jc7@$*&rU3y4Rw{*tP?bS?^jH zTRP0x#HzLvT3PSfmK@o;tcY}tS+03GLGHw2+Pesoh^Y_)037Z%h}f_h1aq}P2tepv zWTCz5n!I;42*uv@I^?hRMqmrPmR#}W@G!Pbn>uX;ohz=ptG%ecTOiGb@OT5^oD=82 zZJ|ykV}WggpOy*E!>hLli%jtSNr{465m9S)Cir+3Q^5(7sHG4B037Z%2rsxo$P$UU z+8_iV2u>C%_(yZW8-$|ZlaargfQf>R(h=~sd|C6@x$}d$ zPXf4^``wx7M5q=Cjm+E&$%*E+BBIz4ndnPdOhqS5BDz8d0C2e5AiU@fg1Oot1Ry9* z7OMEia>X5*5{iny1Np175UBVmJG;*;YJ;QUKAhG4`^2xVpsKEhKm?3Mre3Hq=kNk(jixRHT`{n2`xgz zf}ENekZEp1i;ct6WQRrqt!RP;NUMpVNjrt_wmLNC?u;f_fMhKU&7Z<6nv9A}+Xb3n zg|)$v@Q|Lue<4{LWVKcegNfv42$vyObrr)$8LmU1r7po1mfAXW;>1$nR6f3d?3KH# zWVWM(5W1Zj+;+(5veAG{Mgg6&wQ*ce1b zgQhGBxY2+at{m>il$RyEv}#fJZU$%Hi|A;;|gCASSegV4c&=;&{vZ0mRxhKa|n0!hU-q!r9Mwx zzsztS!#5bd#qeDOcCNdzMd#Xi1)Xa}{Vc8taf4K@#ma9z&ESt|;9Kn0w|Jv;kA3Fr zmD!^)qpl_Q8I~^Y=dBA}GX5BZY*xK9>uWzG^=BA<#PBS`a}3WTG6h3^>jm73sBl0VAH(&%&f&`8zSntNK?QiBo`&mtox>%cR=zb`fT#Ky zuJ3gYmsGmI6;yy*yBMzTbq<#-y2TY#fU`{u*Y`Tbt&YIYL*ZEna#$&70S+CHme7v3 zbED%q`f887tJY#zn_(S>bs5$}=+M179w*Jz zpLMSd02sO#v5Rd)R5W!PAH((C%i+r5rS4^H0bZ!5;rj07aLK2YZ_O6qseXp*yO+Zy zl`e1v72wt`hU>eR!zGJuaRn9NY!k!v-Ai#}_acXtf)?P=y=V#TUb{59m!q%t$h+63 z$Xsp3unohG47)PyfxvlfBW$zoHBP$MxRvi-oW>e+ncZPLrr5Ra&brpVB=;tU{TSZN zus_3F5IS_Nj>kz89g}sfu>cHRi`d2XAu5`=jgR5_uH|s$@KV<@wg4~G({O#)a=7Hv z%C}|<@Kis;^LdV$K1J+e+YlA)+s4OmeV=l;a(Jmv8C!rC z>S?&XPdQxjY2{n91$e5T;rc%1a7m>LTtNl6wTt2UKIL%9qFY=+1vuNpaDAV0xFoPr zxPlt`6#bxmYS%`eayir+d7nBRnX5Ay&SiKH!yv;45ZI@dVw?4;bgpXYQ}Lv$+J~1b zhtFSlEYG9#9^+iQUU}TSWA)7HBS>RsR`2N z+6CZV5#XDd0QVDchxo)Vi^TSXbJO=WltN!n%LUXkHjnG8>m8+_n>8FJH=_(^MNiD%$S-2PHh@;b*K z<3RJ|{VA8{OI)5~CVG;4h08M;m!GmkxGupz{fwbU%dx?eJX+gh0WVc9;#qP0UIvZX zK+{Hf5y)(mpECn~mQ1QdTcWB1*Y*utwebRNO)byhi55TN1kR&zzR8Wc@_|%>KVna# zIZOiX6$yTnN$_U^sst=kl0ZIJOY&%yU{O(m2AWFnPaso*zc7QB06%5fSpp2!BEhpo z2_8%(7`AaI3Ak4zcs7$@ICcaHSf(U_e6AAkXq8}IQGy1VO0W))DZvP4@Dj*$)WHSu z%7e)8Tv3K^q%yo3DcYUDy&}VNnGBnf*$(k+l*LL)$mgmA0aS_Oi%KY<3{r}wN^A{e zDzP~;V34x@Ab-sFhTwO*9?M-BCJXsoiExNfA~%9a(kJ6+V?fGb`@rEzHRO8fK)J_0 z9p{VTAUcMRze$_aB-r%GTJE;JCS)Dk0VEFn7vB`Xa-(i!xlF%D`_Bw9CM~BEz>c z8ODkXLcQ@FgrI?Zt{U)Y)!-Ak1`Qz9;NYOac-3Gnr-6KfsdeSRd27sk^}%R*{{oGZ*YpT4+6Wo z6oIn8hl%PZnCNhsVDJ|6lEJkygG13gndtLiaEc6rpT~_F{Ju=-tFWuO8liDb$CS1r zBGbG~=?@cIl_pF{BLYBa?luUov_eFsrL;GAXfLA?lqL~X`j%Yj2B)a>^`KCF0)a|@ zl(cH4Y48q8M}L>Urs^J}IUZ)mz1Q9eJX`5Mb5LKh8A0?MvEt`a-44^4=8Xz zwaOTvG+219#?fQ?R(dr#K(Zo+7B8RG^yHBMrtJbvu);+sN+%c1`VMS7SL1l9jKNLv z5WrWEtGbWjA%<@u&|6=?7T${Ay6V+$La*pfe-%X3uh(bagbwcum&(5leVc|t-~KE& z+P5FcT=fKYRX<>OlHn09a5JVKSr+V=L|10 z{1$=6`VqD;)@I=zL29h_r!P}yg$pcl%_2NxEVUmf+UlE`t^Ptje`Vk`kLqv2ESsk5 z!KzR?aV}trSfLE4gmekz36ycxu2rb!Nj;j;G4#JvHQ2Xs^{7Ml!66Ku`UP70Nxe$381J zTG?r^JtX1g#Q8;IeJeB8CM2~f!)6Sd3%yoj6-tZN^RroSOTYwU5wX~E#6o+exed)5 zOVQ#vxExn%JR=8GtBe79V>xkQ0*$pbwlLNf>0pIxB`amE^fe96RaMbdk7lmgmyCEppn4Pk+E2K( zx~foGgsw7I?a!*|a7DzT(8NMxrMV5wyUL--K_yEWGeBBR49#20p*1d$#ATYH2^Jt( z3q$jkQZ%s?)3(+$t7otjbyG_n-msM8sTz7qO+>D0GQ(7c83?r00ocM)uc|E--_X$h zoN}_NlBZ4Lb6n0$Clt-}?aWL^lFLyHM>F&zbXZaqN{hrNWM-NJm|!L%79}PYnkmg~ zXx>Z?O%5uV$(RAsYGP>KOb(5?Te;QD0LfYynm3c8iJ6$T3pBwBW}Kss#*77*0i?nU2L4W{N+Ghs6{g5=?)ov3W@;aZ|YDSDN!qEE?)NnW4@ipR*ax zVK^6|gP{tgMdlMTL!AehU??IMWhNGyD$Q+Z-cX7buQ$qZB~KYSpju@N(0j_!WBOKl zH90`CB8KKYrD)+OxFf3!Z5`mt&0Nci5YRvo< z*($rd5MO&$GS&iIV8D+H>jj2M^%aIsAhTUzcsldhb>w(G!wn2KA~bw}g!z0ZLI8ln-3AdoQ$xh{IMcPE2u8SeNFrjCo?G*4hYe1lEvoN> zLiGa#s{bhI*4GXvPt!|>5VClcRx5)`hm-ldE5E=I@8edBu`(6AEy8?3xp7VB5pMLF z&ht_8aHa5h?5bX1_%Q>IZuH}j4YCzVhg|8&_fMHn8-j=h33n7MK&H73Ew%|S#W^$* zXhjn&Kw3=<&39r&i#MW~zB8b}0o4hczBAF$W9BZ<1S|C8Mb?k+ZS-S@tTOt3{33Ey zFERX);jajE($BGllj4FFPQrH;&@1s>9{uUH!dNT3x>umEW_ zF*I*2hsNBU(F6;Stc9WZ<%XigE91=E1)#tMYf(D2)>j+Wa#U4DZ>`rMSG708z6|># z&|0s>7S@VqOEAKycw=q5~lJ949w-)}NY9fV!l ztOxV2NeGS6s)Ylr8a#U4DZ>j0XRUO7~B*QTXv{WCquvC0u zaBe9+{nZ?=I(SN0OVyVa4fWg1Q1eMf#)(Q8wGK~dL7yh|(uu5)da1R&h!wrWv?-eR zl0%b&N?tN%fNY|9FFCZ1+-gAr%OGev?Kw22EgH$V)y#sCmYR{i(lC-kR-WESODWjt z3}-T&i$EhS#1=-1FWT`&D!+BQgNX+1R)$pk0r9-aN zPM5G+Y9}HVMJ86XljSxvZzqQ)2bJt(%mCR$^LBD*t#&dr!2%>}Vch)Cq-gOxj+t94 zo5>s;b+S3?>kUUasw$&*)D_58UBmD(h8qy*s1INZN5vbh-cj+KdBua%Ais5Giz!}u zmdc+}wAAl1OWjI3w=sO0;dX=$vt6Nd$dy{^PQV095wWN;vCvp)ZbS2ya%gf;$x_A) zkX92z^OkaG%-zbZW(G*s!qB{>92(PhfhJhNPp6ol9%}f>A*+VoPoGDw>dOrGF+7Ao zKi!Qj{1ngg=%<5w!&O23MoRNibFz5KyoBYH)cVw-mwuml>3gK|eTK&v9!Kclr9$bD zEA`S30291K#G=B)LMx@Y4b6MWp~*odFBvmHT1^bid&!|OcPqD=86a5;L-Ss8XiVD$ znqUPlooZfsxZx#-tQvYRJ&Rn`PZ)l|@M{Em=_zd6yd+;ES<#{?^Af&yA+Ld&=$ z-^W==FJZ!yTAyC@(jPM~ZAvPeF>Ee;T31YI!DQ|2RssNPIg`wOd9GZjjUncfajRvFmt z%w^#T)dN^5U49d>C^50nOtSR1+=k}OGWdLw;FzOR8>arr-P8I>SZ{DVI~6oG!9$%DPC;CCC8Py*u*Q8 z!qe8?{$kTRikA9wW~rk|N1C;te;p&lS}j#5EkeH|v(#MHOD#pjqRPaIma^Q2<}KyW zr|t<_S7CRl)EEsUGDl%mCpP0ZX{*-YkODN3i7dbD9FM^$C?mYR=T z)k21)45uN`QpaHnOT~*#xusTgv8mxHVJ%fZqiCp?GeezCGGeH6gi))P(tQ+Gz2wm3ppuu286cZz-b)Ux)k}sZSb=2_G`-m5(3rMpq!dl+8H{v> z8R?r1BROQ{>5X&|1-pb{8N*cwG}8Il!btIAlQ+_eE;cnxlwE9kXVFQ2$((c@*@%;_ z=U+DnuU0!1N{h(v%!a0qvs!8=A{Ip^R}1RU*+lbpa%in~GBm*g zBx_;Z{LrLm@nRD*w^lZjIXH^a>Cp7;hNB!+mC-xuR^+PgWcVz@y$E#FP1wRw@nVyA zR5ur!syB67L7iFj)L%1C8ANqInLQxPT0K=LElQu6dFmUin0ktcMVW~eJ!QEK&3nqB z$w4Jg88bjO(Y&V|TC1lFO|SsTS{OI)DMgDbC}wW0ta{4G1y4~r_0*3Vo^n)GM(?R_ zBUkkp!&3}DLZGJ}#x_|&t<>$hu@gGou6tL}Qh&=V^>fmZVd}s5*Dr)vtECF1Md){B zmiiUzrIsRMQDtI9OIdD1^OkaGa!|=q#te{6G;b+~)@mt36D&Zo7RJq6O3~s9ikVw0 zo5>t3Md{R1&o(UOsH%+KQolp4>dy>+XZSY)E%o2nCM&4aQY&6TWu6k&QuVWnhWdME zsNtJ}j2LPS{MF&2Q(DlcNmo#70%k=kC}Kq~F>Q+Gz2wm3ppuu286cZz-b)Ux)k}sZ zSb=2_G+jYCG^Q;YDMgcd1|ywiMtY`UB!{d#y^+?ZV6S4>oM99KjkFfF$qK4A(u!A5 znTfI$)Y(NR{UdYIPGloa+L?d7R(Q1zO@-1T^4ZzYv@5HnLlY5;A`>gx$#NT-x06GY zgGzQXW`Jy>c{@3@Ry!G*U;&b~Fm8TmQna{&V&>M$W-WvKhGmJx^qjtwOSwYo~>ShHsIFu{%?6P>g#d1#3Qvb{>)k8YH41Ek!5IT%dh0-GQ zIhm!V0VY_Ah((o&g~m#A8=AM2Lz9C_mNI65w3-;2x0FL;?pAI!GeEKyhUP8h(3rLh zG{FjfqHgM^#~OZe$f}|D(_zR}9mz0<;dli4X*#y>Q@mZr6}jE6Tj4F1f|uegmUD|< z`d8+qMWnKr;UtD72pzmsC@l(~n|bMEzyvQ5v8XVy&`N1;L-Ss8XmU`=OU4Y4Rue<> zUUF#6-O8_?vl-53xCnt>Iu+YC zFRkzvOTkO=7R$SfUix?DrOQZV8N=nmr*)P~3szU4w21re%tlvYN3apg6vZVL8pzV7 zXx>H+O%5vA$e00=l}ORNjT{4}Dy9I|TYy>uUPRSz*d%J6*zdg;s9ws~oVPowbKK+96;{GyjuiLX5iUt)QN zRDQ(pEW>jM9lTU1EefBXdFchf1TPV>s4%h6N@;FG^Imdja!|=j#te{F6GQV}a%jxm z%B^MwNY=v8yq6pr({_O-SiwuwO}+HfhL;?&YUsW63*@SP&G37Mml5ctpJ1DK$vxTB z#Zsx^rMOhOpy;JlGcWyzR94vxVO5492pzmsC@l(Kka=k}zyvQ5v8XVy&`N1;L-Ss8 zXmU`=OU4Y4Rue<>UUF#6-O8UqFLkk0YIrFwmEKeI(vZwc+mOn(4BH8x)}>Nfu-YD_McnsfHrf$8 zf{j?FC@!(kK$bQ|^EPs5a!|=e#te|GM2hBZ7o zEM???YLziSZz)HQ>09a5b+*ECmWV>$SR|^)M?07oxyN6 z!}$ob)DmoADSx{c7b*FTUw!#V7ptbCt>Vh*qN1&aWwv@hDP6*FDZ>X4I@qdET9m#h z8>TJ;Ot2LZi%Js1+DeWq*~-WP)hc6v-d2tt z)3?&A$pMlTF*I*0hsLyBpb1v66{S;KJ>9UCLsl8Rt-gd@)z=svWcVfmZS^^9JK3s> z1y#{jaZxo`wAJv;R*#d?6AV9KcoLz5tqP?@>A}oaPXi{{iiky}iG|ima~qnsm7+yk z$#ErH89AU@Wem{U%F$!`R(dr#K(Zo+=56KBn6?Wv!3wsbbZV;~Hf-gPRYq^C7m%y^ z8N;s_ev3d`J%ep0TfM@yRa|U|0`DtYYmLlWe<7{EGW<=rwVw8+1*^ZKbV!pft^SD} z!Bs3%6q;CQEK8fBc~>b~bd?-ea+Q$-D(jH~dRIAmOy5ecCI`r*vffoGJ!b9#P~d{G z-e<=8LBm*%sw(M?HFR^-ayY|U4C^7#SpO!4<%3&=R|N9bU9!5;e-yu3Zm+^9y|2K% zEb#q!7sfh^`{yi~fAT>8*nWN465ndS6*$-%G1kd^wi#Kf9e~i75<^1BS|}|lUy}K0 z922Uah*(saSkX_G+t9q99GVC{s9H!S6-s*K)JlaQ;L!Z3s3Fa%m^0=BSJ{Dy+JREJl4o2PiIScuw%1~+6~ zTC~)9nWc^)9T_I(@UOW-taXSglop{c%`9~s>!p?=Vo_ycMN3(3L-Uq$XmU`=QpOCB zO*C&Qht_H-LlZ1OvKGe8TT0Pnh+^8-nr0dYKT$XJ({~zvay(T-@23-xt6I!(3d7qG z=%?ecbwdfuf(*&-`>Q>4=}+&A-kQVy%8ElopjgkooBX)=T|F z#G=Z?ihi=(hUWd`(BzE1St2EJf+m zQr~S@%28Dry`|oVT-65|KE!Yh0xfkRwr+^>mg;VZ%AekJL(~V0mf9e*)D5H~mb#ID zeO!pOTB=Z5g#KV=she0YwGc}qF8R!bS0U;&b~ zFmB#biY7x8)3(+$(>VBvx~ZR@Z}`daR1LkKK8;+}-3*^+_!0vB^eJqUA*z0Ev#TL0 zx04^DE-U(J!^}?)l8*T48~p1bA=c`rLTOR?vdmA9uwLpXA{JF9R`iqQHZ<=ihb9M= z{AA1k*+lbxa%io7GBm*gBx_;Zyq^>;4pGe9TG>qIU@1zcmU^LKDMwXh^p^S_a#cTI zc!uG51X}7_*d{|%ZK?Jls^gg-uPKb_pQQI}mZj%@*oB|w;4+9 zy~w}*8=arvX_yo`Uk@*TOh24 zK$rcV6zX4i@@w_rmg4K7Le2GoJYi~!^~7bDW9*`#a0d8WZuAUrv&>a%laaV;9saeh zaLdN7kP+e*N{iN)XRcZwFu_$sEDBAm=qk%?Xx>#0O%5u#%9sJNiRNA9&{|z(Xo3Yu z*21`XS1DR_6*IS1Hj_EHiqfg8zSnS-qpC7`S8ak^)s_sS8Ma5Dt2V^exymoEx^z`O zAttW6qUfs4Ggs|SM&hd1@vl9ETdS)IrA6y2GFR;hn9EmPWz|*qU$7UUi;5F8+RI27 zthX0o1DYIEvX@Z=WE0KX%b~T}%g_W1kgSDq^Y&7-XfI}Nt!$=uuopE{d;Pd!FGp2n z^!C~pxvIA?Okg+|f%e)P+r(b=)Y!pZac&$OF?asSr_EnFZ_(mqgZk@q^4jj>lJv<@bm`3W_6)qLQ17!;RcNk=&{Sn;o@G4s84~?=g`UVWs9Db&U9%@5cp3 z_?=dkNm^Tv*3b24d3m&0xu*w}PzS7zA9t=cWB4(C-nV+0VU4x;8N)K}xQF4;;Rp*E zzRK_f!;1_fkhdDcBE1YpGb~1!w-hh6DRLCwR40HsEpj}ofZyS^*5q%~Pn{kwmv4B= z+<`TAI%nCLP<><->h!iK^Ky(YlohkqvE1ma^@dbimTuRUdqvyjnYO18P}e#vQ_@yG zS8aK;YTNrisBMFzYWq%5r?#iD3ivhUMBA}b@HTCI&23TKfhg|BisFt*#bxPsak*E- z{a7aM1+};=QxaD`S8;i?ire=;C~kwJiu=AG?u8|BXRW}w^P{-e7R7yIDlSX6i_5(t z?zNe?AF9P=nUc8jxr)o9Rop56gW@(gs<;_fslvI^>R@=0*kn#yV%pq;@(> z${dNQlDln|lq!aw57${TzLaVS)s<*tKp((p8MMU$fikfeeY5p`R zaJ}5FxfKz$uF5ojhO|xd#>zXWMGR=p-G<^dR}_&*iWUSn8bNRpQNbU`1$SslC<^{# z*7m;;DEPDFw0v-bhRs{vW6%`4et&@|esxjtZ8ODRB8T4zhqkq|6%nnj&J_Oxd7I)y zD!H5(P@KCB#Va0AF2(v1zLe4h(TzzEom^D(2XoOCP?}IVMbZBX3e`UmDEgmBtG-W~ zx?Fy{s_?S`#j}`rb}Z6ggOh~y>?C2)VxGr@YkMQMbms~?XN|Esc2>g?8ulNC59LJ0 ziinKYWQ{SB#jG)ilr)B-RK!4IaJQlO#t0~@tcPN=w6t#whawoEF~~(5;~RNnD4;Zk za*BrPKCIVR1D1QfW;Bx7Ios}8J8Vr_ql8x0 zDYha!cY^dbf%=Wavsk_VLVB!`st)(;J5OoHmu~1O%$SA8D0l+ZEqu7+h`I%ozvcYqVnz)ZgB_9+qEoaX&xppEebXKi*O}1PRRU z2V~k$A(g2@C9``UdC~4xL{z&z(|!hv=@~U)65$m>0D!~Y2H}Nw5X{vEApk*jvQX8p z%2ju0N+_y+IIydJ1gbuhiIxwp6Q*1$HVP`1#+gd15yj?ea*usf?;GH6$_sx_=SKZK zHq&?jyQ=vJ4S$=)Rzw84A=CH-Vyni4DG5XXXw2OP;Wbu>_*?c;+M7GHl+g$>lZeWE zbuM#*Q8-!QcK}gYp#Ks{=OcpBfhjWP?ni7fU%S$SSY*@yCk_kIxkT5Yt{MY1M!Uf)(zid;&(&dnxZ|+)HuD$+_Msb8%VYUNEn|!f-#s!wB@%XRw8@;`6`w z0bZH)!&mrPPX9us6L;AS;Lxo_$(zuv=_gE2cX6ZB)8x!l-^Z@%F^0z(oc=VfC;7|Vo_&ep{>%~hUQJBXwg)1T**{M4yaZc1N5eH^q9VtUQG^=tcaocdvt-` zGaCVEmSpY%P~d{EZZcn;(eRa{s!E0tN__PkC{;gU_yxm%BhXh*V+&uoA9nV>Y9G3) zj^480p890bSQuhoHLwYKZ5XZoL`HvRc$wiZ2px=7C@orl(oG^9U;YM|U@RgQg(eo- zE6r_a-dKtjjU~sGjAi72YLziSZ!AZT>09a5Eiqh$5_0EQ? z9J0#jUA4+qXs^{6Mlh^{Kv(^P6zWSR*=W`FU8}@WBNohC&_93Sar2khPny+Z^{0xq z>dkERYEs&SVN(Wa}K(i53G%`8$Y#eHq?@K$Gu5_L!2b9eD4$cibpm1Q|7* zH>>)_tU7RA&(Krm&cR?Wp71Q=lEjP$;moLJmRM(wXl9mKVy#5F2AWpQEpTXLKV&#G zrtJdFFK~#aL&n*SA;Tdz7dWG-;eiZ24AT&(=9bu|6H%?JzNes@MULw`#f8qz#et(Q z8#rc@*x?LEFdT`{=ruuav{|9FwBF6G^RV@f222<@h*;E{Sb$7(8(Qo}HCnYj511t! zS^)(Ps8$&R^yYH(n7%WbU;&a9F*I*3MT@6j%-jW_z=bJ^>Z!lZY52=gRVA&PVgWr5 zxvKdL3mKLm&|k-33x91LPRuYR<^Jk+u~YJxEqHDz8f;2turtW)oeXC(ybGa&!3w2C z^II~5oeh{^Fd`NOCl=Z*&24DjV2TzECdZXVFCzz3tBe79gE@Lk-%7702S`@L(7eGM z8q;=xCRo8;luq4sZo^#;S!ML@dJl3{7c*SSa5)0qbuPAW*B0&WYFYH8mjGmdtp_f> zuq)NS)vf}-rv1DUpx11#M`D}x56x`0oD?|%S0CeF*CI4*_ECJOvwxv=W2>$x+)Ze#5T)9zYf9 z9j*fCC|C5@Z5X@gCtL-%fg8OFFf%jNeb`mq&+q`lg9sf=RVXb|- ziG}7$a~qmBm7>MjM~*A`%E$rLDr11&SB@Ujx6-T00g@FlH1DfGFWf+4<}LsQF8GS- z>G1WghOZn|Rnq(FJIGZ%#_$xwj}YjqN3cy7Ut*JRb*Wo#aS|*V>(fPJy)`q|&&lY& z7=FRieE6t zl557am9vzLs%9y-@Dc%TFWTy`%vNh`4N5W*jo@EvB6P4-p>)WVPJ(LzCfJIIMWu;_ z21|1rnzxlhlY>gOGG>6Zni!h5l|yTt1Px8F0LfYynzxmr#dRPvw^lZ*YOvMqW~(C` zwsKTeMsKSPk*nH-VM~VBAkbFpV4DtBaT2r@efjom$B8gE*BwQ3&C1NRGl{*HVHbv7 z5jvQwP+BCvBO9}J2TU**5sP{g3(c11HZ*T8MT?W699Qy}kprq##sIy)96hFQrB{;! zBr9TQ-d};<_8cr`N#-s91upoD>glj`R>NP8sw(OIwGSv&`!c+RVLSr;wFkEFmpfDR zli|vEESV5>m9hGUW3-Rzz;F-{m~SCznrQF`Nea??ctp@O7vY z;sg0#2Q@rwWVGdS_1QSw$bB%9)9*v^$i~C@?~1gzR~{Psx?{xe8l4*$VHjbVq@nd_ z{ahc;=h5Qgo@u-gEVFd|eW?1oPo2Kq)W|3XsNM@AZ0qw#2+t11n)3qjbo+ACh~#nf zK=8yU+I<)vs1Z)a&f!K+#x6^RV(E6FxL1U_FB9rA0_qt&%anwY&s8WMtwJ60iiK)q zRH3d05ejuV3FSid##y{5)Pg9~{Y9acr9!cEyHMOKLfxMUbwe!_%anwY&s8WMtwK$I z#X>bQs!%rvp+2rct(!V(d`G1gMx`DoDs_3P6ic@&#l51`1DR6y)Jm~TNh$eUmEzH= z)QnfGR3oD*^~IpnXG==;c2;UpRO-Q^Qdgu(v2?pq+$%~wm?`yOtrW|Yl#Mn+ZYxuDe3MWs5AQzu2G9x5tzWvUcQw=2cHqSQl~QopQ~VwsXs^0_L- zqgAQ5zG9^s8C9v5f>JM*l$zezQ%j;!4;PiXDpiW5+m+&8QR?ALslU}qu}n!R`COIa z(W=y8uUM%@MpbH+Q9AYgqo|bk6u*wgg<8T@7iqFY!%B_2ZILE~!`Jb2kruy>#|2#* zil)%7<8i4cOY)v?q1twP+^_nKlhg*c-G-w+)yPn()3j3YH|uS-!iiMNxAC+-er-FP zh57XwX~(D`C<6o}%V+GR=1;1+JbO=cNh_p{$6gbx)@GE~IUmH?CL(wTJ;I z?lu&!xuS?fQnVnr(FlT*hzkDgTyTe`grdz~&)U8Lfr9TwPW40UCb*4r$u~|*CveF( z!ak$V_2?;F|NPVSEN%jl*oo5HGV#Y^XEhF?A^rjQP|o74h{*PtO#A~`%)}>BVt7TV zhyn4r+fcmt0VRAHOiFwG9g1KC{mDi3|4y#I0!nKtr>K7~C{)uBsQTuFJ zg5gMpVt1I0J$k3LP&%YcFESs)BeGNMKpW@ROEG`R>qfo;ro-i%gs6|4T%Ujgjq62` ztDao*u*SqWU$~)(xmP;>SDaCJa=jpM!LZ0OiA(fo{ahzb9xVpxnMHXzzdz)Sc)Z$P z7}Ch2)8)xX&n90)8p{XQlE<w6u?Y>2G*)LLEq*46>rqS23DV7tA0ha-%cI#i=+f z-7XIIia1}*#CaD1^@N&bO5(`pDh`iUab~ZiIE_3i&iP1Ban2?UFV6IdGvcXXQJhnv zIA1G@Gnk6Q((U4KuZZ)tOq@$bP|JAq<>X4J^Ql32j2y#`|GJKrjW(1mbF19f1rr}uz z?a=b~wVOLi=8Y%hKC0W~j#LaOu2 zH@C)p>(!dRm1wHm=pQmUYCUgtxtBm*2YK5F`qtBi*0(ClH#MVH{a?7*-jx}+lHKdJ z-0n3!)4e`HyFLZ^4CHeo-E^;<-1PWunW5`TQ&a6;P@3y;sIv=~u0B?n>|S1>AG%mB zuZP9$Gp(^bvBG2zlS1||RA0Aw)yA1h7!qqs_AuW(-@{PzLMWs<-^1WQjb3lo^sq!z z<;i3ZE39o-tuM$DAWIuT5BnNTIInAs)O|5IdM$hx%M4)gj0zobdu}J2k?CYBqiL&v z3dqJWi(T*etNMK*`r?kfV|!wS$$ln<>}ROH zZu6>*GnFtT)|TvNzInc%q2`59NOiuS-4XY*w`%%XqN(y^vY+i?b-BGkMuUtsf_}D( zq4l#N-Hv4SI$CA6tlmTo|Icmwne;nt3dD9l*knLFZ#Ld)kLN&|Gd=E5wDT~K!$FQP zQrqK9=;v#YC!L#D;Lgmwa{@}lkNr@Zz5-I{P`O>mItr6LP73vFvO8_mWh!H1;^rm0 zo3ydJq3*hEtG3Kk!ird1vb%X>{hS|aUI>L$=erxcryJ+%HQg=IRJk$P-6mUI?l_Pc zAhV31yG=B-?pFCvZ27}l{;Z%o1Z9`1_O&O% zx!E9dK;|0hriXGSs{f3aQTbvb*A5_I6D#OEguUO!l%4 ztIKtPoCR{W5%jWF!fucB?0AK+$;KX&Sb$qm(A^Lk>VCtk22 zt$y16qfzY)Iwgh&C$HL&1M6+bxdosh=f$)O9@M^{de}zK10D1CUK)GCi(sIEKxh|;DCKo(xn&E=KqlSzNLIg`)7xWstuYB-Y zt`A;H``{_lU*88vr4L@q_~02dOFlrJJReXlAE2~+aAU3yYP87*FPUce;5pP_yWLXV zVBZBeVWgb!dae^*PCMaktDwFUkV+@Ko^isvXqTLTJb6x_Tuwk~IpL;UC)8+@6FxD` zaKihjA?pO&xY|um7~p35@si`j8@aA{CGCoDt(y9-Kq_7FM#dF|p>jofu&Pv#!Rk<5 zkGE~GTEYgaI0+xpTgpnuM%|rl?{43)1YOLL)Qj*QrAYMS=NtJ zD6MOAOYXI)(WYxN$TZ{HEQcBpq~0owQ-|WxtXnfq`MUA-e_oV#?4rPY8>p5BXrTHy z?E~bl?*pXL2k&HjFa!$a1LVo`0p;qSC@mk{n(KoaZSuh|s{}q6ipnxR;O@bCKIrN- z#`lSLbA9kh+6SAN{`x*ZDt+*7#s^!VS@Hq$A z#Au=)n33Id1arb33XJ!1o$zVe3A6lZA|iEjgM+J(!>vn@xxm3A-?jkX4>#*molwJo1TM)^0714mVAhpHvWv| zBUADRM!q)c@=x$DnB(+2@y?nj_(VJPh)Snn0^hczU1cqIhnU~okswEVPyPl5RVP@B zV~4~()yiZ2uh{y=cGY`)_dgryg#TCo;Gx|{!b<=e2_LHjfM0(+4lSDsay-a1wlOom z>T9)&mXq7pm40%cVQPGUKx6LZgDz8*GnFtT)|TvDzIpx^ENWf|g;eJ|*ni>k7EII#42`)Chc3Hr>AGWW$qpuMl@7*oc|%0pIMW&%6Dv%1utf7v zeck3&8)qtENUSZ{!K97D7ph$dZPev^7hI>|>z$h3m1w8jnCxBWSY7T{AQymKVg$YG zCx+I$Dzo={>^6GzcOSs|pjhqgzVI+Bj1QLt<^o z9_E|phcMK<5DKZzcQSZSo$TG3PL^n@Jelldw^?28E|7ac?l*!?cB7$nvdYwbUMH(R zh*hS!+)n91jL&i}+Yct&RylK$IRd|x9w}p0*~q=Z)YBJdWm`*R_IQJQ+9gfH=zWxHrLmiRJDt9`1IQ*DJqBtNT~@ zT*LnNrk`8%^J?!{UGX(|t8xwe6AIM1H_)^7 zGQ6dYHxN|25Zb8A$M&0XY`<3%+lh9{jmg;l)ar6yf-JUMyvpzr*63@TmhE#q> z_>XhcfByB3wCCS(xOvocvEu~6@iMZQ*qB&hvUeq#hwAG# zui7|M2}5FS$=>Cg=WiaUc_9>1o$p}qo^BrR*L1K%Q{~BI2ODg4xivtBf~;=@9c)EI z>tONoZ%qeV@U26?u(TS&+s6AgV)es3TV*|DORFRvv%aqMDu3VD49(e`&B^qo5ojCk z8*vs6`tK(?5XR`Okxj!B)TA6L6;xn)kQ}S1^qQ&)20iMkr-BOnO#`jaP2;-kO{1nk zm7B(nrrYgm1fJg(71>;cziHSnkNUAJGZe}RM}J^p3*TDUZUs=-ev@(VC`)ohBQ>{z z*uilYdgp_TgGWQR91Jyi-hl#hFp^^xNe4>>9n7rBnDo~uZi9C)S|JBNk#%rQgDMW* z-|BY<8G(bxpiKz}k6W;V{iAlm#UJLn_}h$&k3zi@SZ}>P9A}|_KFqjyBHAh!LrtEG zp}<^>jM&RN}XjAn+DDcs9LA}`R zf@hwDi$BVB@nV&)BKu(irQAuZx1NjREcDMu85f^|w#vm&ljmY6Fc%{^R*`hERM5rD zn&aZQ4c^6Qg*bm@RlIz)HNjnP1YW)z z%}RKAoc=RL?vJF3m;IQW^z)~=e(s&|^X;hl4%S@H&v6zy>C=p#??P+kXQ;{ZGZdJg zksPZ?`dKO}erE0*KgZ4Renvy&=f|>su4z-n&wsQgxCe~D&v&C))t6m4E-$!0_rc&2 z|7#nL8=vKRx=+T_52Nl!Sa&^7$64s6&oZ8V4DFStp(fAMP+*=$a;zfhX{o4qnz?g4 z9XG>!8V!-BpUQf=rcD)3KW$BL&l`cKA4jvQp62je-_!AI%dVMw;OBm(rNMqRV5a3Q zQyV`qFPm{U3cEMhfQ-9eM{DrJ9A}}gKF_%O9b}Wcp(f>Ssh|SWgXCC6(%n+QTOsPI zr-BN+uh9zm`su8%YZ_GX^~YAf``ie8{T|v{9X-bFe?a{^VeIMMGM@fI(uot|o}7D`kdBVC&`V!r9Niz;~&g)$Vaz?XI}`%I?z$CYuRZ8=#qh z;hBgEEJ=N0G`J9iS8j}P-9%Kk<#=VjN(_AP+E`QQBMQ26uQ;?;A}XgnvEF1vCG@g* z-W88qi+*W!WbBOfCL=1Lw{Aqmda2cvr~Mb}O-58gFPc)<51FfimSd!Y?_#~lh)U>1 zD;8F7C8AP#lM$8BTQ{Q8e#m&iM-=*lB5Jdmh)UFNho#MCKh*r_0mAWa~=cFo3h6I0!m<2C;} z6H{|cp^qu(&b{W)YMrNEdt$xGn3CSgcc-W!FRbDQoBkYAW7Wy9N;DZws@vphgH3tb zg|XgbSS9qLDGQ;OS}S1%_r-dXVU^H}RxGUEN?4`zCc`SBw{BR){>}5Q4=Z#Dh1KRY zVU?&u-6I=TWwXfr0OU-N`5-?BIoAlnYM!BmRpotYyHVlwRlm@*(D$m^$f~(peUlqm z8)hQwGPLbCAeVz&0m8_-(ztFStJ`wC?B8S}>nc;|BMZ87FFUka|EbrWSZ^}2q_+}T z)Q}fhaf3~Nj;XQgWMn0pj3(7>a<##xJnh0*Z!)qHdeM}H&`YhA$b$P~y~)T*=tV0Q zR&OP;QhJk-mC#!^vSR<{dDll4x`ZNYi<-zv)S>Q?jjS8YB6kbOognvs+z;|MBZ#bP z4C_Z$H}|Z{&)DH^5BmXO{}v|si>h+-;==>I{;j4Frbl*$X@q}Uz47m>%`))?jqYD) zCZpj%Q1>JM%J$O3|@+j5l#q@!vHQ@fz3f?B)6^%_)FoD8dk zVwO+dvnt&*Zf_;5(w0^gC*vxi7%j@}PqE@kT-8&Y46KCWx`CBe?4Z`#9$%Yki<6O+ zP|O0|x*~JQ6Oo19vu=7FXM=(2`=^xVEl|twbgHv9+94?FNCY1~LR> zJ&+BJAhwn?%*K{(SKlqR@@`v+ujqt@{OH;u6J1-PZCimby0!+l4al}ey18X_TaH)W z8jy~z?M0#;sG) zLjkr`O@Ji~P$$XWymqy!-AIrk$i5&4fE;WD0k(so1z6<^LbW%q`d<)QnEO{U#(L$) z*p`_Xn}FsW4Z;|k2<{k=Nk+Pfv2M%pGFb!CF*eyW`WS=q+-n52cCG6*sH!*_V+qA9 zpBH0sdrf&95z;%a+MdwgxGijy&xP>gmg zM2oBKHSM@ARmI5|lj2H@p%vdrH#PehW5-a8ZBr9t2?NwgvN1Nzs&)+^CxbMDoCZ=h zf*6}(n2oX2O{{K=#XrAtX5lmcBP#4|;nlVHjPX;Zjn@_^`~aJ8RmVTTwy#8*->v;4 zG!i#vcNVxG8>xw}Gfk+UT{*dVVXWS2RAEEyPfhOVkx7J zrIN9fROcV~tF~0tRU#;>&P=b{x@z03ov@>-t`av@b(Q}a1wSr?M(Xo@kM3i@gdf%H zsI75jqVKt1TV?J-kjp@>G=koCj-mCo)%;GP#Bxvf|1-*YXXbRZ#8&OF4@PlxEBhUC zHbB2azL)7+H&~Kfcq6!*jC9kta&pt%R$01l-GUm`x1ciD*HBjJTXE%8b;-V!P{&ev zeJgI8>C91=>{|(SdfrW}i&na7td7b|C+w)UF4?!FuCk>Bb=S2w<7(fx=sxwWk8Ao? zqOo#ivTyy~Ds%UNJOJ`{Bj{VV8Cu_}{7>OT-&*jVli~h{ZerZ3{gvAngP46I!Df&W zqu5hcZ9IzYn(1keqk&I=JP88F*Yvc1S&BxnoZNK2Rh;f=&!9-1IU}#mL782_bmdib z$(|;4m2rXvlbtQ8k@`$+Y)(~Uva=-`hz6v(MvXcuGoA2GRb8^PCDftfg-}OjzNgWB z>SQM1Qs7v-Ps#EX!vZi+>RM+;d ze(T#sTLNSmkQI%fcNGk+cUAt|FnSjUux@)++~!fJxXNqm_M1uwlxy$x|n3 z%6lzS9gkIeS9+7aJB5NZpRia1ZNZ*7q+-(})K+^7J{7e#vZ%KpQ_6cP&cgbji;A;% z1RErGljLe*D7xWR0sOKKDmt%g3IFUcX5Tt{LwuzpD~=d|V1nfqOhW()rhU@pBYS=G zkxKJTS=#(9pi$-{Q=a*h^V=CAi_82Cv*y=mlKIJGDNEabV%2_R%Cnzx+0Wv#f77h}HJW69v$y|b z*HhBf3afzQu*eGY4ef2zWGR{`KBywetXq?WXdz2a+%NKGJizY{2EO%{|s+_ zr_8Tj{yn?D{6WS3rSt6{leQn(>)Vf1+HcCz_McO=ADQy(r(E{4xa{97Yk!R<*?*q5 z{}-~qdinS4_VSOen7>TE`TM5LNA~*WBbDZxvb6b^SItMJJo71+`7AE;H_w`1qe&?GX=2tHN9%B~}{b9xaW%KPHo3>)Vf1+HcCz_TN^uADQy(r(E{4xa{8|Yk!R< z+5ZP`{~fZwa{0~vZZH3siuudso4;S$d}Ob0K2mAEDNCFG*Q)u*lxIHWGM~j|{+3zu zYc$FHhrRiKm-*Gpzkul9w_^YD`Sy=X+mGz^?MEu@H)U!2pRU@EOnLTGF8f(r_HUK7 zzebbnf6?3jtn9B|{_dlHY{mQ)^3C5rZ9cNsHy^1q-;|}zf4gcvGUb_1xy)yAnZI?` z{2EO%|08eyyJ_?H+^x=*1%K<`uVVg+`Q{&xHXqsRn~zkQZ_3izj{Mns3U|<}ZU*>1Piz z<(W^p%x7_#zirn18ci~PkT-w1wE26CjsK%;?t|a{74rw>oB#c^`N&@1e5BHRQ%W(^zDAR*ALXszGj06>p8CIEv3_vA^@pUbNA~*G zBbC;hvb6R4SFJ~;JnJc!^(-#ycgR{_qe<2u>a9Oe)>rzzO@wuS<2$Hg|El@+ADXrw z+3VYnRN8OK()J%ywI7-C?5AAzv$*WvF>8N~CfR?SxBu80`@8S`2UqN0E#Lmb()J^J zefyD0`%PKe{-|m{GUeG%x$I|g*}qfP{u)iPzscKwQpWxR7VzA6NX7ou^X)%8Z9lTt zw;!pr-;|~8FIVkHrab#8m;Ed*`*+UTU!zI(pYHAN$lAYvpFM|G>|Z0_{v*=%BYS=O zkxKhbS=#=eRqaQnJo_n^{VXo~cgfmcqe=Fk>+S!!?5{lcjvCW__#aj=f6aXJk4&47 z?Dfq@D$O@#Y4d+mH6NMs%%@!Dv$)LPHEVv2CYgVYH~)&P`Q1nV;T7}O$~S*}+I(cM zZ$46Kz9~zae{0ozWXdz2a+%NKGJm(M`8Aqk{@vdE+hu-b^tY?u{hZGc75j(e+kaHr zeq^t2KT>JGDNEb`m#Y29lxIKXvY*9e|L$4)Yc$FJhrIoN%i7<4^mp~PFDvqk<>A`- z4w#U3z`sp@eFq?w4lreD2Rw~->B%3N@*F_99Khmoz|gD%YBb3KFPc^?csvX3)ddfj zm;2_T|J`?F<@yiJH~;9g`N&@1e5BHRQ8;+y70~eq_qCpK{sH;vb6onpjqnv$dqS4<+7i}W&g;m z{WY3o{~)UX-G4b$l<5AWyPx)$V2z>q>*bq2DQ!No*Eb)jG~bk^%^!kR$$VtWGoNyq z&*CzFudMksnq>Yks{rN?MMa6rKYEYu?LXRDL;KgyxBu9*{m5S5ex%ZVQk=C~^76b#MMeYYfdFmT&&#wE4(h-+ZLfd{dS- ze|NM><|9*{`IO6i7MJ<^WX-S9B=bjE1u%b4RFt^<;}&rFkFnO!{^9xdm(unldwu(n zO8ZS&+W!5~EZL7tdG=E-`&nG}kILF#qe=E3Y8Al#15r`p@{jKR*>{pPhURaOZ~m0D z`N&@1e5BHRQ^}BU7IFl*@b;m-)r4`8Aqk{&7|T%s&LAC(d5@aio ztwFW{*%o9wknKUf2eJdmjvzaM>l2gsfvBSEmO$6<4e!xj&ReGU$* z{SHgs4hz2ytDg>QfDQ{`ZY&5EMcg%N?75KFg^s-A(@Vu@Sr%stR$qytAL&l6eX!NKt zBP%1yfzi>4f0wBK<;Rm!xvPIAH+$NWwv+7w=v>}i2;fe(z@^8JORaKi>tzNtgNM`) zx9CjdVqh(q896S8mU!fVvYa>=E;U1ab+Mx4upvbZOV(K%4PHyxT7`OIEy*#Xs-;w} z-w;`?ZNC95(opf{f5XZ;<^O@O!Z2RF((Da!WZBomuCd&TX9yAaeXpzc?Dyt#=P<21D;C3-!gL(6t?SE_x%rwmIH|r0u4_x%y z|A29$#_TZlA186Bvto(^izol@=4t#ZPDWBUI_OlK~&x=ZD5 z`Ly*8_ks}w^W%mV%&RA(5_iqZZj5qxb==a|~ z^M9ky;BA2WacMQg=ql?s+H1bEJfO}JKUyU_P2$lCPw+b?dq`;q8{c;H|6aDg4QuKe zz5m-a{?Bxc(yr2W0N6 z-yqwt(snkqK~1vbI;n6_VE0ud$10MyRj*<%Q^Bms=k0_F=^q-a&|UTL|AxW;jWEDn zRact7tkhX5chx={m=Obv;I6uuAs&$vVNqc-;eoMo<7AgBZDd_`V?S2n5l~~LeT9I( zgeTyau=V`X7Cs)Wu;OtU)m_@kDqgl>)`Xd}5RGxjDIg2UAHJw65s3XKO*@9IV=VS6 zEmzvq9&0!AKX~^_N8KtgcQC6=M;+Tg)WzxS?7cD}w;KGTkb{n_{VZ+qhP1eRGTv0) z;wJI@9yi|}-7%e0{TWYtdfV8tTy<=z*vbd@jjWz{cnQ*mLY3RVGSob`4@b-KGb7Hz zw)W20x(TxBo(eTdTb)#B_Gu_p6EQLktdp*uVm-WebeI!&5bL#=@pZWokgR*x#bl3$%a;zokcxhp@Gix0c(ywtH z4Uywt#<&jEX`d%_s#z7E2go|s2zzpT!@pu*KhafZ}gl`ZAwlDehE|MkC!^=5$6nwO{5kH=`C^W+=;bD%9A8iEE4% zB`>oSRbFamkz_y~0v-0>uj3|}LR1**N?z$iqflwxMpc{SuQaUFmEO6|l}@OZz11t- zYE`)okkgIeO3y*%_WL_?rS;`fW$Vo;@+q+NfalEv*I|X;> zUx8d~0(S+tTR~32>HXlI0eJ`HiN%e44ze1)bF>S{=^)pFJPh&?$e@8nwgWi~q#5LV zkb6KjU((1pkXax<1-S*}4UoZ08QBNqB#=u%9s}vKw2>`A_69i&WEw~-$Q2+5FJt6k zkUMeuFvu$)pMtD}gv~*Y1DOkQ7RWUqZ-TtEoRNOZ8`%itESUN`kixP?)(6=K%?lAJP7gu$VzJ(ISAwwkY9oP0pvxHerp*S4pIa;9%T6;kb&$B zauCS%Adi521hUfFMz#St2&55YG7=g=xPH|KfLsr9FUX@HFN1snawYn~-5?Kv zY=R5FGswOmz0r490of2_Ymg$y;ULpMo<&H01kw-Tw+hJ9Q1w1YZ)jNwi;MWw%#-gm1MkOp!r$mt+AfIJ1#tCx`tKpH_V z0J$IJZIBgu8`&OYJV+bJl^_p+dD}MRh9KLZLBkL``{8sdNDIh?Aa{a14Du>SzX3+>hA;kM zWKvhZ2@_{cnLZ6`PQhYEN=4h}(N_HM+i$-ul?hzL2?lT*La;bDA_PltD?%`kn-PK~ zxg8-`iW?GwrMV>`ScaPtf@QfaAy|$Z6N2TrH6d7mn-hW+xji9Ri5nDxLENGctjtXc z!7ALQ5Dex{pYS7OcZ<3&FbFxDc$ztqZ~W z+`JGBPbxgpq=+Z%%IxWOUV-eHSF@I7vF2zKB$hhRsp$Ob!c zt3$9eH#-EoaJxgWD>pm@yK&1yusb(B1bcAXL$D_|J_IAV^&!}cn;(L`x&0y7hZ`V* zQQQI%6uAi^2)GR*80|`EM98fW!5D6a2=?W6h+r%?L#owDn>rvlwX2B**|J%vz zBK)_L{ZBesZ*(loJ{3R5SXCr&tP(e@lEe{9GVGrcw_(g-CzWK_P9+X|s>E#?b0cDI zvzXgF=CIjHC9va49JXDF+dAg30ZTIM!V- zG7OgDuo+Aoc7%z;wlHxRIK^Rum^kbb6L)aT9TIccG^W(UV(##mJ0j+ejJfeKhb?6) zi+yF{j*hvBF^6qtN}UvQ$Hv^`n8S#xeAs^`&cz&dqDhABXyULZP29AYn;vsBV(x^P z!)7&=z>YO>*tRAvs&IB^XS9?h$5u9SQP2nX{vbHga=fQ|KOhSFLf#gPFO`F%qM%{ZdJf3?m7E$8XWIF1Wb2*?FVWw z$!&*nP5hjC(Cm)H*=#ohFvpEYBTjWE0ve-Wpc?y#8ViVmB_(@UGC&k8Wd@#LlSje4 zhG1zJb2m;}+yVCUBxrZj0A+VKpu;@|=yY!Ye&80jK_&R1+unZg1YJ?E3`&3ZV$1fd z*m5W~*5;Rjvs^Ra$L=YcMGAi6mW0dZN5S$vQ@nyXbAht~Yk%|)?aEmW~VJ!|mF*5K)=?EHpc6{xrx4!B6&@iN!PrtX5v-8+CQ zG956tlXcfCze5Kc{QsY(tcs>QZ!>|x)vlM#_666t8`n1YoqGUqgL?{aqk9{0(|72@ z1BY6HTNIDCy3KHQo7)|5dlamOTJee5;0|{+YP~ZGR*z+OxzRRR82mmPw`;^X{@|WQ zj(ehD%~&t>{ew>@Pzis66S1uyWQf(8GHf(=ZDCudz3Koo2!*=LdgqF^J*K9vj* z1shBDiDZB%*hI3AB?CmkrjmUm86XNqNcN#*fGF5ZvJWH!M8W2gy)PLc3bv5!J;?x3 zu%%@0N(P96tt5L#GC&k;E!o?W0is|V$=;F-5Cz*x_NHWjDA-Q2HzWf@!S<59E*T&S zz9-phk^!P%2gzQQ3=jo7O7@CmfGF5WvX>>0@bQ7}@nrzHbK!CsO*B^e+J_Ll75k^!P% zAIYAS3=jpQBzr~nm9bEiNnL1I6SY3L(GXA9dme^lnf7)(FSs~fgEihM;pk|26D849Bm*+8_3ZHaIWAE`^EZqiuIGDesa`Lj{3<_KRN0r zNB!ieA6yiSi}mjq>nBJ3E1a@0?b`pHp0xG2~^*1tonpB(j*qkeMKPmcP@ zQ9n8ACrAC@qTqm7|Mz13E1a@0?b`pHp0IqC-&1qa6Zw~zIcqkeMKPmcP@ zQ9n8ACrAC{s2^Mud_UH|U96uR^^>E1a@0?b`pHp0IqD}z{otbDpjiL5v3_#YPmcP@ zQ9n8ACrAC{sGl75gNuTLp`Veq8}u{M$T8B$G1AB}(#SE=$l<}e2rdc^iFNH7>mo;8 z4qb_pP1uhB>gDy@BePid`s~5Z+1&2et z9&VwZnb=l10Gezi$H`W5oNOhxK_xp^W>tcdtxV-)D>+WKlH+76IZn2c<76v2PPUTc zWGgvNwvyvyD>+WKlH+76IZn2c<76v2PPUTcWGgvNwvyvyD>+WKlH+76IZn2c<76v2 zPPUTcWGgvNwvyvyD>+WKlH+76IZn2c<76wjQL+Cx*-9BFTgh>ie_N{*AQ zie_N{*AQ;OuuAp{%~osDdC0jzBnJS9$PC4}S9= zS&8@WHbpr;j^U3L!vMuFImR$K#xOa?Fu4ukO>&H3a*SbejA3$&VRDRNa*SbejA3$& zVRDRNa*SbejA3$&VRDRNa*SbejA3$&VRDRNa*SbejA3$&VRDRNa*SbejA3$&VRDRN za*SbejA3$&VRDRNa*SbejA3$&VRDRNa*SbejA3$&VRDRNa*SbejA3$&VRDRNa*Sbe zjA3$&VRDRNa*SbejA3$&VRDRNa*SbejA3$&VRDRNa*SbejA3$&VRDRNa*SbejA3$& zVRDRNa*SbejA3$&VRDRNa*SbejA3$&VRDRNa*SbejA3$&VRDRNa*SbejA3vV!-TT> zK4TbyC^!n?blGr=;Wyo1EI5L<|GO>ezuS@)a$C}bcq~58p0n(E3ZSu=9LHjE9E-_u zEGEaXm>kDqavY1vaV#dsv6vjkVsadd$#EkDqavY1vaV#dsv6vjkVsadd$#EkDqavY1vaV#dsv6vjkVsadd$#EkDqavY1vaV#dsv6vjkVsadd$#EkDqavY1vaV#dsv6vjkVsadd$#E7JN+pzw zfGC)3dd`BL_Zxze@ke6O`h$jGit!hL|F9u&#=j5#qlVx(XhVaFn@Ss@%(Ez%iquA| ziGS>#1bpJoMuR_feX&mYncD#HMGu|~kB_ZbPxTVYML-lxL+TZ%@JlxU3*BG2n^5o9 z?p;*)ja$y%X9yQ7^q}JDamDMZVnVqHh=LjBn)mDljIdB>2u`q*{q2Wv*vn34+6k_S z{X_1CputWC+umXOx5*8`ENG+)H&!XU@ zICZ#E3FRUn3Qmqwhbfg%E&`(9lsI*Lr4q_TKop#cRCckol}acV0d5Am9=Q`LTog3M zC5EU3pc&bXl#75Um>s8Xq*Ow=2#A6?aq5Oj zC6tSRD41)h7Jv^SsZ4P5tPh2K3sEo+C2>}KP$davo<+fFD)|qUB(#S8tXK;YT8n>G z8A6$7QP8TSzbJ`NE)q)kmz}5LA6GVpix)~wq5TuX=5S!4G&@|fP?{6ke|4K1+P^lN z7urAgIxV#Sa@7(pXO*={;{(!2C>H^4V|2#lt=9urD7XWGD;C`Mfh!f)*@8KOxq^9u(*!MoRzaJf zENB;W2s#N-@PnQ??1yUe1!^;)u}EkVGz(@6<_P8r<_S&{vY84z=!f22|p#_(yeeLRn`$4VyD#rT0lGp0|o z1XRsD%kC2U*{-B;9lJ~X*iL5SWL-P?iJd%cdx66B>}0;3EMW>bg>O0ixg>$*zzL;0D0vVb%kFuCybR28e=RNOrhnfGGH-WWSLN z5Cy-I>@djyQSfWY4wVcL1?Nh3nPh~Xy7D}!x>TwFi_VqjtFB8_7r^}hZX0fHyFh8@ zDh&_?7fSXk$pBGsk!0IQ28e=-CEHpuKonde*;bMPqTo`=wv-GI1(!*-g=Byz_>E+n zO9qI7%T1PfG}QBA6kLI%%+s5m>7w9Dsrt240ixiylKoOLKondh*^eayM8Vb2>mN!} z`}=xuKf4CH{DWpnR}@@pL3)AhI}10mAiYlQSVrw2^t5MPFI5Aj3b5#U*6&~*j3};( z>e>WlLKNH(rw&#spH@ya7&!J zf>H_PA|MKGjZ>FbDxq8iM8R!w>T*gYl#75UxIIo?R;h$?5fBA;*cDyZ_WOk!+ZDaj zPOibpCU$a{o$O?<5{H}G$?xqXv<-gY2s^pkPL8)XgTu}2ONGsw1u zo!n~`n|r6A)-oXq?vtz}86XP&DA{Dm08#KK$&Qr_5C!*3Hc2u-6#QATV_!QW(4D4PIL@OQ~ZO9qI7e@GTc28e=xN>-E% z5CspKEVtcJ@DP#?w|Aw(EeoZdUW13Bh|3;^N=yN%0r0DNn9vmG59pdC5+`#>tL$@-h_TLsiAE&>J`* z2`&m=iBrCgQ@};Rt4P6Dj_hklJs#IygOWxl*?AF;Yp+`^r7+y3=k0t0?VN4%py95I zu$^zBmh~{(`jNTV7E=IPOd-d`6mnclA;-lOa$HOy$Hf$KTudRy#T0T}Od-d`6mncl zA;-lOa$8gy!Y>M_EnG}tDi>48aWRD)7gNY_F@+o#Q^;{Kg&Y@C$Z;`+92Zl_aWRD) z7gNY_F@+o#Q^;{Kg&Y@C$Z;`+92Zl_aWRD)7gNY_F@+o#Q^;{Kg&Y@C$Z;`+92Zl_ zaWRD)7gNY_F@+o#Q^<{r{l~==%D9+9j*BVexR^qYiz(!|m_m+=Ddf1ALXL|m>^;cYrPS@<{ou$^wB#{&(ONjwz3sYF1QF-DlF8yoS$34QhPY1 z=l8uYQ1!W}^0lV29-C&%gQ&ZVI-`QS26*B^)oqJ^D9N65a<&`)uXNQ)klM;(T+^uq z_o=;v8a5Rc<}FlPhD6DQZ;iTS9LctF8P`0w;ASJhPg}T}`&voYfveq8Xt3om=-CES z`{f=yv)US2Pwa?nS*>T~7VlxXC3;wHU=PbJ*~4;6^|0L1JuJ6O56dmv!*a{@u-x)J zEVn`r%dObMax3+)+@KzoTe*kjR_S56!96UuY7fh;*28kE_psa=C}*qNmuaz<(5SN} zL9<}CV2)s}V4mPKL5rYO&?YDg+65hgPC^u}>1)44wG$ePgeF0=V76e6V6I@E;50#t zpjFT&C=1#J9fD3m6t3lK|BY%VG!_X>f@Z;N!5qO{!92lff)+umpiNK~vMZ|)|zj%vtf3I-%pq8w-O+- zWyYR0Vgsng&Y|#jQ@tV1uoo!2!_GG9q0Wt2j?ZDg&pwBB_n?lSc;TN4m5&@O{6kWA zzuatT2%fnXt3-b;xJQ8x6x?UPzZBf+SP8nn5ZR&;va!*B{eR!+5p2yEtV}&pa5I6A z7TiyP4;S2Rz{jl7n=#uoENwkraAyIZD7YJdPZr$cz<+0cMMk#n6pMom_Up7m(tTy_UY@<0fuu$K|uvMDa_BZ|qYPP1Dh3}yLho!A^ zp!)A#Zd;aH1Jl(X6kG`W(44Xz#Z5T4JQX?4j*{c-C^^oK zlH=?sInIuf?k?Tj*{c-C^^oKlH=?sInIuf?k?Tj*{c- zC^^oKlH=?sInIuf?k?Tj*{c-C^^oKlH=?sInIuf?k?T zj*{c-C^^oKlH=?sInIuf?k?Tj*{c-C^^oKlH=?sInIuf?k?Tj*{c-C^^oKlH=?sInIuf?k?Tj*{c-C^^oKlH=?sInIuf?k?Tj*{c-C^)-+6UyqLoE?QAvW1K|o<31L0TfT<7*FIFPvjU+tAbk-D+pf~a(7nsz_f#@{9P#j#vX5>{M$nQ7O@^^Kg8Q}m+S#V;h|pm zQwa$@?Nf(&UHeNHVA1ud!+m9ART&@(kMP15B?Ls_kzV+Mgn%dp`vx1#XLOp!Tu6QS^Y7>+R zQFttqe{M^=VXt02-Q;4ccBsU!uq<>yMz+e=vvN~d4zH#aulUCI5pj4kNF3e`5{I{g z#NquQad<;W+#xZCw}d3adqU#yrjR(iD@4v*h)V4aNN5*y2s#N-=-9$v!moXM?$_gb z=%1-QEO)$>bH7E`8DL#!8qSu$I@jWMHk~^0x@PeXbtFQWXHhtV$-jodOZ42}6R738 zcchs;EZ4wtc$2YsIj*VA1?tiQqHq?I&x27*_1vhahmAY2hviN}IWy%C8d?BRc(NDX zEg>KZPw~Q^NJ!}EAattNb%=BUqOj2m50(%R*#fWHcaVgDC~Wq^b0j46v?;T_u5r=@ zSai3MIli)eRT&@(=X&AyB?Ls_JTE*@LO^6|&T_^95&|MySH`%=*EIhLd0UZCCPZN? z%d{5%tTaMdXFYXUn^$?iR1(TXz@qb9+1L3e)k!E90a4h_v{Lb32**uGBpe$$?bO<^LQP|;YyjwLA8jFM`d$-iyTpffrS69ZHtE&|3&DFtpb9GgFb5)sI z1g(NLL0Ql)=n!-gqOjA~_?T)WG!_X9vXPlul($vTCMXNq1s#G;LKObM*Z6zYNN6k) z7GxtcwJ2|^piNK~vuGBpe$$? zbO<^LQFw;0^-k4FXe<(%1kHl{KFf?P%F!yYM?>hcM?*lnphM6}h{7{{tq-eKLSvE8 zBxn}o+sceB%F!xl6O;w*f(}6^Aqs!wYrR9Y5*mwyCPA|x-&SU9QI1wYo4}r4WGkUV zXPty7Jj>VmkZL6~770y)X2ER19Kl?H-f9=$B4`!ZtNGAqujZ?6LWj;e2~qfCU-#{* zo6uM!GzppovjuYma|M1}V)hn^S_N%_vY=hiA?PGT;ZJ;9+eCNvfaO@d~@Y{4ACT!FWn*;^!P71-Sy8tv{ay9pgS>m)?sPkr6D zs%}DKkLxT62~C1#!EC`C!CZm2o7r0=Y8A8z%7S)5 zhoF-Xg+KRo|6O$x8jFMl-J6)HMR{8V_NqVh*sK14c0q@rlMsc!@HO778VQX>!h-Hi z%+#X1t%5c|SEzo|w-W09~R8=0v^d0Pc-g0i4p&>`p~MB%S|jW?-A zLSvDzARC#fMR{8VZGy6(UC<%uBt+q_eT_G&MnYqeu%H^5sYQ8P1#N<|pk2@*=p;np zxxU7~szyR%k+2}IGE)BQ;YJp3fctrts2=#=+IdwAqp?@ zHD0e835`WUlb~6U|0KhVEy~dNEc!{s|I@L;OEE1Xo&4PSenXyGVS_N%_vY=hiA?PGT;ibOT z`&BEUu}EkVGz;==WyTieXce>x?B}p-C3NVllMscM`C6}4t%SxRp-Ipzm@Swim@Dx2 zF=lU(s8!G=C=1#J9fD3m6#mB7{U_B;Xe<(%1kHlkf;ob@0)PKz_7;g+1#JTRNiMqy z9XjhIMB(MW?rT&xp|MD45;O~D3+4#s3cTIS-Xc+}piNK~vLxT62~C1#!EC`C!CZm2 zo7r0=Y8A8z%7S)5hoF-Xg}?Q6->13>jYUF}pjj|mFh?*~;O%Di7KvH~Z36pZknARO z=&X|vg;)8yuTtHF#v-9f&@7lOm?M}g@OCqMi$tx0HbGg?F6a<+5~A>GU-!MLo6uM! zGzppovjuYma|PaRW^a+GRnR6V3)%%8f=)sdUgPWjt?DK;76}WwH!)L-^0o@v1Z6?H zphM6}h{9`qjrXWVLSvDzpnDTDwJ2|^piNK~v-q2GPv3% z5*l^ZBxn}Q7R(XM70eTyCTJ0~3fcr^LA#(s&`F5G8&JDG{B^G;JqgzHaMshg+Z(G| z?~zu5U28jAbknytS+#C|?6Y3hmWOVpV{Q0C3vBo@h-}NXWqB5xua~ndx8iIWY`$LJ z&TjJsE>;16y9QZT==n;^?Ox|u(g}!c1GZ+^eO<4gq}L%KCkNp=>jY|XZ+Du zHdU1YBHQOsj2ZSLf+a4S>jYzf1T-2?3F9g_rOL5&|OI7cXI_gn%ggs~5IO z2#CVJd11SRfGGUC7nUUiMBzWYuvJ1p6#ml-XGlos=_P#7>zXcIfJJu+AM%wQr^*14 z?aY_knk588wtZj1IT8XQ+siNEYzYC8ZRnSuv+Bm_jZ*IzkLk`NHt#(xP< zln@ZvTL2P95&|N7B|ySi5(1*|DKDHSAs`B$_QDnk0a5sj7oH{|APS%L!nqOxqVPE{ zoF*Zmr1r%DKj>@5Zf zPnHl6g|B$wCKZU-QC|5(1*|buZjQLO>M0;f4E12#CTry>KrH0a5st z7w#z`APV31!rdhVMBzJLxQ2v;o?gj!y{^@z3$W;}snE|0E@2S zAD}XQlW4_WJ=pd3VYU29Jt_Z@H}>mq_+$i#!jHZ1D+vLSy?SB~Nc?!xAbUXiG(KC| z&OXE06L=M66+8PJX9Msm%3wSD!p^Ya<4Y+6MB$fS_=SXk$aWlBx!+@Z;Hp;cYn-ix z?LMp7**7?Q4{590*|#_wgS0j5Y_VQ;#+O~zw6j8dww9gsva{j*Xn65?t&j(8}&+m z1t+qfVd&n|k+?}s;tEV$=*^t*7d=+Aa&AowR3oe_t%S2NINQw52H~t3XPevE$~e0c zXIt3WDt6{y8{M*3=EFeUzLq+;T5#*{RB%-kG>1JUhXEoR2_<||LO^5#qJ;mJ5D?k8 zDB%+l0_^>BFMM1=fV~#(g^x)Hu(!1#)UbIg&NzzFx@$ zTZ~}x@M`jPN+#I+J(D-6CSR*$f=zZac|#;yjTa)#=2a-%$P3Sx5Dph`#9r7(LV&#}?1jB01Vq;9)xKU50_^QvFDyt1 zh{CPB@LS!=0a3WM7cQopfGFI?3%^lLKooWLnLK^gbo^BL0@<8aD#sC4xIIO<5o9V)6H1RU@dp3eUvpA5(V}^5J0^fynzW0b6=p$;on6= zZ`07nj=pyEv!lNq1MFDbjwPV^ebj%n`w%eE?P;&i2gkZU;k#MRonlWW!Ex?gyDJ4V z+!HGqoZvnK%yg^R2Hv0{3I>{@CGA+s29_gj<8m;!Ay^uUufoY`?lC~CdkxSQ1_=Q^; zZ96s!R)Xvt$bKEm20^xq&2|Onx`P4dMZwBeeig`nX#;X_ft!c&7rJ|JcCmW|aETiM z;iYarz;E2fwj0}gQmJV$Ye&+R?iYaHewWg#qV)ZPtk_lVeEUdtaIL!#aGjer)Zlve zEzWL^g4L{{tDA?X+AHP3-3`GScD^PG?~i_Sue%R$pL-ARC)XdA-|vP1{_M5`Jm4E1 z1#6+$3Fv2kb1R{r{oU;j_-CAB2yz?-*+cF|z{63nHe{!xQIEJY0gt-t0FT8@9Euzd zLH4g$whm-(K=!zM3-E+nb{&H!qhMVmorIHryGH;|MZtQ^j+3X|M!gK4iGuYZ>uqUTaLAsp$MfJt_Z8r!DA>Tfw4ogv*|D)5o7l0b9V6`6%#O|N*usu2 z?bynWt?k&xj&1GO&W`Qv_?{g**s-G>JK3?b9lO}Es~x-9vAZ36*s-S_BkkDBj=k;J z$Bt2U6zvG?7;Q&r#~3^IwPUOu``Izhj{WU8z>Wj$_`V$n*>SKPhuCqb9f#R*xE)8p z#=|fayy{K`ycPvVnyfTBc-=K4g5QXO@s{x@WSot`;;lL(%LL1Hv>g-eIL3}ib{uQR zWIIZBOtHh+ahx4fp{<`i6bGNX5MY<(c#}_q{5=T1bz9pL7Ua`SKEsX^?3igsgB`Q% zi0nAgj+5*-*^X1}IMt3uJDTihwqv#(bL^OF$2>buv!lh1Ry*44D5JjVcr##)g1ZE` zrhOi;9nu4FwpPKFfkRB%Vfj1l_yJPigmmpf+<$&(30+9|9y;0QvHWzCpJB(DcKpbW zv+Ve>9Y3*Sz8z=V@l!i~X2&^p{M?RT*zrp{er3n6?Kszt^Xxd^jtlI#(2k4jxY&+M z?6}m9%k21#9hcj2g&kMg@mo8tvg2yB>jfJU!VL=9_FZEmKyQ5eV?+DO-L;naIyhic06Loqjo%I$G_}&+>R&gc+!r4 z+wqhgPuuZ~9naeFoE^{G@j}i;`440p;coM7e4$>nyf4}DvK_D3@v0rK+3~s^Z`kps z9dFt3wjJ-FJnnM#GA(a&_WrCp2*0`aEVw1HmNL@5I$}e&y#vd8oV~vqk1Fq?@Y6Qr z+k2*NHC&ZqB{6Qyd&q+ur@a8`#v@NyaIfLxc4G=|UmIw{eJfwF{4sKkvk$lc_b>21 zYp*Q25opFC_H|2?KD6L2KUc>eG^2!G`e#-lgq@H+LEeA6@O` zA6v#x?D*7<&(KC(M0@)zj^WQO;R_^ehy+(~=lS6N(h|P1<7+#1jo`M1sU z{ihC>x45@=c3ivS-+#vNeY=Z?Cs~l9NvBlWU-6j#0^wtJU$VDVDz{Sqb-~YZC7R7O zaKY??+XOC{V;=_TQ$R}&hj?DKP+yb!?*V>kj~Mozh8v8^+pI)?lqgNM7aQD{y-n6( zvH_G`VlM)?p?ysD1CuRI+23sAyc-2sm&ul(>_gjV?q)!ChRFs(=2k7(>6r!hGl+j= z;w35G4dSy3?kvjXBUCz zn|MWvFT&{s1$QyrK3diS349WG#>(AZvpRHDZfX=oL2<-0y%lTK;vAwl2teAnSt+0~u~) zxced2MQ@9v$wp&K+7M(TrsFxymNfaG7T#t0Hl}zz49|C4M*{w#;7$kLW1nhT&G6oW z+ZA|U!MzFmVV^Z7A+*F3ik z#dtmp-z)F|F?`>|+fw}F^-TPcy-*GO*u>kJm|qFv$AS1wAbtvnUjX8_e)v@%eu{+O zPvG|w_qd+Er91SuNvfcy}o3*>Z=GeFJ+`4PxjAU_8A z3CMhqvq63e@-vWgKz0h10%7m{&%T;!VJRnbE%C}mi=RpPe48e`3&T9kS{>K1o;Z& zYmje1z6HU050`nky2CXbF12t)#NyIYu{>d~i5tP<0oM#H+>FBnzP&DO1heTDGe$5s zZF_+1cJLf-WgFUjFD9SevNo$>vv&=)=?4~Ejl@lR#m{D#OXd_ZXO1~x?B|KM#R$Ar zugbHrErX%_BuKZZN-@LB8C}lca>kZ3w49OU3@qnaL(Hazm@^GAGa6zhlrx{4-wZK} z8Dg%|+HC|gkeqSk3?pY0ZK~U3nEK;XAE)*>mB*<&PStU0F6vr*(#)CDXC50(n>fET zqO1Rm@jRdZllfiC9y96K3DMM9la3kRFl)lBNe%WRud6?kryn=JBRYE1#k%>M+vn`Q zixrrmYsrS06E~eP^^{FdnJ{tU{H_6`$BmsZvtiOqt7U_ErgeDNz!@{A9}`WSHD&s= zhWSU>bLnDTON@<0)qH)XO*nqi{H|X651ZfB2W94W^|i*$oHf5~epkO~(eX!{)?l%& zJ~U`d*MNx=rcRx3^wdf7$Czx2Vb@|-a_W?6lbCJB%t_XuzB8vxF3mDQd45;#sgtJ7 z4+>p{dfvnKHzZR^h$TyO(;#8a15MS8fg{~b(l%^j)X~fafXHJ?W3b%ES>YWrzf67*Wx%>5Nn6msSc!mIjqpv8EPEtC|xwo={pHja;kJ$kIlo zrAk9f8`IoXO6vxTmDY<*+oCiqZBcB8*(C4Np!Ln5#b=DK7+G4rG@`UKnqS)7T5w3J z1v}QXU!%M|dP#RqdCuRLJ%IY!)`DCxT>ZZQd z1NK8ByB4?3b@ZfTr_Z!&7k2fxvuTq~w)|CBQ&rKFv8Gs6!FxPbRXW(JJEXQ?U`4Ho zalu~Mg2~P&pWr&lIem8DZHzfgXYJ-7d#p1LEn_2vIdA+iQ%)o!FCAC$QaHVLX^>s& zNyl1T73>lX4tmR9lS`!cbg%=u>f4P;vg(+WXY5eX35~uJ-@G@Bd!Dh~KWxt#j7iXYaL64fj;t zq@UzvBF;frp3czd$+HF!|KxeHnca8%94NlOXC}9!`;YH~o!awC{ymGtm_6$ROeKAD zj_o;SF8&yZe;7xGMopOofGM;3`zK*08kmhf1G8aA4NP9M1m`>AwR1t|!p=pVi#wNe zF6~^_xx8~l=gQ7iovS<7bgu1O*SUTPPKR1HyrXTyH??i}mbMMowrzN4+lFsz+wkpe z8@{7u!_}Z*WD@4Hd7aa8OzMD3j)8?xJOexVeoH$OO6HEHzzPBmONCXH$iVTLs~75j zozFI$RJ{*rm*#e)kLFJ7{EP5=B+Lt|Gi!4Vbz z=-q7f!t&tKfo7tZI~RCW4?xwO(=pdA)9JT=1r*#lBNgx4I~O#G7rQ$5ND6?Iiuc|q z-h1k)dpi=I^h?D%Z0^?Ut@|wV|G(Z)e9_z6bH%>^<#(jL(IWnJWzHMgp)m8z^!{(SS9 z&P7ei!miGT6Y9U-FG# z=i&y5#l2x?#3n)mWFoY%8}_Kyq){z%iZG#baRp<&=+-UKMIumlT5^9MgV*_yN}UN6 zA~*p$GU3X*0YGV{7i+>sRbtzV=cq&;)sra*6FQevFxJa* z9h0RJL1ZZXV5}GAIwndbf{0Q|UelCP38GBNdU zYgh>$hbw5VfuSguli5{K;s$+7QC{XnIj<_pb26gjYO~H|O`_Dj;bt@vQ35g*R0_g`&Se#h^`czIM5#m&QA){cno=r3lqs3KC`&ToOQQw==^jNXY*bOUy>nHF z(uJ$MEk$_;cpUDfc@GRlc`KP+6(zneYbnahy(s5bMR_hoxnu8!{`8e=Jv)~-iBk85 z52KNY5|F7VKgRAVN;ayeQV=F|F0Wv$7v(x8N+p7bQc7Oalu`+zOv&U$S&|9g<2C?D z_b5tXql&Wa*Dr-AUAW5IQj`yZ$Kh)<--MwkA0V@Wy&73Kf1yNZ&H>Zufj37sn{80$s3j)_uR!qSQc0O=k@DQr|xwq06Kh|-0tye&ofQ}8(aiRP~`6y=Y|?6pJBPj2<-YLZl! zjo->&>2-NhRhQ>ybje2@I#)L7Qul^2Xe7D>WU9-J*fnM=s3E8$MAs`CP>bGa>clXb)LA|ZL+s7aw*HC?t6kpejt>4A)& zUrV`Vq8b<0^Ni}l`m?xj?^;=-cQGR4Dn>>=xNODB6|z1r90?R#qW4w?%O#$=GEZKZ zArDn{uB}EIj-Y!kX!NZlQg})uJ*tRw9+tW}1{4^A03beI+PSvGkm*F1LIuU(ia%}L z1fw7mJd7t}8HzUS4s&g!q>M-@`5G=RlhXWuvy6~S0E@MQaUo#A<}y~Mt)%iaq$%{! z%!DD8k7nZ54s9#t=Sr}?*+Sv*Sc_OJP1$<&!UaekpK|jx;OZ}|%jc&Pa*T^p`n@gJ z)ZuxHmWvqQ)3)LJ+BW>pwhcefw&91`HvDkgh97O)@Z)V8exhZ=xY{w;v@uVs?i5gR zL6`tSkS*oxFd|s8 zCQqr7P*Jc#P8ICCM<`eqH>2`Luxm)$+i2bhL&09b7H$<-6zuS;zzV(cS5>`UPpRtl zBI@bw3TEQ$f;iMVeF>YyZNtLjfjKtn7C>DqF#lZs@M09P_Hg-<#2g`eK!IK570aaL%qI}A=MAC zEtYK-g5{4Nd%+G?1$!|C`^QqSe8oawFL}vAlVHNUX{r$7j@rR0Jatip(3$(lT+N?m(QY0_-kO0o;^ zRI|Tl{H9sHPoy~_N(a%bRK2EI;}i%%wv=4(npGIlELoGMR7t3) zSs|xt_WluS*2S$HE;aic0tg%9^*mv77;5%ehUD&0>Pc~{RicGxPXL4$?SiUkFQsUw zCZgrri2^&y`-z%Fo3@hl0z4J%^MO=~mTx<%XP7vsk@+i;){Ax>6RpxgL@QOViPks; zLXa&b7rbZ{Mnp^2~N!B<8LXa&b7rbN@MkGtte}>zb471tt7bsPbIsU;hSXnmcV*` ziGvE6zY=M^WY;mtDjh_!QuUf-jZ+{5*-~=BOIBe-vSdx3QYE3HWQCk6*^i7+vMz4r za4FeYr>gdoBUG)6TRB{+_WOu3{F3Ik zFjVcgv8}7N-OASV6^~KFvVQcms&21_ZvD#EKQo5E(EL>bsk|Lj-pn{5q^%^qKu$&b zcMd?8pA)K{UV@-N=B$K?ij8l29RBT2ZQl^K{^g9_-zwMk2@1TUyWRTGV!5JnK@=-h zuPN3D1wxQ5B^SJ66{c8IN8;pJRdOndR>-NMUDGo3V#T5hU1?m3b~Jw8B5X^uBMe3R zPln{apOr(dELfC-t}WxOuUL$d)t8!j#dfNSy@rZ)OBTm7fIVpV+a67a*|e3U7s#n% z_d@@uSVGlvOAr*uoRu)^72D<*+xA&rycfTnzVO=omJLU$SmlB!R;pf8tPu)?AX`c< zc*QDAp;!_p&#IDBQL#c!RqSmo$5<%Vg|0L%6}uk-2rs2M3Wkb(AwzOY7FDrWvDi|u z@%_VIu}iCpy_Sk~ix#IdfMaR+%R5bqOzZVn zwvrqJJeBPo9D>T0Q1t{81XVI;CCqx+u4A%QE{JTU>NVLKp+E?-rR0K_t-=(tC2{hs zDmfKpE96w!zIlYQb)hSbOWCeP0O5T!AAq53-^7sI%0)Rh)`g2Lb*sO_;Z?h=s@fZ< z+H~pS0}SF5H2hu3Ce@~`B*g$vRr?@EpsFQQJ--A&k<3{MvtG69m}->^qFSkXO|?cS z5Q1zex!_f+FokMKoII;aPDRxUIaRg)F+$b4(3QreY9B%X;c=Sp!ceuJVMx_0TCQA7 zHQV(1%lNA~UbAOZHG3m9o335_5kvSf4S)2tNwaAyNie`u&Hj|}t7ZvR&n`hwB6C*4 ztk>*1rdj2JXjZCT)2tB+gdkf=E_lrjlV?@Qsi;{Yr)u`D z5o*?jt~4$+yBz`ud(ljU;c<2=hE&xo*Dkizto$OK*X)^9&Ax`3O;;}-#1Iaq;m@Tv zX*O*o2?lto*+Ut>9%l(v&n`hwB6C*4tk>*1rdj2JXjZCT)2tB+gdkf=E_lr|1QP^RGG`^sdfBdHvQ;jKY^CZo*&3lh z2(qQ*f|sqr6tX38@~kR36=f^rRN3A$LfN{|mByuP7b1YLlI9#3%Jvk7;2I%1u`>Udli&qq$t7$tyQ)C20nDs@y9%3RNzl>NzF|3T4hp znDxqC$CRsF5amkMYsxi3fe>U%$px=mg(;Lv;^bLXaw;lU$f?SG=LnVSLRT7>%Do-| zgga>70z>6q&5)|fjf)tEQ*Kmh@qSJ(+?7?~-VEXTWsLVQh<8X3dBIwP@J_Un>;g0u z?0pR0mL0QQJ-fs~iRh|=6g9j4or_(6F8lw`E8LV0qFJeWO|!-+5F%xj*KEov5+~29 zl2cK$!cWy~TT&HEqFm@o<5IKlCvzX9`5X*2`)-EhzV;EFj&HS!v5;(i8RM#|VqZta zx@C-yFods42zkX?gYYO?iDChoD)upkZi;2QdTxn>0?}0kX}x0G9A*CxDOTwqij}I@ z6lnoyS zTP%qx1e;mLcy?8>w@|Te8RN4I;SUl*Ua{67{1L50u>egK`y4|z#j;&Jx5Pn#=&FLW zUa{+#VwDb}SgCqVvBoJ7B4w3VY|1JUC(o*qQ&F+PPgQK&S6df~b)hSbOU3?!%-v!m zm~CLF*uOBO`Z2c6Dn^rReHr8Gs$^eJ$+~5XJ2Qq|B!;|XtwGootwgc_O(nZK_LP!k zyLxhog9_1A1!=ux*D=W|9YnHH^_pajQy@gjDlgfTRU}THRVAmQWQCt9*|sm4FC^NVLKr$C65RbI9!t4N$Y zt4dBq*$O{Zwrzj%p^&W$U1?m(wvWs`ndVd&%Jx`>FN%NWnCs`gf@ zHeJTJlwkx3Bd=O(5Kc!cQ7v#&)t}3d zgh*NCRhzPk#L2U&$fEZPaYKjPYg$@j3}2uUTslZb2*2EO1lJu3_+|S+=WZmpCX9T~(0QYjz#e ztkOX=D^;&));I-1q^$CqO<6_ahFAOz% z8$+sTw#_O=(`;=SY z$=t_i{u_px{RBg*X|~NOM$>F<8RG?2&E8JUrpp+gU=Tl)Ao7~E2H{Dx63qfP)$CIY z-Zaa0_3RP{C8Dbe(t6FVW13Yuh-RhgHO(5QK!}u8Ub88yNSr*YN=`-13O`k|ZGRxJ z(5wqxX3teek%63aKcXygSVJO?p7}DCIon-MMe%)1XM73PSXv)>KQ|X26 zMOEd#k;+Y%F}{eQ>@T6@54hGK9Dr7$T;Qh4eKCh%2VAzR=a@Js6kS!2)+=`%Q?AlM zlq*%QDc3j!LZqzn%1v2C;^bLXaw;lU_^HZm`}?_ta$V?3<5IbYk-5jv%z&YC4`xU; zY(Jeu8g7pQj5kuadNpDRU>R>{NA(ihxYy^u=vk}L7g$3{(mcX!3Co_<>L)#QnZ01Ln z@B`ZAPp!wn4sEDkrMv1}hUx@XqO(ny$IZ5#gF4&yzl#&=eE!?2u^j&jXTnrZiOOnN z6UhRg(`uYWJgo+Ca+xFos=?hQhOdTVNHwIblVVX(i=v~|kT|p&|31QM)FLjbaUKE; z7s0R^s~Gg!p{g?3FeeO+;%~%va&Jmq z(%(01i{oO8$&DCuhikz%eNw2k>=o3w3CRkthaoj?U;^<|_XRa_U&l@AqEyd~EArfU zPn{cgGlaKG2u<9uCQ?^Gr`&i4<5q4EC&vvUfE(OhVt8&SM!^kfp5cZC61gFvL~b0~ zmK(>iYT*t>)WD5iY4N5mEoMuLw{&SSM_R0HZc%Wfmc4=-??vBmKMc8XFM-@7o>^g$ zP@_D`j_sK_ciQx618c740rpBBU^k5i*rm&HM!QYt!0;KM+tKv{Ji0y~%-w)T*6<+q zE$CFJ@hOJGV=DK`WCY-n6*wk`&#{TJf{hYZXm{z{nNW5YLi7%`dwC~MTC#p5f8k;H zWa1v8@3li)7NRP$(YdpK{;1Afy~%7_W^Gi@R881bOxWfzVapaQT(V44_ATIA%07~& z?7y+Ema=a$5T=ZKWz2w1Q}!J;(Uh@Kk}~Zsony+RyQb_lBTHG$kEZNN_+-kyN8f9Q zHno&Z%^#b}Y>n=lJ$p^@&+A0tSNpuJSg>-*a?RVX0B(8vM3Fa4k`(37u`~RF?yswz zI6Tdd$4x{6T`RdFgR3%+8-jYjVN=Ot=ei_`5kn9Fq@GHQVnQl0eOeH@#pKG-O!iFOb2xlSq4UG;I($n;V3qx#qmKAE^J>3i+a=0)Np z7cOB-I`cZcKDbT~i8})y_H`z7`pJ~u7h!BtVGw0ZXQ$3_k+6MD9s89V(s9Gi-WRXw zKZDd58a-_$^f_Z_%wW&Vp1Es=bXm@ZeN%hpL6Ut_Pnd)IBKX&U{&Ole_~%sqbECeg zv*q)RW*tAbXXfNJLu2QhFlqM89{jaYf6u_ctO0y9yJz0CzNs^dza~uuz?^AwCryJd z41LYes2LRhlCimcv(f9*)>NMIplW8YSr6)LmhtUJm5Z$O581sfxXUsg2pD^B0 zJ^eHUK$>7@5`FZvdGtMH3dT2j4lPrTzN4{`e?TDo2S+poX=dZu+%sji?7;>fO`U{n zjh@MDU^mBk?4&+Aorb2P1PM-w!4h#Zm%XtO#2(b1RHGqGQ8jv&%FmqDT(Nc3*em;k= zu>5S<|_ybM?@Mdo5YMc+C>dRG#tuo%uFR;q3Or&S2OX3Jc>^Zk;*M z+Rn?CE?+Vp_Xm5Xj-Phi@j6e&`69SF`m}ws&DX^}*E5t3efqs0tJV%|i(g@N57ERUD>DsN(dSzPq3a79bhFp}D!tkCASNgA$-P zyX}u@QD?X1oZZ6S>^~#C5Sy?M%|x1gY4)S}H<}mG>`!w5&4Dy8ra6e_V46c{{+;Gf zn!{)gr#XV=B{VOkc^S=-G%u$)isltGN7GEAnM^Zm!^-V zpJpb_ESlLg$I%SX%%Pb}b3Dx}X-=S-M>C)1M4FRmPNsPk%_%g4G^f%mpjk+>h-NX( z5}H@joJP~3SxOUVPN!K$a|X?Fnlouu(40lHl4cdn*)*$Z&Y?M%<~*A7X)d6-kme$q zi)k*Qxs>KIn#*afpt+LfDw?ZluA#Y><~o||VK`-k8}LUlVR%}RddajA-o$=yrMa8t zUKk#$Z^IVz$1aumL%(L|Hh~On$S!Men{vYitIk-38I0#7y)V|wC+-`>=X$R5Avh_{ zX?vH(DJ0&P`~U;Es&+PeKR@QxfbQohGqB64f_Du*%r-dPnONSND}oufFWUdRUYRhw z!6j#`IQx8!=F>i!Gh;N*|}GkxOvP|c}~gCENI z7oOr8@Ak`5ZDmnfEB3T_PsmBfyU#H%y>@#JN%I5 zNthawet-}4x+v2+4x!`~C;OkUhn_|g(NE7IqDelG%+$A``J<3SV*rh4f(1w$iJ|!e zkwc^J?r4GqNRGnL{DH`!(QOMf!HUNsUViAY=uEi?qa@D6nz66@#mmp0Q%tlEJ0R zSDn3b^`f(vp2>^r@FO}KyNNO}{4c$2-@AJvor(CcCY>?pBGTbrkf|0z8 z#upPDIkK=ZH`^A&AD;}L?VE?sy)yjKmErG1NF6@gP9 zTgN}HI{q4vj(2m%_R8=#Y8w8Y$?(~}dHCEb!{4Yf{Qh$IY?B+l ze6GW1Zyo*?)#2BGbog`Q@Mr1p%kuLcMYr+uj9>UO)dlSxn_X9H3QpJGf)H44BQ?w@OsTap@Dc0vReXpi7yn` ztSJE#lLWARa{{R8-*wd>bOdi}Zut{AYO3SZ0&u3U23L5B@4uuFySzz;84 zi*w}{$0WszPhy7uMw{?B&9`7`7oXq6hkEguX)TvzTeu|h;`4j#q36;>Op+MUWHh*} zOMM%fBvtPrdo%{nh$dKow2>H^zxZ^q(RX(=!2%>lVQBv1)1lFA3pBxs7oW_VUVNTa zyZCg-nnU5w>V^C-JOy9jr!>E$`5g?eEq;J4t}V8Y#}<5XGyd4Vc=b6cudY~*;#;1n z_+}-ts~q%}P#k%L3$O2TV`MrgCDUK9GyIk2Z#2V@Dbrd~yk&(<|DelZ$V4oZDfMk= zo=gr+I{EKv3u;X}Tqo0* zO;M~(X||-<4u)iU9<~)SO`SF(GV$BD8kx3gBGbVsnRaC?yV2}UGYpwBt)*aFRmijl z2Q>_th=nqxz75Tj$)QQ79GNUKK-x$Q&6CNY(Ra4|h9+2^OdP2vlS8B17HFPKL{lu9ImZe1-jK4yHLAhGg0k+X|T`Pw7gg^0>z@bMUP(zM$LygyMQXN#E8u=xwxO zaK`MxMRv5C1~>j_cSOo5B85qeYckCgnA*|qXnd$gyG-loIK9<6ojsIOL`*Ur(c}v$ zR4esuXr5CJjR7>G2^Ju2B!=cW<O1(W3yCtVCAHgBCZ<4_N+Kd=Q%fwAC-rS; zo($EHxN84gv!dk+G=iz^Y zi(_)ur3Olv9LwiROC%pOWCA|}O)X!1=t zN|yRIG*2vt#sC`81PhQh5<~OEa%lA39Zj$R$x#@ZCzeB_+ZJeo6^X^XDX|``5z8TK z4n48n0AJzFGKpF zqf;^wBD@bR!ux54AycNcq|Y$+UeFnI@%VlJzr>F`%#03`3?&Ybn_F6*7I3gBpfR#6p=;--hPNyH=sR0}LlZ1dCXUpT$)VA03p7tAqA8g^QzMf@u9N9|EY_1W|4Z|87?SC6 zY*RAfJL=-fZkU;gi>MmNv_lh_CZ}YQby&|Zpl4}@AycNc6l{kInf}N@4MQekp-ic7 zL-S;EXwoT1CW{P^HWEYgWO8Wqoh`qi36>`lN9xJs(CD@WnkN&{luVzkk;x&~$@F&? zYt&{iV`(;rA(@_I2>viPa7=45aWP&4nRaX<)0C7REZFb#$NJGk67i=yXNIB<~R|5DT?SeH)r* zm_w6Jo?#wMumEWzF*MIGheqFxv6YbllA|y*&oD*nEW#7lblU<=u;N+yPR6j6HHNu3 zHHZGJ{22HOy)?6F=E9I+lduKDo}arz&PP46SA8Mou%MW~>T?v^8FLoNgj4gQxUp07 z87ZS)g`MFPnn9XVVY)CX(^~Spb7jt22$;wyB4+bUEYvFXZD^iR4oy1c7-f+G(nexv zo>2~sz8hmJBLgHyVQ8LFiYAPr+ZJeo6&c06>6~?0jZrR6&7o)1QuqqXX;#sk3qwXN z#r3 ztJJrlc}6)j>6BxXMFvP4iJ^H$IW+oijIE3ekQ{}fc}6LkFp6$lpb1uF)Go%TD{G8$ zacT}dqlVxsypHBJnm59bQP*NyVU*n~Z_Oxw7>tj_$k2CfqEuf>skbqryJ_A|a}P`x zN@ZF{Zz-kT37AMJB4!g!EEFsCZD^iS4oy1cC}oiW(nexvo>C5tz8hmJBLgHyVQ8LG zidH;qL(eS$1un8`S7X(x8mnBWnnus6_rX{AAk9ZS>1bbDCe!{1T=Mtun18*}GS0^=rUH zS`jguX=0&Zsc%E`v~p?4CuKw_V z&)G{>ojtg`e2!vEc=MNjb0e$bV_ou`;5dmWeI7Jyjt}K&3Tq+{ckSf}#D}}KW;4CY zBTSN0g^);58SXZSf;A;-598)9j&vZ!gWplTlJ&izUZxlqL|buV7evRWdEOg4!wX?*nzcMz z6G_tEl{`-*y5^ZMxg-$)dFF0|@Of525DTZ*(x5VmkLjWmdPJzc@j6K z<&#qq4Ps|Fm1Y6WLYOXO%CwH&_Ue5epDv3_Ko>mGbLy#&!h_5yF)cW##A` zy&}z-){^yoEA!RW9Hh=yM9ij~Sb$7@8=B{rq7^liemRO+a6q-e7@((^3yo??n7^rzbvXo3~_#nkBx_N5xXT%4Ll&#xiG6K6fFI1qW1)BL(#Ia^cZ^V|Zn7fJ_GK>6L~@&n*B2E;5YSQ-*!9#xNJEX3{h4 zy&UEJG#{qL)vUHKeh1{~umvs#&o12LtMT{tcKGBn9$_iNB$nk!I-`9($E#FS9J!Lp20V?=*uRyfJZ-hogSGLZ<4$m7iD2{ ziK=p(+zcPeO%iJ&SKbFZ67inIR&1suB21E3g^*BD8tyiTf;lCGOiuLG1R((N%AHXN zS9;%4y>hPw6pt}sNB9pr!%*GZFxK)W3Ey(pY@@!2cNrzPOybsYe&xFY(i2%qmcN>JyvqBUKmDXONv04^JOf>`1 zRR2dc)wP(4R3Cs4!oe_1^}Y|xdv^3?jDjy8;$FGGb-2qgUp{m+TWE&aCdsh&mcH=~D(o%Eo!Gl!k(d50 zZF1n-n6jXfqA(o}n48ITw03BdI1k`mc=2nTyho-6q$8gXe2#oB{j41-o}w<^ zFPZxmtC;g9%4^<+HpHE=Bka+%p|SkuU}u>=2J#Y+1-FWYEQ^o8EQt4(G5lzMdI;Or2tJiy`^td2zx6xJ_v3|e&t1F1wm?PN{}moOoCiO50C~&#CI_r3u*KZ@_o>w z{R^0aIv(1fPxJ5`q`{#>8W(aCfBzsI{}A>xkDq&G{LfX!|5^g-_}L~me)(Lr!QMLl z4`s(+L(}o!3S^G|W_obr@9RE(%3$&otZkAB;?LPEygSXpf51a?7Pwck@aIYv-pas` z1y4uw-Wew=3?+!as1}RBM2qi2XJh64X*^tLSc~UgdLt{jSK|3gC7$=hc(6oGmnym| zCG8y-Jz8(A%Ez)*siA3AJ_skQ%KPXHFBXo2A~CtqeI{u29aQI;6ySY(j zejv>OTQ}!`du0@Vt>oa71XO0$p2~7@L}jwK=HTPm9MsS>2cL%%=HNj(OLH)#dk&t! zEGs$qTQ&zDOmo22%{ky+$-&<$Ie4_p0ox?%j5(0cRh{guId~wOgBqIV;PIG)$Lcwd zSFmR$sOXz-z>^&OJ)46Mr8!{h<{WUZ96VX(fNgR)kk2&-?5#QYL^cOCG|jb&TRJjbh8aH8a!#pmln9EVv8Aj7=2va+;*#IBviA~y2;th^kI9UoC zv$2kzV9DqskoZFy&7%QHeQ`=$0ixSRAb|lIy%|tZj|vbyb_Wuec#D!llT!2#6({V) z9cOuaWCdJ@E3A{(cWen~VLO@^z;H&~l2 zMdsVPx!R8pGE0$9$MnFW;xI9m8$C?yR;lcs*cJAo*&C)yWz&X|$I~m7-G^?pvINWK zkua!*c{CtjSqCKT{nceb5sL?C^kzW5vJQwI8zU;?iIwHhw6ZrBl?}TA!OH58b*yDs z4?w$c2+a}ffwJz4ZMCeErL1zyFUrd4g)>JIW^H;YCzlv2FBhI+ zHnf7Lyr5FF@DC1Rp2W}yC~!cv!5Cn{*Ah?`=Fok2G{FKS2V!Xc$PmLjTf?L07Jvd5 zne|d**4Jyys%6qMYYu#c6KGDRITeOFn2BwjS)AxfaXDz?ccnOLZmxlPeLsT3{5s!2aT{}uCr1qW1)BL(!N za^cZ^V|Zn7fJ_GKNtK32&n*B2E|ThHMyiKvq;jEZCOxTEbCl=PTtagN3`w;DTac<) z(OjRdS}FL`o4c zn_^<2QmJo4^OSOE(kaI$iwux95<~NRa%lA37+V<`AUO&{^L%n>blU<=up*g`G&22n zjZ6+%bLh$R2KWkZrn!^m9vG787HmPL;?-#&(;U1^hOddo;d-P*+6V{BgAQA&Hxyr< zCDTnQncl}(-cR$NH21@FAycNcr1<4XieEPMLBK>Z5iy%$Vxdf_Z$tBBa%j>iM<$C5 zkTw!S^JH>p^xYU+85tls3PbZ`D$xA3P*DY53gLH>cc#!7vG|$2?%>TkRJ)GGL6sMD6Prc;}=Yl={#QUi19CUL!2mPJV@C)eh5B~K} ziL3D(Dbre%d6cU<&ull?62sD}6ET})VnI%+Z$tBQkVBJBxjD!p1Eh__(EJ?a(DaLU zcjLDpfT0N%AUO&{^K+1*73XjC+&I|EP$Q$5I%U-NYm9QCY8pMGHb#!aIGSx}c7P$H zMq>*`6|W$|F>TWSoAYf6T1NhgES+AL(rI^wGM;7+8YZ~QAuZE7x+*6T(rHhQN$J#B zGGd`nsc%E`bW*gUWYRCkC<_j#HW&l+jB?@8eU<=*CRl*vKn%?@%AwJ13pBxsbb5u+ z>5&?p9I~d-(`i5W3J1^}LURNR>9jYtDV^Nyf&5~Vh8qLL6jhG==q#gdNf|YX(M+b9 zLNg4bGOZ=aM^`4OX&lxtj3O2amHIX`&nQJJ?hHu39HT5apxR&z&@;+~NB3C{7@A;t zMscv7Q4Wo6TcCMH5lttkZ`2s&kn4=QoTMU)glJOh{Iz3wM0T|JnCgyOVuV-NOc-VrlcZbHqXS0wJLoZnkSV* zlTJBCS!95;krWsZ7=CRl*vC=AUrO3@0!({tlsD`}3?6jP_9`az9UE>umU zpQe_>S6D@J9?eBCB-K)EQ&Rb(9@lw{>^w@MmZ4A1@@Y-Vr>hx@@aY-}sF6x(L+>R$ z;$6q_3^P9wtDq9yrf8l@4oy1csAQ1=vX17do zqshjj@6~wZkTp)vqnntpH8gk7{09tqbOW~F(YA4Al{vB)9=WF5`3 z$e}f|$j}4}kQ{}@<|ibFMz@V4t>ieeh>K3LmCkh18P41!UHU-7SBOE`^_QKq%z zc3NdJ`YlJJlMxZKxg}PtPwCsxJc}Hfbjq>FA_Jt2#Lzs89GX@q!y-cyEI@J;hUQu1 z(CD^tq?H^;7BO!+8U3)vB8RLw^ep-le1*T!Y_Ju~MlfX2?-@e&kT%d_GNPOMWOPiH zMz^Ol+LBSQf7ptDF|l1HqfBc_?lBb_Z3C>~CL?0S0+qfE&C|%CNv9l*EHXgaNDR%> z$f0R@GBh$Y!2%@nVrZU54vlWJG)mC~E7FL0QyTrOMk9x;boVrR0gJU8&7L$9VMwFx zuuW++@UKorV|c+AzbKiWWzroflMZ4aj5Hj~zYdX*8YiSoYsv5Q3X=}yh;%|CVm80T ziZv>I8=7a5Lz7N9CRt>Fw2>H^XOctH3T2pNXo3Yuj>6D9lL|EdMM*3Iz9$*oGcT?N z{|CM&8LO+osn~VXr(6qBnl6LJa zedD52_LemF{#WHo68k2g1}JO}^8rrc&ZQImCg6zp&4?@tKefKRO5HOdaas63!A_{0w|F|%4j z(X7f;k(>&Y^Xy)NYx=8bo@PS{CafV95D83y@Cmxx0 z;z5T~tR8fptQ~X?C7cdWr(20A{0Q`wg_{_`sKJDLWaGmGZd#e z5lnfgJvMvPd1HFic`GBilZI-O&Rx!5GOcAUKGxNl-(|dwL(()f&g{g(6q@=rG%0(0 zM~b3_8SF4GNWa{H$ASZ@4aNZdfyaeM_u2XznodLR6o!NK2OfteGgjBw%E%&{h^B1% zd5ujDxqb?BA0iL$rTGBOM_|aNw_}^KNm$hWG^Y9xnLm-~$x`Y~DWyKma6UuxS(?wm zbfHwHwbZPqLaB!U6DdW+Y|V&;;-|h1%~MLzim%6{U#@Hx98hgA2Iwi}!lV1f@XFu- z$$=P}r&J6tGM=7W018}W6|<+T`f80;E>z8=pQ9duukaYn<22ueA*;TKEm*ZvIY*II zkyIm`riv>q`A$Qg%aR<-$a3q=DYt&apngpAzcfFA>B6l{>*%UHEOEN}8DJu}h?vba zu~4$qx1o7%DOxdINxvMoEI6RrU<}Z6%Y{exjp3ES0g?kTG|#OV-aWd6mY!Px3S8vY z4CB@>YTR<6Y9>9megj|O8Jg#4{su#C{Ty3xYuA)pOG|PIM_L~Rjb47@>dlhtEh)Li zY>lWkq8SU*g;#0AurXRos(UMQ)~47IdBrx_JQEA$GPfz3=ar%rypn!7URiKJo_7m#$IqHqYl=fow)iW?+C#8tEfvBix zQ#^)Skg$R)-8}<6uAl-uGfu-TxLCohoiWyM1r^|F{tUNZWd)a^H0Gg_1%O*!3|GjB zbJ7YfqiBmOr~qe840lX?A7BNS0gMP&P@|a0hmDAf$8T}%WS7wuAIc3UCV7hMaCi zYTlVDYCiU8?M~!hC()ct^D3Awit5@=D$`$4)F1$(qKKWX3{g>CQB)=#!}W@CxY9kR zD2o>0-Eh649B!kc3|CMAp61Wu^@?)145cv-l`H_<>SDNFQ4W_;w8a%vfU_os>lNj2 z8Ni5e1vM&)d{9L#s42?%P;OKe^=fzyr_-#USxs{R&809@)Be^2#GpnMgPXI6~irCr8 z5Ea#JipOxhq8zSt&ne2H1$bthhU*pOa2pk6xPl7sG=GNc73FXlN@E@>Spc}z#c;i% z94@11iz}!AXH5*(E6U+AfDz#eYE%^Ypo&^pQ^%I8mQ<|UAJWca+8nUR1qPq5r z?#o+4JXiiDP@|$68A?=Cw<#XO^@?)1(mkgrix%LSaT=~yl*45_Stc2-paMM2pW%8% zIb4R)0#{H0ZgnwSuPBGhDB9u*D!^G2!}W@CxC~%KxPlrLHQN-msHQ0AL%C5^)HCoL zo}>9Y&8Tf)#?ovKLq+|Xp(VFf`{MOe_xtt!tPhIvmriv>9hX(qyHiDN&#-o&*^%b? zG%uhTMp0e+No9_!9HVvy>Trr8DyrKQkKuYnIb7+UQW&m|KMG+O%ZHmWmy`mhhbk8Zuq6K(noQCTa{z!g-0TU`v-E6U+Ainh3d3UE^t>*f{Za2ddeaJ{04tBP7&QWdI|>^@<{{Dk{_z<$UB7bs0Q|t7&eaxrt^C z%^fgQ)P>kq6cw+Zx+<#pUMd`o_w~u^66fNpC%o8p_P{K>WRl-K-3?^>?&*V3J^JQn zcB(Dex{Hx;AtU$70=~IU30%2$4_oN>NNkg2RC`O`xR8;(h3~x+{Fe?{AURnM?*Tq@ z@=p3$JG5E+5?F_nc_Ei2VN#KV<24D>W=@^0N%#n$ED0Y=lE6;2B>Wd$GYQ-)NjSce zgpaX>CV_2|BxrBx8>XwAbh-PT)(#3+MgEBQLX4TwB_iY;_NY?BN~drRL~YxdS!e+ua=j%)U9TNY4j{xrZ@^B>d6 z+M#i=i1JnwdDZ#e)fXd{g+@{=LE+Z`kT=3ha_1pO_@x%j5jIMJKcS7K>8mkK@kN-= zVxNG7Kj5$0&68*Fp?(RSHk5gNo=YCz4Ebj^-Y0yQn|XqlE<&Op1skJ0ZN z=?p}Ku#*H*nSow_4@IliL>NBbagIaWjm=cBgh>*n5E3OA!rcZ@P|ZX58ORL^a@%Om z+<~5%({&y?8ROe=h5Q1gjC%0ZE6+S*aMjr>mo7g|2Yetr*?^x;rltL{t2QmYh=Frj z;$E4SPIjZ@EOHQA=qTAH8Kw4?zHwS&Z=IGt-83!L0CieA0^nrpA#{QVs;sn}m|_yZ zh!@r-r!-&s`*(7Z@+xF&yIQuEujcQtfE&}`$Wry|Bvn(etCp(C42-GbUP;xfDyf>z z7W;JY3zpI*NtO22&-IxD_SRH=h8ll4I~N*f;uTG%A$p7&5B>O?dFtV3_yvz7Py7d$ z7S*y8)PM2&lailPaOuM&V41@)+~_ig-z1rMC3ZAtf_o(sr&KaAk9hkOziPr}xn#)a znhXMHGCrG4h5|}56irEU3XqwM6X{{?&^Adj?DtG++3*twH0Bs2Wr#oFBZ?gaiMID7 zTCr^Ps^T|b!J^aQ*;w?4n7ep@>tLS%hNTi`WtusSLCX)orVZs-GUzhO8UGA6-Y4wK z%{;+NG8(Z0kw8(BNg5eeG3%6Al1ypu4{#nxIXNK(@&`B#N(e9X?FytpPq2ve0JoVO z;1cpzLS*jz0d5t?b{@?|Fs%NWY_WD|kEHtLKlC8iEDas9XF+i~Rmeq{a4O`&YC|r| zxKX+MF)jJk*b}ayxfZ5&P7||>3Z)GtDW_ISem%Wd$%&UOIgtQpP7TYKT(P9&(mq!y z3khiSW}VOaGWu|?G< zl!eBDqWp(e{a={_top^VSIWPjdhC^nz#n_>K|nSY{4M3hU5xx~G;l(@I*myy_qy!Nw5DAqur-tQup;*ETY43UAfdmF<^kzVw7Y>LX8zU;?iKO7@ zloaFgq;SYO){+$Og{SZVnvcMc6!+2h+Mzv59%M*SJDcD+FVFRosNE@?oWR9%UY-T& zIq!qjbKcqsVT?5F%@98oLmaOm7OzF+$!ba4uIqxsRG##1hBKIjOZ;i?iJ&I##sF!K zp4vPN7skYIM<(K&f`?XOgfB@fmE+MD85mD+tcei+L`OfKvp&LRdO}E;BxedC0KnmH zgD6<%A^d4?GAtaF-HF^njBgx%1k{aN&fuN^elbiwKe$8Z`B)^Ycb&eEeYF#p?=fDE zkbC9CWuY4!Pr9CD3q6Hnn`Cg>Tl&VcUG|o7^?n|yE#{5nEKUJMU<#-wEi`FNJ?*A8u0$%jqjwS=T{0B;f= ziGP1Ow-{8yHGzYVf2(lDZ08uYago7zIyVYC1u%@pU$r@W1AM4+c-l~=%f&9QJii&s z#`}bCbGY*aFUf1f3Pb{3nMG-2ScRHOEJ>-f_j9-h(qiD3Kmck$eh$~53vv;^8=(V?A;coI@@Yd!@{&sB_oN%EJn^!XClV@eP7TXf zUa_R|(mq!(3khiSW>DPvHQXgJD?w zz3F@H&K~ zb>#kp*14Z34}c8uHReYQ_LM(U;C}Yv)hNRj#gA1kUA}m5W&Fj;1@K#x13xMn>w(#g z$GMXk;j3s)p&5j!O%o^KLp?xbT8k`S?TW<5Ll?1!7Kw;SdLx>6MwL?EhE~+eqxrM0 zA`^%!E|A2V&Eo+x+%Yyz7t4?ne*dw2F|#J;Ugsbl%f?=nY^=o2u!`nv8sgNlaTfMy zHZrXx{$-VHoW~xT4I(Dlh-iUW$VTeh(0n!=nsoAWr$-YkK-x$Qt(ZWWTSb#YCw(`@ zRz?O$m)|&r#`g}m*yy$enqb8VnR%1%y!)rigiJ=-IanF295ytOHX-AOj4nm%a23sU zG_Qf7>Mp>Rs#E3gc#H*uJ@e#DwfPsdVj>G_%{OYjsi4*wH~^DNH~^o^jUIsat`O=r z>eDfb;f87}7^+K1TC#ng?LIu&b$ylySME7#_V1 zp9EH<7-6%CCm@QKiNYWYo>d^FXO3qUBXHA!8Dv4W3M8XwjIxXoxRxt}l(RrgVI^eo z4FF?kfhw4QW~vOeSiV!D%FIRgZ6O6Pl9?5fl{@`Q>SV6?(2_YaV}ATR{Dv>nJWBHo zns399(+^?`PVZWsBuUZnLeSPzC73;?zh`Ea+Gl2|y-$VO|HH_hqWKZck7@oFrVF*3 zx=1a~tWf)>z>3r+Y_=8zL_IT67^J7RgOr{*YFmuJO$TO>p4twQQ8Y$b#t2-?l|g!H zD`Y`!#?S&)Faga}8LFqY3zY8LLJDA{HY=pmes!MO&WDyPrS`AkH#|f09L?WoHrQUN z{WSgoYVTI1ws3TW+~#RO!>qj`OYMmjYHy05!e%s^(~P6p0;UVKo4QCXR#d3H6|f?; z37f420a4FP6b9+3?I5LRj@lL@aMOVqq^GunWE722mN5d?a%GU7+6q}vn=!ON6-+=g zRfg)R?E8b4? zrDu-X79()eff=Nywu598jZu~{0@re7ke=EKSx}oXv_KV1Kr>Z_>Z$DlrTey!0vM^y z3MsWad1^Z!TC$YdN5gNJMl*wECe0ieQu}4t)@SYc<3~7aa|yk>FYuP);%#M?()$&Z zF7AymqF2$JLc`u&*nD!mwPb#!BQh^yPer>(WFlrWPb^d~^=)Vchbw5(DaT=p43IVw zLo4W8qRGV@eK*EdMg~Zi)%4=cjg)R%pb1u_7xN~J?VnYrR~f9FQ)?m}y~1g57?#nj zpjiz=UM<8Hyz+MgCYN^uk{@F3Y`pK~Yc9r%U^WZo-2fp}c{gBfL8w((Lj7BXP?s>2 zOKC2nVec-4%CwfEt*Q{}3XVzd1`siuW@4dKsc%E`gmP%oDMu)a43IVwL-T}EG$9my zH^x>*21u9Hlu#}Or;0}Kgu6}ELk6?X$# z5-N8$AimP)>@2ZfR1ga~;N5_?GNe0c?xJ}c&D}6v*wxfU%6PV;7%wgF0am0KVY7)R zAc~iX!XOKtRUoBjj%OAlaMOVqWI?tHB%^4IvWyY9mMeqw2VWg{aqwjfEl>p$&`gz~ z`h%}aC*8M&6u?MkR!9%Nr`O3`Nrslpkr{Ub-UGkkewq){e4OS%7;^fZ*jCOfWs>yg zmF<~bKd+3`UY(`({uOF}iIIJo=3$zz&^!Xuh1yMBq!z0y)P59Lk=lgK)`EbjXC?}R z^wf5c(lbYGixIf#zzot;+d(pl#wg1efor)kNKb8rEU3*GTA&IhpqVN|_0)EO(tTS< z0gTjUg_PRM^3--dv}7r@ABW%YJ(?$JeoXT;45|GXww3crp>}d!*^=A2y8)5f=VYmU zK!w`BXJpUNJWKNjnm@vHp>|Ujsl_=JYCi|8NNvJqYe7KNGZTeDdTKjJ>6xRp#R%MV zUp$&`gz~dTP5s>Ao$b07h!FLQ3s3^3--d zv}7r@{|Ucg!yRBYp&3WB4GgLM7siy{!v(d|y8-Q)-8gHXo2B-F6>9Iy$abOGm1Z}Z z-C?>=yQz!R;@k?g_W)L;Hes{1ARy|QiNYW~wH>7N%u(B71a3MogY?vPkc^@+$}&da zTCNPzQ(GYmYBPowsDcS-rpiz~wOyce-xg8;Behu}rS|eXwVe+wSxW7R@Ei80Ihf`! znwP z-|t}Yb7H(@o?Q_XVkYy8Qp$(TPs3cXfIhYf3%Swn$hRuyig=r4E<-w==9M(rR}(~s z$Zn>!B>OZsLonI%fE8y5edyf%R8BzzVgWMsZD_^hP@;w99K}3|A#((0czS~=v}v-@EXzQQ7!4$T=blzHj z9*&$kK7RS4s~__}nOQuh9#=Z)!X{h#SSR`(&ZZqIcC0Y#P1q$M;m!Q( zEihe}m1!->URs&J*3zLei-_4w6D!UOrEf#?%yMYbDMu@d43IVwL-Vw9Xtl|;AgQ4V z79cqaL-Vv!v|@6l=f=TS(i~~U)aeBFqZ+MTsG3Gkt9#%pyo=`jG#`W^t?tA&oxqBh zb$8*EO<>^|u9F=#>6mhvru<{{q}j9d=Qhv71hy%k%^Y0tYW!a(ug}7%O=FMzK%eL!G=TtsgA9NcPWjJmZa`&$FBIjIc@SBc!-T1Lpc4?MWAm(n&GC%y=Zx$ZG{2f6u?4p-2mlbnqUFaMq+3M zeJg16-56UL86Y_dLo1kDL8IFiXo3|P#=Pllxpl&@3bN+VF)Ta>U*YdGqjrQD3qxu> z!w}XE70(5NT6kBqp6Y=j-Dnl2bEeb>=Hw}^3qU3OGRT=LPt!SZTevV5%`I3I9|&aU zu(bqHd6Zx)d?=3+SQB{;-~vY&J_oQpn<-%klcZ50BtTSxyA7gXNeLm}Z_!s1gaE|n z2pEM>qxUOb-zA!Sqp1PK?1x?8Ka7WAzISA-YlpUsQl2$1-5$GJjmPdrtzPK9y3}zD zjYcb5YbvR}5T`awFHUW?<3>+yj<4i+U+fC|!PN9=IkqN}pbINGei5-X$Arlxhychj zcN>Jyu|gELJfyW3W2}^g5!1{-G|i7x(_D+GNb?~GAsi0FG#|jQ)(&mu(rl0J)bd;$ z{KK(i^eDWTMv{CH_>6Z8EnjfPl2t1fELx(ho&scJ^+}bC6Cg~I7%HqjngQ_v8EYcx zx~P)zX>6t$CrpxYg%AMXaJNDDj5`SWYJw1em~KX)>Hcap-40CyD$?Bt|KT_orh7VL zEq{-C=A`~mj8v7 z{GY_2PL`l5`9F~nGXK^@l6P?>|EIE<=AST0@)SY4 zHUADx11j==8nDAM80LQ=9hI{`xiGbM&zbaT<6ToP?`sZh&EJFQnV@hD=)9 z;RF_XIHgtPbt&YvCFFHxZFaj2&P-j)i%Q31HnGAQ39F*6*E2?{%bJKZE_LPQA@X)M z)AAA~Nv1*w0C2e5Ad13PAn2*3`6z_x^+TS+*(hxbYpm4v^CAu-|BM6z{R zCE@q8nI@btNwyS10D!~Y2H_K~5OR`4Uri7K5EITQG~r*bCcG9`1RDYwI>RL=is=tO1!Z%@< z>W3K?-eHxby2YtdFPUAuszy$it^l{?am_^wmqv0w32-C#m6fCuAv_^rRLK1U#zb;k z6Uo>Wm83t#W}0-uBuQ5Y0RRqn8-!1~gP^Y_2my#0XB3+8Z&owz&@`YT<4?nX_%#eO z{$s{k&L%xGr%djd>E1P0J3S3Ln2AFhY1=iHcf0SYQ;ZzIt_0(mcQF2cxKYMmRU{vW zAA}wLgk9k|nm^P01*XRR=ob0Qw2r>g`{{oJOsohIGwD>%fJ}WGT2UNc&N?&((1<2j zfV7bqn$#&i-RjWjyE~d-0g|IIG=CAVXfi9(Z3{HPisuGbfdoj3km@ zDjfZM6l-IeaWq@QkW&9(2y2ISk0MSe6))zCcf0)S?8=etsw~;AF35)4UL@O2jCE(4 zU1)ZdsI!{qX=0{z*p2;mhmm4jY+=74=TB zcSj+~5?A7703*T`)F>zNL0@0KDv?v^LtgMuI!>Sd7{PIFi8D}FP zJF4Da(OM5fpFz`0(?`=!GZUsZ#UG0g^*(LaexkaoE2m84MYV4n&L5B zuR4b--M#8OuAl-uGfue)S6(hXE(c(g7qPSDCn`$a6p!J0c{yC^o~yk@ z3-HW14cE)d;WD1ac*|%3p61VRy}TSQLur94r~tRR7_OI>!(|k0aRn9Ntcl@zc`0ro zFUBw;XaSD$A|+H_XVv88!dH4!<#ioAhu6^DLh}ZiH`BZohVr@++lsscTX*pcR(-?0VEx^!(}Kfa0M0MRu{wd(sHgs-Mp{t$a z`C|RB)$qM)XmiLiO1Il*45_jq#Sz0zA#1;d)IuT!zvDS5N_NbunD8 zDTm7_+Tsc-z*!T+^_p_H3}8gKf*LhNKB%Tv)->gOC^xE_`YAkzU()=J=8rUgrFq^9 zR8vpk9~Did$EpTR6&GEhkGCtwE?vHe*HQW%zzdS)x{QUrWZ_vNm8v45+Dj388d@HN z&tJ0={dhaHeE+3U$r~MKqo)~T80zHE3VOf>l zXR}l1U1?F)!cVOzb_nHO76n*oP3~lP`-Fdt!)9J!v!o_56Y{wpK?tCk_--{5iY3)h zAg#tb0Lp5-jo|pMH_e0$v&*4WR_#j~{*X<>+tM`fk%HzlaId7{50y0hr=&qbuRW9y zb0D8<4%l0B@V#mdYCxKUkHj2&Ky$FU%Yi)7)OaGn!`Kl^l?Q*!CgAQg0iTA4<^*uB zB;b#g1bmi(?Gt8fB|KoW91rAk%>e;42j8#eK(Qp53Zyys5Zjl$`~-%j{{j7#%RQz} z>z?Z3`e|r1ih^|qGAs7z_0x@diGfPt`Uz7y9~^iF@OC!$nM&G!%Rqi7fmG7|8%D)* zI%^_1yRnk?Kd_mmoiIt-6+!@j!`%kq)2ZI{&+R*wU~;u{|(sT zc{^#^|3pXSf+jLNrZP)#9n8>Zl&6yPArPEN2Em`@MhX7eN~Sk~+pwwlZpySZku(ie zGQBza#!M3?mm>lo)7)(kKGOt*BCoR$RYIKVKyeC~!cv z!5E+vEH1Ng;n96#cx7;al zN!A1$2w&k4nj>hAgdw-~!xr2s9>eO>N27=P;y=VB>ht!MN2B8l#AWcWg|(N}pE%5=;COk@=ivxz1aik12{G|wtUD_AA{a;&o8 zfNFy=K+h@{9^E&FR|W@24#dzrt73TZcW3Ci1)#u1V!hUg^;nHqE>z8=C)RQB6^^Gl zk>(T_5~~ke5NoISl|f3Z=EayPv*P_0xnz%Pj%D;WWoh+rg;u9Ao{j{?1J_cCta9)u zC#y{B=qshw=^U6&Rz%FEnOLY+>f6vftrRWH;DK*mkbXH@S#Ut*{RU42x8a=00BA###&4o0V!jMyEU|ZwV zL5GcOwzA9M;*OXn*3DUBeWgOI>loDaG&j)P2-9W0%CweT-&{Egz6LOnSVYX`npmh; z>f6vfu@tSi2A6(0Vp(uNwZRymCzcD3?i<4^g99W7VrZUNiYCOO+ZJeo6^X^v=~3{T zwaLoGscH1YS_5C6D|CMFvP4iJ^H)Ikegh zlHxSY&;$#R9EG8IN-3I9if$W6S{Y}g6!WH(`f-g?E>6v%r__hwD}0RRQ#7B2A*J4r zEhx1^NvYzM4b97zld~#Wd{Qjya%8$COQx?@$n+J)@(9gWX&!~?GDT%tONwu)km)hN zL^2UEn_^<2OsQ`}^JH>p(kVwKiwux95<~N3a%lA37+V<`AUO&{^JG#qArsxUKohJ; zCgx4a^sO41T%4LiPo{6fSNH+VQ#3z;A(_5`Eyz^-N*+$8ux3!7Xm81;c*`$$%zJ&7 zP>)s!^&7_XTbkd|{2rzYp)#!{&9AQz>RG@N7o%@X0_Ij6>!i#b0iGox3o*TE^ z!r_eP2#Ke1K6My_<6T;7A~*hSb@bt#?IYPt=|h+#qY5Daz~OF#C^%C>6zAh~*MuS% z@!lZ=5u)^7UcGl%iz(J3OaXS74#VspMMvem!x^*n)*-$H%-*Hd!Qj>*b3TgCa};0Y zh9|JNUg&lkju%Wf?&-XY8@;FV!y@PLPT_3q3dhk5(6Dz+$I%G2^Ju2B!=citZ2nI(RAM(P~d>-5}Uq6(S=9PEzkrj z>i9O(@r||Pg+tafdL5qxU*S}m#WYJ{$fQ?d3nmpOv|th*UBI1VJldnb#g_S2t3oF{ zr(ksYVL_)iWa;!|g-$CO$|{<(Y1q39oieSZWN&b~CIQajn3PUL%x0Kas8Z_N&^(_0c2181H^8aD)y~FG%jxSJF0wi+INr)gJGA4ZQBj16)Ii(qiT0We@=qA|9~83V@T3?^f+v4L0TR8LR!OwZLy-{bea z?|tka)pdJ%PFGc*4l~{NuB1Y#;unOgl)~w+`gqlXQ?#0{|1e3YcPoVYJB;Bt@eD0$ z9G>EWwwjnr&%z<)Qe%4otKbrt4raVc0PkaaZ6C4<(|wlW~zN*~a8+B0aV zHc6ylTWOgm(hmcXmZ}lSAgi60NUtDZZ$i8a@sS7+>3K#SgVso=fJQ8Vgcrd!J2WX;@f?Sm8=b9$*)!@#fl=r_~WTzF@;ypbpGNbrQWYlYGs(S3PeAMRYkOz?GmMZ zT`{Fr6Bs{20Wle6U`b=ebq6#{DTBtFQj`jo31sjjpjk>8G}PT_TWOgz+RJb$*rVNGH0b-bl7FxZgw5_yEAmK$ovs^N0sJ3&j@60t<+g$iF|8AmK-UTB|6FsRe z38Y%NMk-T<2Gf#iq*Us5gBT03p9qj@xTH;_;ulM1wR)Q9STzp zgXn~qAfg4S5~Y1zF{ut07*8r7CL0b|(p+)f0nL)ipz)>@se)w!89WJSmQ)4}bvN2p zS|*V2BA{7P88lSe12kfJF8w%gX_Xq646?@1a%r;E<&J}x3UQhUaA~5XO)?E)y8O+Mh1;H zrDzl^6G%7_qgfgmG}PT_TWOgZ z5Lb!-jlL~u6O9_adRwl=@n(t3yRc@`Ly&CYOOr(UxI&~~!x%2ae*^6rn$^grL}_1F z%%>4GKtGvooh^+;g3H*4!@LAKuks%SOuj*-2u%~ z%AoP46s3Y?0$E41lrm_IlnQ9X5=eLvY_pV7wBofBYHoD45@t^+gihaNo=~HdF;&B8 zDfNuhukG-7!xeL&;aP6iFtCW#cI z(N9mL%L9>)s}ac{tDTle|3kpqRuVBU!~!Bfq)%Z&`P#`6X;!bDY9y+>cDf?Tq)#hM zS`yZfNlQUnnsznPDN&k1zM?WTEh{jdPC!fs8CV6KLfrw)(#fFlrWBomWdd18vve|O zjdTiV#1cq&5p1(VlcE)`oltY5vz0J=Mj>=MG@V#ulrdGqXc@JN)a6!(SPNoZ5n$Bv zk_w}W*G`sEJ$dcqc4@VR_md>2{$1fzfN-0_vdw5&Bc~Fj8R?%?I5ilK=@13PWSD_f za4OUt&@8768gEK*Dp)3vbu`NhZWu&6#7Gg~)RvM)E2!DpxZ8e*RvUL$CMorw3Z?dhIUJ_OLfeam zHBu^3nt{HuLaBY=mr@Fd$tVM>pj48G~Se=RIp4S>u8oz2Cb1&0gYG!2`_?e zmQspVte{YHqqCJTdrBd6N~!)eN*Pl%jFwUdN?q zT20rlN)qa`3ZYJbF(lN9w5X9waY0*6yn>nvhi0*Y0#?B#R2!pNE*Ui5l;TpbOd#uM zmP-b$kxK!MSl&t>(0B!9&`@oXNHH4y^hCNU5UF2{NCsK$v_v`s0XrMwe25E0fJi4x z8m*v8BF%aQRUuJj1@+S;lm1&_(vM&bnRGF)`Nkbzau zDbyX%ES(G*Z%WZASSFBlG)pIg)<~y-Ml6AZ7r{0=G$~rKf>4XBA4_DKMT=Kuks%SkhQ=-2u%~%AoP46s3Y?0vS9BXqHk24Rtr# zR$3;I@FJjDN*Od%+XFOWc|IX-%BN#$D{_OZF|>TTN9uA9LOcfXlnC(YE=h$?#l{_0 zt4raVc0PkinCHX1Qe0P=QnAPKizJu6sBmeXmBo~KA^M1DL8G{!>no)h z++S2^G{0Q%G(wqVaDgQa6snEUER75rZ%WZ9SSFBgB1W?`GH9r~(YDeufs8!0G%{$Y zwg+g$@-#x+ltzTa~Hv`ir3ML@G$GH9r_2WZ6dTteKGOUKr@WRNw6mP?yRU2Y4AZ6S6L0WNJIX%m-b zc`gO-1}a=CmP)@&a%ryO2hjZ2Svp|KNQhk^Mu}*_r9^2)_?HzfjTRWsB_Jjv3@mA- zxbA>vxn$6IQ;JK$GJy=91T@PfgNC{rZ7VGkNO%#@ESC%#s_g+9u{@U$H|5g6+S1z~ zYYZ)y_L92X{tyR2bcz6%#z-1*$((WO!BVNlrDCb{t0b4&DqK1mrc8#I0&$Fp7F8gEK*DOe_u!IOYyxn$5#ccX2kWdaE=0-EKL zK|{4YKqHpt65^&@TC2t-gRC*MT$(0zxzi!eg6I|jE*&pv6PJ3hRH|{QSStNG$)#Qu zE`0~4d>7(-w5RcfKwQv$UrIB$zpl{ehjPKw2xXGN1(q~Ws5VBkG%{$sDMh1TnLxsc z7|qhiprP(Y+e*s>GV;{Y$e^Lx9-tA+(+F`>8V#t?$RKMBEsZXNi&sHh4RMVK(CA`G zn`qR7rBaPX#Zu`vNgDO8(C8+Z@&|~UA#M@TVn|AqCYOFw;nHma8gEK*DOe_u!IOYyxn$5#ccX2kWdaE=0-EKLK|{4YKqHpt65^&@8dT$wLDm>r zF8x{Ra`!?!1o5~CaOn<7o4C}2rINf=3SZNFR=yU=A3LYp*Cfd_cZE#P!j|VCaJPG& zRy7VxiPFBV_{H~&a7y1)0x=n4U`aE@bq6#{CPgd0UCH}WdJ`xbA>vX{Bfdt$1IGR>3$y4bB8WODkg@s&6!} zG)^GlNIYLaTOx@w5VBGSa}3){5&6XqHxrR?v#~ zrDzq56V%{L0JO9+=ArsV^Gf3c5{?8kODlthYI}f2EKe(hPHA;cjaCL(!)R%>tJLLo zhu9lpoCwfrgru!#HG2!IFbNhbEk@w_B(?fhsMQIxCO|ma*0{8a3%bLlw67*!T1}J- zo>eH53^cH$u|l;mnq`%u6|CZYDOLsJ1QmY7fRr9sm-~ z6YKgwtgafdjHwz)ORQty2Q*7IgT|XI(<~aX1TuIM&@9soTJ6L^4UJd= z2`>VgWtyV#ceqe(qo)-=JJlui&XgZ8@Z&uUIXn~5iR(XD9tF}SmD!e zQK9k)h{-4etKd_pJD^!U88qIM;#06pAnRzBPX?`#PXUcs0tqjIZI(}p=KODbqvl3u zD`EDOLgJga3Vd7C}kI}HoeCX?w$RIwLzlmcQh%D^fp73vOXmQn_d zH>D^QEEC8&nx&LMYot^_BbGqIi(s3jl%jEnLbZ*aR$}b=gt#f6PO8m>#!ii)<A?_Hr;{C`exKyiVii7p1aruzkD>jGhBfjjQJPWy zeT7e-!Y}0$5R*{`R>7xGcR;gzGHARh#iwAIK-SSLpA1?fp8^`O1QK2Z+bo|Htr((E zbEC7BFndZNbV{kIHA)#%HH?;0Ur1f9cRvw*Ar=$?O8o~WltYxIRL_Q}>d{RzMBS96 z)Z!INEd_H(simPUL&F*=l_M6UCXjVB zODTiaNU4BEEP;dSr+!kGTLWSc#CjsY zrxhfPhN$w;W{-xbDxK^Q^@k*%mZa$;>G-H2rg=Gie4rLh-qriJK2dsi+q3(cY zS!U39lVzDjqaOlUN3$$5Xf>AA(1<0F@FLh|S*B>kPeh~UMrSKw_AEo_lw~W_SY}Ms zFj|%!CUvC44(1<0F@FLh|S*2(Nt59>Jvz0J=Rv~oC zsx@n4l`&PrXjyf&)a9;$xB=oO5n$C-l18j5XT%n)DyGEl`3{_L%yAQ@9CFm88QuD` zEd0^<+hihKWYV~B5&!m3XTrZoePJeCwP2V3@%TT%vb$(mWtDX&%*2n!ha&vt_uCAS z@XPOaqnHd&_8-X`6FV1jC(74}`QM2DEGEtuOjP3@liP!Fn-<2k;g{`sW9|R)K2n$k zSbF|vV-f!K`xD+G`Hy2L6ScP9t@-*-d2u(XoQ=be>9eC9FaO%vbwiAlf9~rJg2*Ax zg6M@~AFDx(=_BGuh&v#jfOrdHKB?Dr&o5#K#B=!gf{5@RuxmDwH|`%|Iw4@zca6$f;aunO`N6K$$>+eV=gFCed?q8F^ z-8>F1N;d}=R~g*DR)YI!8C;Y}1(*36T->d}9shqA+?tIV+%J4^|C0*t*jX@lS`pm4 zlffMv2N$KAgNv&S?%kE(_FXkF7iChxWxfU%cWZDD{67qC%|;FGB4Rq2J3m~gGIzJ# z!_UblnR{{(+s1`Uey)jIviH zP@U5|yEuW^X*bwJZ{+{94-h}x+TN*qwL325H($#k4-1XLwO-L|q7Fr?4F6MIZ28-T zMSlV6;_k0VbaLrmu*k0}Hii+K(1@m0MJPf~Z#RU3dz;C(0c z6dQGq(k`>Mps=)EnfkU1miYfl16wreuT`V2fcziCsI+shBI;YqC%3%_^wYt(RDM-8 zj(WpyGStJ)r3hnwM>6KCRbt)_6R>=#Jsl{v1S%9^wCeYAifP`DgW zY|IsfA!5-)z=Ih+;4nl3{(3dw22Bkr0zMkv?kNHR9|@agb}wF|dBblP)Pr6AW&tDq z$7IA;uSEO+*f5?pG_gDsVXXdGiTFXVJ475vDV766B96-e#YWttd?D*qVL3}Df*vgK zL5D3G^f#(OS3owQ+EfI6f|%eYia^j0fm!9=L(KA~-{np|#S{<44E>$*kg#ZYNH}T| zo@4wgy%VLz@T@SX(iq3Vw&Q7Ar7@0$p?Hu9MHt3AD~&M~#X@5MDQXNwA%;NVazL?- z;ZcrUQf?osrEOyv6k_Z&n*a0ofR8Q_&cwiwW*b5onB)VQzW+q0$%o|{nIg~#-$O++yX&)U z^WKSi6VJB#TAWRAmyAP*w#>FOkmzi?K_%orlS}UBB5EUl2ze;NVBJ*-`PIMidQZmiVZ{7LEGb)u<~Vqpmg;QNLD9a5soR)PDuDQnT%> zw->tS9<}>!JIhyRx8HFF<9)ACe*Q`0kUMVFF0%N;DY84If@N& zM2+7e#-RuUbYCUL{{Xhe7%-^-0YGAm%K^g1SRso4WY5x;+`grP89vM~M8kYpHOw`e ziZH({Cb-u{Ak5FftnwcmGRzvkR*~)v?v|{+KN;mAl_bV|EP0>d{@CAA}Ds| z_~^km{SV4$t^a{{{J{rLE?;=BE|rA=dw9_Z{-+N5!K_sw`a>iim{yT1y7ikV?W>N@ zn6HUDl>4;@Cy(ZUB~2689ngwK!ar_Qv|KycT+;27w%T@zL5?2x@&o0~#=26M+Yn+?h%H5cS_35& zY85AcZ_#`vb8c1}8YOj!L2f5^iLF@>zlZaX$ z_f<+@6QzA!F{y?Nj3*TklW_)?K*n_kG)pQ)D@eurQltvT32JaA09sNR^H6=Gd8Kgz z2}c5&C6za?eK|oI%bI%tNI1`{hXSv9)p%u0)kyk5io6;nCb=;XdqM0c0=&v36<(SD z>}+|}JbJm7+zS7E>ft1@WRzXHb6M%NezZCi799rB2{A!L3t}ZoGu98Ac?9Fj;R54{ z1;k{afhFw~*B#I-u@tQ!7Vk?DD;OuJ!I=PPiDk?~^^NA0#t9@G322sB1`XBr0F79l zRS2DqR=sPiGRPW6%c{vzmpc~XB#2W*fK?ME6;>68$}6K))3>Z5O3gR%kcs0a9C`4B z$uc+cE6RG#oZdf@q}7NDtYrz>?OA>kep^RspT= zTBpsJCa=F2=r*2rE+k{O{KsW|U(tCE7CRj$x5k&ix;XO#d(b%u*IJB+v@~n4$;CLDA$q8$vr#ml@DiXLr)IgTP zXN+ipEMv8NOEh)OEN~u?#^H6=Gd8Kgz2}c5&C73}&wLL&1mS-12r|g=q#x8@b zVYKY}kJRNphv>DMh(02~u8(2D%T;_=41`!m1ZcLLq*!v4G%LPa*Srnjyx;yUfR1uWjy)k`7x*M^ z0hUIhZvl3%kZKdTz#T4qx-shA(5P zM$+;CG8d09ndVX3>t4ru_{<5kinCHW?5y>P9DQAmKc#5ISYmQ?=2`n5tp4tU5^Qa-9$pA&wRSR_!mTu*$pz*lpD8QHrB# zpp;p7F#t~{X|-pCRwu$FPDCd`n@ZyvC&5H%UspT{o($h~5(Hv0(!i1ii|Y<(mR1Ih zH>GG5EECA!NkFr-GH8vHU_c|5K*EcFW@)8p#X1l*H#%E!)zj+9K&#hkv@)h@7%i>3 zq%LarYixyF7sIeiAbt!nQ$&j~D^Z#u zf2uNOT_!M|TtH058(7k8aoqvUl1tHw$&mM@_!W#3)Zk12wEQyWq54MiO5+3)js!Hz zFK?cIkVVZs03@8}7ow-b*1WaJ(3q-`wEX&+)a8B&aV^9RBEYXJBo%&{Cq+9M&JM?- z39(ABr;`NRt3t5bVA<^ucR>76L<@o?N;Bq9R|xhef$;sl6H&h4rrEO28}nR z*cB`j$lyspv+OcxsJqd&(lUXB7Xi((OVNta3pMuukZ_(|2%WO)>DuUJOw}-2cHJv= zxrZPghj>Z^*!5>gg ziF9w^sYTG0TOplUU0pjPsMT2=$KNX)3AEhBY$5vhMBBlVp)QYhUVDO_cw{#l9CfHG1j zlZq7cHBz`+BX!suN2*q%Mru7DskKs(8qqpZXB3foCmE^l#*sql=1AcxBlS)tQk$2N zLYY*gn6Ht--5RORIgV7VMvc_AK2lpGBDLFyu-lUCebb9by_<~G_u@#QbaSL|m63Y4 z5~<;3q);XmDduaWaJNQk!W>7cR-;C0l#f)Fj8yA!s;h|9d&x+BKaLbiH%AIr8L9Uw zk=nbA6w0I`#e9tv?$$`TIgV7VMvc^XAE|v)ks95aQ)d>DdOsPdAHE=k`DkJrN zB~pi%kwTeNq?oUf!rdCF!{<0swHh^2lYOKnCL?7zg>U3xp%&Syi!?6LWTl47VUb3I z{WtP-kyd;o4-2{`6s4!wp%mqP~>obZdsMV`HsLlsA4RUQt<+YL>(0c6dQGq;=ilJ()O9iw_UKrA3udH8uj<8QCC17c0i@! zT!OnmKDmoTU=?*9E|t3jQPi8hs^n^c=Sr9{|3@=5(X8!I0zUH#kLQrNS4u16o?#!Wb0pW<$mO2UKEVU^(j7Uo<}b1H#+!T2WOIcbe~CXE|1e2lik z!%|sjha-x1kRwB4ySpD|Jpl0_M6x^FCs%Z*HBs7E8NXzH6n9h}vAx;ye0^3iU%n=m z_TiTa#UDOgKPxu|ThA?Q)rV``R+~7Vp>5K{xXRA|njs1vuAld|U|6h9oO-wB>%@t> zNs#Oags1gShz#x}%{fJ8tYkepUA`vOqsd={88f>VWaHs^n(d?C`|A@uPMt#X4v429=2QYXqXJnIYn^ZNCu}n4h~8;2M1RfoHr`LXe1lzmFf|k-Y~-kXSZR}vjcOm%BsyhzRR+EOXr9?c<8dlNQlD`+GTmV5xqQe z)}j$hAj5DG&^RLcZ!{DGfY^q*TcZ(6AmK$ov+qI;8mjF9nnt95K+_kYYu8@08{}xZ zNsJ8{J04EI=S04Z^tvsMVF~Ixm0;gbB zuuo8fKLOA(O945-qWUBk0vfR_v*52~mO(?cJwUU}0-7>w{Tj0ja-CTlNL_AYh`|us zhyb(JkyLtm@vp9wS@vrU>GhebQcF)=7b^q*Gh~eB3rTJrS>e`BFluLr;SlX2YHMU) zrvx`q+Se5?3PuP_$t{2;!won=jjIoUmRkzQA&WPqh!uu-kI`R!K+Qb>B%I&Ac_DCXof@}{sTxMV$mFOsTIzCpKFT;xAT)u89Iu(hH!;hyzX< zF0MWRT6%dvKXmcF6vKjjf*Sk@fRX9iO8$&ree~)%#VP|>c%#BVyp$x_qzcK- zfobPLoCnb@q6NtkrF~uT81_wpDM<#QBkYgBcN--=LCy>FJfMyw{ zXk-}bZnUkmOd#P!K(h?<&ic1OsJRD#g!2r0DKKo^8pDjK8b-^o8B&+~KEx#umx%zw zz9p$JtT^D19K#yl7tLxIi%Ir!l4M6$NcIa@cQwQ>A$}#I1<4Yn8TXefBiU~RrX(3a zlW_-}G+$hO0JJ3YfPN(7eJP#=`vf)k696sG6p%ba^^NA0#t9@G322sQ-aOAU)Z7C= z!g-z{fI5J6c7xRAZi2WC;!Y9Z*|m}i&-%3-$(o2(ye(^bQNw?48-5`D z6?p|Bryt~HKzMKVh}0h5flR9~?ta+$0K|h34~eKVPBQ)b8kC7kvjtwMFzzwj5tjV` zjaxtg9V(ZGYyh;3Q$T&1Y=uTFfeg+B^DNyIjdVlZjkcAR2_(D-XqIl?S^v%tHTM9J zaGq|6p3-f-8r_Vk8b(XEr=>3UEW}F?uZaNNo{&`NR{RgP^sttGE9maSy$cO&da8`c z_NokOV3#~uK8i%2EKjab>^-^U-iP=A;zJQFD3&PAsJ~jF*vA4>QVgKUr~^(KFRnfS zT8eo4loM#yV zs4Uy4#xi57M$)qEGpWmc0nvL+5q(5}WuL%=>f~u3E_?LyxmUX!mGA2&jXU&+@rO+* zPtEI!4Uy>Ri?8|4J!zct?{t?I1HyRrc!g|>!NkQOmVj7NM2&2VN~VryiPA-t@j7i; z+@kL_0U3qf!#RLS3&xd~aF%NYTy$hC1VO&++%{?ke%!fZA^0x8^X(=vKI4u$rt=X0 zg{QTox)6q!iZJM_?E!MF_JVRv*oTb}T*V7YpqFmqo%7l#qHdy4m)%qi|+23N<*5!VUtBC~S)= zDpBab``E@P^cuB`hGCI(7+#6P&>+FrZK>49wRs@OBNuS;;We z;4}<-2sFa5E2@x_ZxI=GnME9iX&e?!$KlmD4&$VT<~ZOg;~-#h9LB>kjROj$;=p{3 z18>(joSlqA4Nl|GDbR?+L8zh{2RX;uf)9P&WIudz%vdZPjo0F694+-UM*~+G4FQXz zF$MNXG~%JFFaSf>-r6WjiWxZV8jRzH+$nN<7_~l*O`4W6X)5Z&sD-OENq}RMPD2rG zAQXxkNN?xOe$?Xa+Cbk*Hc$;t8|Z9-MgyIJD$rlO<7#M*D1D8?}7p_@@ADnWfM& zh=2@Rry|i|>tAsgP`uVMAq=?6FbG&2h6{nHVL+i&7?`g-I1HCd1v|W_11Pbt?$jh3hbB<{Q#?a85Wm8qQl}7ML+O6l+Vf~_Z-CY z5HE;mv9wQ=X3V9svY%j!@+HCX{REK7CIpy}A+EfHBVYYawZgg8aK~838&j~c%EjApgu`nSfiOSR-wFTAmpLVL+pSrFnu0S4ud`GjE_D&QNy`5Q*oh#t16aHm*_4 z7^~5=R9ip7Wl35 zVNz9intHY%n7n$y(g_G|D2=Kdi5&t9@k|wp;4o{4z?WRYA@H40PM46ti2|z_L=Yfc zE-?xMl^6wqP+=1cLG(ndnnKoO{}x}uTcr|_?^V?*-zILP7~w{XKr0M~!85y;@Ov(! zhm8sS9qYhc9@dhFk_Woc8u{XpwmkO2)ec#>I2r=4B>lU%1EjLh4*x9L!LM6@=k|qd z`$6n4qSn#(kxYFhnJ8UUSvC&VEeGNjZ5%*GQT1?iMOr7Wyo4*-sf2SUVCWyKH`y25cnLB&0QBbe{y(qhHhc#~Q$HHeGN$#{ij(Il!dh;>t@n%Q1y3IK~@O91G?N zY;dPUv>Y=A!hl8tOY;O1u9R?=W8OeNnV{|-AQI2hj1knyWYZeWjIkO`OSAK&GIs&Q z42bWG0L{*nRA?5~#xbOqzSOI-;H01baDjV8mBC@Ks2(1AkEo^38I| z-6EpaZ9|Yl5e7(b;~?J#a1AnWQUL;j1R0l03>##{V31kX_DVSW-;mH(50%hFk~ z+3kk4kO}h&=`hb#{K~Q)GH{!FlNC3GITT@t1UC-z+pt!{44hP$fgoYV z^OvZ3u7RyajZlO!65Ke>z2s&eXW*pb3gm-` z*Q_c+J)aoi77~F_&kcj+-zJx%bMs{A?C@+Oy;3^Ty(*Dj8Wk_Yiklc6iZDuo8%KIM zSgVW%PAbwskVxZliD4tH7)7L6JQ3+&h>tW)(MbQd8tIxZVwSB8-#Z#GGU?y`#1p&!CtWv zY~1W}DNzgdF_Ot(hawD=pvS>J0p&EHeT5li zTlJB+%)rgsRsfIM$^+9uX)L&yGN=SD8n6Nkvl@Z@_xV}_vsGooumY@K>@&aAZ7nYC z72D$wmKGN;Gb+FcduxkJdj&nTO$p3*8g+|dM^AwXE9hvK5_q1@HKxs|1M{Sc?Ih_? zR084O!U#E6&*lYi$1%}cm`YfbA-?D6R=8}2|48g}mqJ_#@e7FGKwK{Zz3RJ?O0O!u zuseKYzv6dSN663m_LDA>dco|x2i?7fkJSI*WBS*S(7%Rg|5^=`H#(KPHn<3hzBU+G z=~=hRB_+mf&~Ard>)jz*i=Nf;J~oaR5cjM*1=05`fG1lWutm>G7*qn=o@Ib}f2!4k zy@K8n*!C;~+}N`MFkuBf4q<7r?O6sG#x#ba(lUY`+NK1yJ<9;Yj-CP+Jqztp0^6Qt zfE#;O9EwUHe9s~)v}fhDo@FXghE#jjJz}4G5aKb2ry!n%cu53$)?Jb|^sFK1S@>T< z^{tzzXZf#>n!Z(=yH-#4t+gtB>up%}Pl$IQ-i2WMy(e0WzSZ(RHjfw(_pJ{E(f2KY zC)*vc!k{LDN?_Z!6u9`=D?+5YS1?&BNH|+!+wNtIhEa`1mnI8(9K{mYb}s`AV|oBg z*rIzOf+eu+UIrL;^c1-0UNNxkUIw_adxaRMBJ8^tiJ{%=*jo28l_*K7-Rs}tllvb; z+gc*#g;)S$Q4#1~AHnJJJKHjG;X4|BzpBS`SG|YTX0J8UJ#0{=hb;%=mWNmYVnqn{ zu$4q>(ZgEa$EFtp;vTk&Ao?B#@MO~iR!G!jPzh{%m;x6)j1Z|F7EG2363&*`wuc#` zVN|2hrOARGN3jI9JvKVw&-DqUTvJ zVrUOLuGYg$B}$TN4_i}wa)Th&hu9cmFvK<@(8E@hw7G}1n8S)^-GOe;zIe`xp16w1 zjOz&oeoZ`n$w`o-a`}k&4I`xD@cgwwrJn)P?F>8FyM{w+hrqI^*3Wj5EBgG^^1i~3 zxSx&0&HDURTXT6}dMH$CGN=T${mcNvtcY0_n6L#=V^2$9+sO6i4e2sTP^Qn^N0a)-}D+YFe z728AGlos2*Wq^4@%TQFB-1aRtp6y!(7$)=}6tuXs*Y_;~>jZXUt#6r1(tYa;soI?l zaX!R_5Z{BiSOogk$&yz4mQGu<*SG9LSJAi3FZ*;1O!uyhD!uDU*me~Jd)H5){S4yg zB3jH`E$?H?ive-(x>^u@?*e$TfdDJ5++VNs*S zrM-gQ1Z;bk0dDMF!Q!%I1U|;+r`y0fQB3ksZmiMul#DKVuJuQgE3vI}|SI6X&&=dgJPndh*~c%FL zp)}*WTSO{zOF}FMv62X|ZGK6GZ7cX=NM<{z_5ZPQK9~ur*%RPvV-yFiw5#QYjV+X51=P;QK%h=WCdX}d}) zOe=oR73Fg~*hnNgj%{CI*F?GGj)XW0VixR5lxDQ0 zvY1^*qsCdV3t-Y@apfhPWtYMgBL#0vu`8G-u)&=Y(Xz`J2m_LI2yg_m?4rMxT?P&n z_W;hai*P!QZC7KLfv&Ubc&W@yg*Xl3Oc7w$6iJ0$#eYBsyEuTgWLB{lb7SR&FMmK~ zJ&acJ^M+F<9C_Fghv=K{Zwqu7t9B~bq`!lO8*9IvaRF?>w^neKCnW{8WD9<{>LL_T zwxCcHJ-wYb`(Kpd?PN!`wLKM=L}FKTKa>g(m+zpWncegIwZq6=8qSXRO7|)Pu|7Hz zJeSUNArkFOJIC&$c(ePsO7{gUcK;_pRQFLR5wA_j$Xzzg^Y+8j`yI zfOr4y*!|sR;r_mb`x~X*&tmscyxDzRrTYRFyZ>bAJ_@DWXTG}6+tvN;tM1p3)cxnY z`%kO;3;4c2Vu#`3KcYt8Om?3A`xX9wBkli)*nbpn_8(X2zktR5zft;+LMi{5um1CP z^?yj!{~D6||DN~%E%m<``^5j2?BBm|f8(_K9kKf;-t0cE(tQDo-TzPNJ_@DWXTG}6 z+tvM{RrhO1>i%47>)7{Sb-!%>9a=a4xWfNU(*BQ({YUX;|8bT63s~&`{9>LT`%x(6 zKl9ap-md=dQ1!otr2a4F{a+9jm1F;i9d>Bh{09{7Z<=<0m)Lz2Z+0J7>Arx)?ypd~ zk3uQ;nXm5ic6EPP)%_Zhy5HZszmmFNH2=_1v*`Wf3;#Du`#&o7AH|#f$5r|-V6p$} zl>Vbo%75mo|GZuO-?8d{4N3j~hWCFx^}p!-;(yELKd^9r^R)ZB#_pqdv-`M8_XR9= zf9ukH6iT_ze086_4v3e*ugA zA6fd3LMi{5um1CP^?&E8|1~7_e|PWyDD}T={?@(!;KKba((dmTyN}|{?&B)m7qHm< zai#kxlyaZ>>OOB*_lH;AuOX@XhkEzN$L{a2eZ!FjKlL9{xW8rE{V}ooDBkQouF`!0 zi`}18x{pFB_nEKm^LBN=z3P4qN!>rryFWR0f8?;8T6X?J3-`Aw-2cw6Kbnhw@$=8_ zaU5`8a~yD$aS+rv4yToIK%rC|n6Gi*?HY%y8iyK^#^G!qhcn_h3>_8z-EDFOcvumK zt&2Fs?(Y%1kK)bl<0{=3u-N?zOZQPIVbo%75mo|GZuO@2L7;LsI{L;r+i-{h!Z2 z0Ss$B1x_fu-!|?2*w}j%Z}uKn>Air(-d|sOk3uQ$nXlgScJ+Q_)%zNfdVjO`{>Iq* zSuFou;r(`L@ArzmNAYIwah2W+SnU0uOYc!Ai@Ie|EFsH zw`Ttlh5tj+{_hj}kK)b#<0}0Zu-O0COaD-~ST;{TXg zEC44K{tr$2zi;e6iZ}a@tMp&MV*meL`j0{>|Cz7;^LF)rbk+YFlKTIp_y4o1|Fd}h zIkND7hqV9u#r~ssv;VkC{{<}ef4+6ZX?^vHLMi{5um1CP^?$dj|1~7_e^IFbuRa$* zMdjn~@R6;%|51he!_w~WAG?p@&F)^}mLs{txy3 zZ&UTZb?@))Ex+HWKbpRCIsoJ20E`g)<^bR-10Y~=07k+t?fxi~3IOvp0K8oTuxT{_ zH6#td?t+T$KMLq&_lJAQuQvJzz{y4X4^O*)VC+7MH@lCkbYH+?_s5m)qfp9y=BxX> zUESZa>V6GL-9Oa3KVIE0_%H6a?*CH?|J&349~AqK;?4f!D*YF**#AkT|0tC5pZV%P zZ&&}vR{gIbssG1$|0mb{AK9|~k171m(*7SD`;X$y{^KhB7qHm>(@OtQDCIx%)qmcu z{_j=wzlNm#pY8oWL;Wva|BN2d`u#^r3}hDCIu$ z)qUQs?(bW5zlNml-|XGLQQZ&CKYA9;e|+KpE@}TeWB*aS*?(N6{{j~K|L4+w6iWHe zeD$BVtN;5|{jVXZ|Mz?U|Em6n<{!~|?R!Gu{;0J36JqyKyxDzRrTYRFyZ^V+eH2Q$ z&wO>Cx2ya6SKY56sr%1*_n)e`KiayF|4j05wfcvUv5@Ao`fL+1UTg1Dt^IKn)-{X* zY3;B0+W&^jGrK{)Dfr(-l49!VUHd`oxlv+<{6{BMx_b@XWriNf!A~4zo61of`OkDH z6oj70`A}*qPUK`$JD2WUR({__ezZsob??eG_a4Oiw5RfuWA8|&o*Rq{;>?uPST>lP zsCFNt@Bnu!92!f!sLebUtCZ6|q0)fG_=#yH77zZS=0+gG2pX&pK*c$(63G2sjY6fZ zvDEJV0gH#YV@2Ik@WAJ&UW6QV460f`yDPSy;!d4{^|NNRvGSRfU+qdz#6!{-K{B!8vrp7 zVl9Y45NkuM1F;f?gVpoXK5W7K)f!G~l4~RV> z#zO1`fkQ+NM`;`mlsKHKa5!7wu)*%I&Frw(>agSJu$e#mB2#|Q}X*u^9zaH zBA&@N;l`QWOCB-d$V2>SHKpIkVSA1kK5|$wY)!}yH2*46IqV0m&aUo0MQO5mLpe8h z127kNHzMJ=xy;3b)Ur~!*t#5TsEkkwKTw7MQ;NNJ!^-fq0#L&61h7P38>le-eRToI zj!g>0uFpE_J>s%H^TdP_8y` zl6NqQVBQP=m9}D-Oq`h2y64?(%;@1GcbY+%vtvVPqkLm*f^<>-0~Y}ehCWyr>VHKW zXl|&KsC~U+2yB75p$G=I4ymlP8NU*_Q(^O1;zZqEF^C{QsHVgyMxP1>YHPyK3i_8A zI_KwKj~mj6UymDM&aY9jnD$2s1Kn;SFklP^apl`6w&!E|A2)pD@G<>-2P;R7@v{C$ z&yC2beOmMNnT>r~pPJ_VnwMYmQie0~G8*0sCh1f2-ky+KDj~x<)DqLIL-9zpm1SY) zmNHInrC$`u)|BB{>DT*f`h875L%+`L^pP6mQR0nP;&l#@VQ>*y{Tw!V{6vg^x+uC# zcyxIdub2AxQdlwt-LIms7RlC@HPgaaGZD-zmyVRWid9IqpMMtei;%O)*!eXAnX#vL+mIJENUNs420ry2_n80 zZyhFm-POMCYDHJWf>znY$7v;Gu>v_(_~%X(ffdMPa8e#wOxG*aN*BbKzmBDK=i1UO z*70NhvSG}ZFH-XL$9TN{klxj~k?-?zv-bJZwC>K0rQ$`W95a6MG3fE3TWhCpvu`_4 zmFet#4mt8*TF3srT<2n)>&aur2L3I_j&aXE7w(?ND&w9_`}@0aoqck-O1C~A@zHJ# zjH>^Y!U!5_3qbZor?Ch% zHd<8L<6n2eHGSQAc%s=$8_6Yg*7r4e>GiZ!<(`FjNd#K|aa1n93sW???{fI9pw|6P zz4a|${GLMm8hbVAfM{XqfR7;24mdh)=C|dF`zORZB3d+aT#!LstviikV9+DOwRrmld>b z+`)?7LRw>IKdEkk-3DNOVWl&qYdnMbw>N%yY8i-u5Q8DwA@+ed0%97(Hz8(1Tm$h( zh({s*4Y9)fBDR4z7~(95UqL(s@d3mV3y9Baxw8?6O+uS0wvMScr$ z55$`g^Dii3H4&YI-4@XIgE$4^Qix6|)VY&8A1>XAjMpKq!N;X>|9lIH=nrul@*aSA zdmRyjk+%!P0T4SbEaEVTb0B^N@n49Q77?*6#CV7^Ag+YC8{#d9g%%aDKE#h9Zijdg zqW5AV)_~X(;!KDeAYOr3VsQ~eAdZH(6ykb_yCI%|_yA&=B}DuMV!I_p459`x2I3Hi zGazO{JPq+K#Fr2&;f}o^MlUVm7>FN0dI{e*A)bfmyOf9xA@+ut3h`rz zze2nZu^dothIk!f;blY&fY=6NEW{LuZ$n%SaTmlh5dVT$c3BZSLL3kAQ-~)a`YZxCi2Gh=o@Z@ePRGA&!UmA;c{Z&qGXK9eEJfK->ed`WhmJ zK^zKk7R1jW?typ*;%St72jUBecLCK4+7b|}Lu>-EBgCE%oe;~T_1A~k4&oAsUqjpu zk%PeJLR<{-Q;3@(?uU35B10b>4>1Lz3nIh4;~}O%bV1yUZtxPs!t&qN-7^rML#&K{ z=eZrk>i9SqB7@iu;wXr{aCI8QiTHQ{#HA3|LCnO3n;;&Bcpc(%h$RP#SQ}yp#9k1S zAxg`GX`dJiRf&XV^GuE@Bi}4FFU)KaAz><%R@TZd_1O;&Ci26*#bPOlP$=@I@v-z zu9Gdy13TFwJhGE5%0oNZVm!8!EzW~G*%CaulP$@^JK0h^zLPD@13cL>Ji?PL%R@Zb zay-V9$*!x|ovpy5JlTpo%#*Fe<2>2QJkXP^!XrIdKOXAIR^_pttUnL-WUKLLPqsP_ z_hf7Ecu%$_5BOvQc*G|g$U{EaT0G{H4dOwcY;7L($=2avpKM(o_sQ1dfuC%Bha*4P z20ZkWZRl|9C)>o~;7_(GkN#wv@$gT!IgkHjgB=b4Wn1tFP_`ux0cBfpOEBA-2Z6F} zcoZnxmWP3|?RXq0+nxu4vLQSYlnv#fplk;o3(AJ^U{JOrj|OEs@o-SKGmi&l!+Ag` zYv&Q6EaM@eY=pxxp{#=kg|d-6DwOTQ!$R389v8}X<$;5ryYbLaHipNBvfX)b zDBFWahq66+cqkjo<3rhAJV2D~%_BtFK0HK}?aO0C*?v4olMAs35Dyh)2lH4_b_fp^Wry-;QFa&)7iFD1UX)GX0i(?Eh*5Sp4;f`g@R(6H zkq3>kBlE0nn!aqy=9-fA5`OdXz5NZLH(%9cM}JLt5aMgX|Nn*Xy+LxkT5SJ2#_)C~ z9K%(^SxeOh1`Ve!HE&SRaQ;&Ba0pYib%TcEn3{((nW}9NG@Q=Vyp4i}bDElm!H9OYCE=Q&lwp-$CsvQsr2?^F$EJXOO%Pt|bRQ#Bmah*&~T1a z^Kh6{HB2a~;aI6^I9sY34x6a9SI}_ORP%7&R5ctrRShRkRm1U9)o=z?H5^1$4X066 z!;w_g4hk9$r)u6IK|3^PIIF5xF%zkVQ>&`EpyB+g=HU>lY7>KowG~E-i6xk)LOCV{MmZlcmHx zy0$}-XY(L`dN!rAD?297<_&|#314vVK%#>OH4YxsICxOw;6aUp2Q>~J)Hrxh~J)Hrxh~J)Hrxh~J)Hrxh~J)Hrxh~J)HrxhtONIT>rRW2a>EYgxGu50FUmYyr*tT=S6R*@Bw)Kg~mu zXA5cGe>D$Do-M3-pJ^VFJX=Kb{-b$F@@!Gf`?uyH$+N{Y?^De~l4px+-Y1%eB+r)6 zynksPk~~{d^FG!*Bzd-!=6$4jNb+oH$#YN4C(a+|{)^-UxAOdwoXFo%&W?8v$P1oq zs(S^=v^-k|<+n$PliguRPIUv;l%&gTiR4Vzf#fVV4$0YWj6Aew=eZ-1oa^pD((Rr= za=v>D$v546i2Ma^8gBR&|D#EEin|=2zU{6@a$%k=i^m|;cF~kVVuH`sJj~t^)qd~pSz#Si}vgn?p-8T zyVvFYdG>3!w7eeAe&ddpmwnmwt{ce>Zlt{C%WiZBA^Dxlk^J7Bh2*BRLEVV;AGE=4 z*8X#gdjXAkYt`8PYV0r7SR{G2n&w@tc}VhXb!oQeVBGCfGWoCP1KqmvGCup`Y)3H9 z*2LYj-{=G8?6!fp?KXLzpWW@=Mskm9msj%Hz3wC=_vP7Ia|S#}0^(*bu8jE}#^!k~*u z%gS5MKAXYx7A58&8k^5q5C_lMZClLQZCm0tFubF!t-_a<4A~0z?Yx%U*IVvGI(J*1 zZ7sRdB9Cf|Ajz|BH1843Ly~9PYTm<|ha}Io)4Yc?4@sVFuXzt@9+Es8qInN!9+Es8 zs(JTo9+EuULG$j@JS2HGO!My5JS2IxqvqYCc}VhXC(XNC^N{4(&YJgE%|nuB!!_?O znujFM+BNUbnujFMGR?b7^N{4(2+jMG<{`jMBkS9@ltl@7uNoFgs$nuy4a+6fuwqgTizd}Ds;Y)@ zTs4fGs$o&A8rH?CVQH)yR>!Jgfvg&OooYjZh6fkT!`h2lo*fW;*dq8qjXqGL57g)b zHTpn}K2W0%)aU~>`aq37P@@mj=mRzSK#e|7qYu>R12y_UjXprjv+==)!NCV=^nn_E zphh34(Fbbuff{|FMjxor2Ws?z8hxNfAE?m>YV?5`eV|4ksL=;%^Z{C)9T`aq37P@@mj=mRzSK#e|7qYu>R12y_UjXqGL57g)bHTpn}K0wQ}gMts6 z1s|x<2Ws?z8hxNfAE?m>YV?5`eV|4ksL=;%^nn_Ephh34(Fbbuff{|FMjxor2WWYA zaPVQ7-~%=KK#jIhqiuQ>y^I?1sS%$V@uB6}Apw8sfKQG1)QC@w_<9xi)QC@w_|%9G zEzb@O_)7(RYQ(2Td}_qktH7s5d}_p}Mto>_c38k)GT>7qJ~iS~Bfee*J~iS~BR)0a zL(8+yfWJh*r$&5g#HU7ly$XD4#HU7lYQ%?@XA=Vc;sKu;@u?A?8u9fi@Tn1>8u6(S zA6lNdfWKJ4r$&5g#HU7ly$XD4#HU7lYQ%?@XNL#;MFT!H;!`6&HR9`4;8P<$HR4kv zKD0bL0{HA{%LAW1jT(CzHTE=W>}k~4)2Ly|-3~3!CI+nK0v0u5Q6m;LVo@U&HDW=_ zvm*o6vH^=4v8WM?8nLJmiyE<@<=Iid;+Dbdz`}XkXHcA=kNe6&yL2hfNvVf z*OKPo0e*5Za`o)(w!X5+vngRPdRPY|Bsv&T<6uOMgAp|jM$`tuEovN$sBti&#=(dh z2P0}6jHq!iqQ=398V4h49E_-OFrvo6h#ChYY8;HHaWJCB!H60MBWfIssBti&#=(dh z2P0}6jHq!iqQ=398V4h49E_-OFrvo6h#ChYY8;HHaWJCB!H60MBWfIssBti&#=(dh z2P0}6jHq!iqQ=398V4h49E_-OFrvo6h#ChYY8;HHaWJCB!H60MBWfIssBti&#=(dh z2P0}6jHq!iqQ=398V4h49E_-OFrvo6h#ChYY8;HHaWJCB!H60MBWfIssBti&#=(dh z2P0}6jHq!iqQ=398V4h49E_-OFrvo6h#ChYY8;HHaWJCB!H60MBWfIssBti&#=(dh z2P0}6jHq!iqQ=398V4h49E_-OFrvo6h#ChYY8;HHaWJCB!3dfRMohZ24|6a=MxGsm zVPQE8Mhmt5fB&TFR`MdGW0AJ6Ppp1@Vzqu^H4ih~@p!A&-i}0PIBJ~XsBwm)#u<(p zXEEfq6J2h0fe4ASx zRjyd6GS5zvG7{#)v7YE+l4qw&YyjG^lEmf=E${~LyV{v_cC6gid1A*ZZJj4|^lR&! z+OcX|=d_OgZJj4~tk%|fO2_JLou_uJ(bjod$C_=Or*{l!>pY`lpu9Snu28Qi6cV?N zya?)8tIh3(bdWqbAzi!8?T&PvHn#`Tb=%yYNY`s~W09`k=JrCmL7Uqf>4t4?AEX<# zxqXp-L+a{+?~Kex8W|>C?MU+M%y9KXy~?Dk9Z8;@6|R1uSDAFRBgwO~!`1ioDwD2u zBzbmDxcZ)6WzyA-B+t&pRlGQxy~n)s=Ipj^xvjIaeHsioQInH2nX1V&O-|P26irUm z6)CO z$#hM+G&xg~votwdlXIBl*|%Ub8T(JvKfRqv7n3~ucDVYMUS-nNjwH`63|HUOt4zAu zk>uG$(x1=6$TL{_^9=cL4L)okAHE|W9>s?(<->R7!^ilrm3;V~d{|nJymf3XAHI*c zOmF{(4iZe}bPV``LOr8UNWQu;;D?CH?Dw`G!NfB=i_gA61@s7&#u&~|JAEV^eVJGyGnY+ z!FXKVUV6n(<-^LDRfoujpUH>w@nNWZ__=)e0v~pe55JHP_sN0Dj$!iQYWc9JZ0L9F zC?9?)ANI$Go#ex>q+(*X_kpL=HR)oKXTQePH{`r!$IfkTM>*5jF}z$wOp()b9qn!I zdq}f6dHwhsz%K*X5p6C<+EK0;_LEb29V6S^DM&}n3H&v16vpi;j$SJt`uCR)qvgYO zxFRjrv73DOt$a8SSH{SP>q9)|(|9oHYDbc1H-xKw^(vFDb|iUrBd#K{?Y;CWlP-SB zv)_fQZF-eSS38nC`+c}NmtJMk)s7_3ZVFew1Vg5`GwEWIXMey|@S}ZRy_ZQBKjqoY z;p#kkl}T4Sl03U5T%B95GU;kZl4rMutG)Fqldg6od3Kvz6YYg3)^bR;V-LCWkGK<`+JB;VGU?){Ji9Yoy+W@t>1t;( zr{mC{0_f!m%A~6u$yYZb-K9ae3RQNsBXMsd2zyEp{tS!osr|=l5tA|$WKP5Im_nVRP)NSIFg&hxovd{sasR+f785N z^N{4(lbW}P<{`_wsacjT99I`$K)za$^} z;iba<^5JFqu&Mm|OvgC+@QQrcMUG~793UTFRh%J;gCx&h)4c694@sWAu6f&O9+EtJ zL-V%PJS2Jcrsi#X`Izt?Og0 z3rU{+NBr6kejO}+eWnFo*8)uD)UW?4)XNHm@5WbupP!v%eImuguVnx!Tmy=hadsb7-l2fk6oO&4EI=L)xZv^unii@aa(b)Eo8a zo8H6b;9*I=+z{^XoRjA6v1Wo;GcP_Jg-=dC^&vLSvb8@iBb%OYgOi@?MzHDa_o7r6lU&ZnXy!edi6ocJPZ``_z&Lf34DO3@JmDAYj&5^z z;0fiJHunnByv_B+L&_u>&X6Aa|FCx*;B^$&8p#!~=_Pd2g6R+-^x`6HOJ3W^HZ??X z5nL}W!uHZ6xu6arKu~C*goG4QNFj|RKoZhl@)FVusX!_uqyXu8|No!acJGz2^>E@% zM*8-g+1Z&hvvbawot^o2?rAZ$-lGm|A?*GT@%(~3(=sbCOnY5wPl zINOVbZTw0JmVt+tZ=Yf2n=s6L`wlbTe#6YS|1k3%FwA@h4m01&hner7Vdk4S%zOtA zGv6V@%y;N8^Bp$Ke1{J+-x0&icjPeh9W~5+M-MaKD~6fxm|^BSmid5~SjShytBkc8 zYctkvY>lzC#?~2IZ|rJg9mYD1bs6h6)?=*KSf5xF9Op`3W2K9=){C_nYd5yW*ji)j zjIB3zwXqIkoyNM1bsOt3)@!U!EDB!fN?&iKi?!B^wHa$Sw#L|6W9y8qH+HqL4r86h zx{P%j>oL}AtWPWoj(4T6wbI2}>&4oPwHsSwY^||%#?~9V+E|CNPGeohx{dW1>owLV z76m7$^xoi+;XiCogouy>mf#b3pq`YReG+FUCug6+*(u4{qc}S?Ir}uuPQ#fS)O>C@ zgObzJ{9hbS{xg^#L(i*ilt~t0tvjjImod`8pdsB!WWzW8d5MO7lhew4y?hY*k`xiT zZ)>oS&)a@2$4K8S8^~?+P$|5F%i7Nv=p8(EA z{?qdN8)}gKlH7X)i~`J9gX*8d9M4S7eu1-TY1MyKBU>M2a6aVbPa9s*SP|1%i(j%u zoOeV6jy0g*-7FM@+*XYQp2l|F~f} zXR-z_V-21|4FaBB#{U41_;!o1?#X$4Q_T?Jv zUbSHz`QES}@_lN<>&W-4jj$w}RC>a{ztXQ!ZRT)}U}9~!n*89}@K*AJYQv9{AA(9R zNGpA4d8PHZ-JqScuyB~%`;q?-F^eQ-WBn|`(%bOttXcW4=km$%wc#V=C!kR-QuJ?l zdU9>JC%1h%1<@BHI-J3?Q(@aBo?Xea({Q$gXENB+aTX?LXW;A-oVn$VA7WHvD<2F+ zSdB<7{UEu-P=rMUUeXl(HxPj-dNP}$S4dOz3TcX7Ax+UMq$zrZG)1qFrsx&Y6um;4 zqE|>$^a^Q;ULj4$^a^Q;ULj4$^a^Q;ULj4(`UQp5 zFDRsbK_T@E3aMXENd1CB>K7DJzo3x%1%=cvD5QQtA@vIisb5e?{enX37Zg&zppg0n zh14%7q<%pm^$QBAUrf( z`UQp5FDRsbK_T@E3aMXENd1CB>K7DJzo3x%1%=cvD5QQtA@vIisb5e?{enX37Zg&z zppg0nh14%7q<%pm^$QBAUrf(`UQp5FDRsbK_T@E3aMXENd1CB>K7DJzo3x%1%=cvD5QQtA@vIisb5e?{enX3 z7Zg&zppg0nh14%7q<%pm^$QBAUrf(`UQp5FEE6DL99#6h7yd3undQO0he*Yr8}|+i*6QqwndUf!KE(p_ZCSO z1uI?T&n!}GxDb_9PSz79i)_vZj-HdvVLl^$TnS*UF`~c)(Rtwf({q? zq(zDi_ij3!te==HvQ2k4U9Pa1Rv1}?HD>dhITlG4VZqrVXIms$gw^rJG1hCWPb|U; zxZS{S>;}jpEQDL+5{o2@upVxaueV4tyH$pw;6``(Pgc0taINZfPSy)1i)_l2HDTU?3nwi3l! z>&0H|9+jxW(sml_GS+RZ$5^kiKCvjc)s=XQl_=I)FZN>hs6-u>w$oUbv2J5M#(ItQ ziABL}uEcj)iDIqwVr|CSjaA)nz%F$d>onG7tlL#aipd+KjautA4La*kLI;jddC8Hr8XT z*I1ue6x`uTeZ7?`)><#tW~|*<^?Oyq4olH#tjk!pu^wZ+#`?sf;7(WSJFHZ()_Sov zW9`P)7+Y&>oiW=^!onTK@XaGd;hRUYZeu;hdX4poMZsOJ>;WrVthHXO%~-p!HOAH& zTW8GmOG@5hQJu!RjCC99G1hCWPb>=Vc4gmZWs9}ei?tbRH@3#uT4U>sx%*b~4vXqE zhOap7zQua%tk+ndSQNa`m3@WA46{yu+e8jddC8Hr8XT*I1ue6uilm{W>dK zthHXO%~-p!HOAH&TW8GOw~}{QRHv~nW8KDjjP)Ap6N`d3yRzSAWs9}ei?tbRH@3#u zT4U>sx%*b~4vXqE)@7{QSdXz@V|`*#aIY)-Mk`yawO*{vSi7+`#?~5JXUyHVl6P2C zr?D<$-Nt&1^&0CFi-NbfvfpZDi?!B^z1Xu!i8?H8r!jnrOF7-fdW`iN>l2HDx4IH< zuoA^u>&0H|*`!1rmbTMam$7bRJ;r*C^@&Bn+gyonu@c2v>&0H|9+jxW(smldcer+s zVm)@&YphQ!3f}HYyxvL_YpoZ1v3pda4olll2HDceoPowGzcz>&0HIRh6j2(smldH|o}^Vm)@&YphQ! z3f}2Tyv|A#YpoZ1v3pda4ollowLV76tEiCH7m1Vy*RJFLsYg)M05mjddC8Hr8XT*I1ue6x{Dhe3O+Z z)><$2V)v*-9hSD!7(V>Bdlc)jvtDC;Vo~rOSK_r+qF8IaSevnSW7S78O4wm3I*oN1 z>o(S7tk+ndSQNb1m3ohrD%M&r)@H2TSoKkx5_VXMPGeohx{dW1>owLV76tEfrCwvD zinZ2@wHa$SR{dU;u)|Vx8tXFFZLG&wudzO{D0sgs^^I1lSZlplo3VCd)$dgaJ1j+~ zu`XlX#(IqP8tW5_f(KlweO9ViYrR;Tv36r?jIA}c&X{|SDS3xQbsFn3)@`iESg)}@ zu_*X}EBkIMTdcKStj$=vu{Fll8e3<~J-?N_!=gHkbs6h6)?=*KSf5xFe9)EMYh{bI z){C_nYd5yW*ji)jjJf+(@(zpYG}dLT+gOjWUSoY?QSc#G_FY!CSZlplo3VCdYmBWm zw$7NlZzb=rs7_mAu2E zI*oN1>o(S7tk+ndSQLE3m3^m`E!J8u)@H2T*cxMNjjc20?pw(_EUMEOcJ#3O7VELI zUSoY?QShKEyW7eZYpoY+GuCcwjj^@H)){m6t>hgR)oHBDShukrW4*@u#G>G%uIxLk zY_ZmQu{LAv#?}~HYiyk{ci&3hVNspNx{P%j>oL}AtWPWoKIY2qva-cm>&0H|*`!1r zmbTMam$7bRJ;r*C^@&Bn$6bkUuoA^u>&0H|*`!1rmbTMam$7bRJ;r*C^@&BnL$1V5 zD^aYqUhKv0QHeS%ZKts=W8KDjjP)Ap6N`d}U5U3_iDIqwVlQ@&O4MO#JB@W2>o(S7 ztk+ndSOm;8=SxfVtyZE~tDUtOYd5yW*ji)jjIB3zwXqIkoyNM1bsOt3)@!U!ECSft zW^13Z*;=wF_@s+`+9JuK;8QO0*A__@1&_MOUs)ts6nxr6{?a1JqTn+w^1Bu(Hr#pN z&pKJ(FMdr z#v;k0;7cy@a~4S!1z&cNpR`D_DEMC&`C*GBi-NDX$XhLvEDFBrB3D_Y*l_RUF(+%K z$s*fyD)^c!?6X!FSrmNTMSj8}$)eyJF0#cU#fE!}-*mD*ZL-Ls;9D;8QHvytf^WOX z_uHeqB@@VJZoltq$7!4F*I zBNj;(;X4BB3m>vbvIrj)Smehnk}L{->>?kuNU{i@9kAAi5@$cDHoTU6a_vjFwv@k4bP?%OMfQLe#>&g+wi$YFs*jD+pXfu4@z-({|yE%NBB}?c=>*> zd~aZ_>xWrk#hnep$ z%Eui;>#yeM6W^_ob%{m6Uv>Fz-c;l8Z|ZNutlZy+neQLWhg-SbT#qaY{^=rbvq-Wi z*rL`_F11Lp;W}xJll2OdMHU6MF7jxLB#ZF1j+J_pMUq9qNEdmjMT!l#Dx;jNLroUh zrt4-$yTT?~VPsJ-#zh`!k!08($weMvk!08z$weM+kz^4*5@NTaEmz;pcr04kWwI#P zT9>u2>OFQ@4D)|H8*ZzOOQpWiq>5pt4QHFK{l=%o-fhK-VWJCX*vdqgwSDRxc3BMb z13Vk6RIeS=9A!y7ryHr>5$hhoD|@{_Ci=rXrwC7|0L)&P@CU=hB)LXv=AUY(63 zJMwG|&(6WwPA)^cWgrXp=EZY|-#P85f*xyB-XOU!4aDa=PW|3mU zZS@14tTRm(*`{mtm%GBISYc!lzHYN-YqUtR2%o@N#z3 zNfrf%y2$-3k}L`ibCDA)k}L`icaeKrBv}+3;Uf36NU|t6(naoTkz`SDl#ATQBFUoQ zXcxJcMUq9qD_rCr7D*Nb$GFJnZ7d=-TumP9WIboH$TpoOk8_3n#tIV~ZpB~eWIbcD z$TnTYk7r@FBm+l10HuF7lfeNfzNtMm!PpBS076iEv7C z_9~p6%CmZY%XlHqPUG2CJbN|HPRAJ^Hs3H|WKnR2i~PDpl3}w3<~xjUTTRF}nP>ex zdkxN}@a#gKEx_4So_&mG3vpJ*GkrU`2xn&|XNz$*4QJR-A0OA(575$qd@_S4i-PI8 z{Bd4h!>W{wU1jY5w}VL(%#ie_D1EI-7rV;N{(pOfM0kHoZtW(!H8FsjbZZT|ybZpn z-xRktQ__#8^i6PU0i>Homq3TJlCuzJvsJP*`dYhfF(5T`+jDgJ-c5AdXG!{(H_>f3 zrnh#T-I^Ex8oIT)y8Qemy0v+dz7NKTn?j@WDIMO|%lpE5u5RdT6?s1gk}b9Q9~8r0 z1G@M_UR+sm@myWp;Q5kd3y1TNFT8;tX0L`RozJsR@GQdF1w8vL&o0B+t9Z5-JF&}g zb|KE(h5}cVf1f_Y))5hUz$D{U8&P85jk!1K> z&P85fkz`S@%0;$Wq}XsrF{_=dAKDX^44<{R$RAiF89r-sk&jy>89r-sk>9sSGJMwN zBEM&mWcaMjMSjO3$?#d5i~O!dlHs#97x`_AB#WZ{kr%I6y#l}NHL+m}w&*quHKoHE zYD=dyj0hhXi@k70g~wsv<%ZGWS9vxjd~^cXE5e`d4|YuWCG1__aBS3oNhW*XUCYEP z!lh&_8zW%^B#gu{3dd+1V{nYcu_cbJaBPiZ9FFlgw!yJ2j_q)4k7EZMJL1?0$Idu* zLErI1Y?0B>x~gGU+*No2evP4FeYgXDnXuvNa3`{k@W*7G;iY7KQNzoSaW@>h6a1YdXg+C*EcX%O2EDi4sr|%B-zOY30{_q;i zRW>{jHS7mL`{OtO_xL}Ugl%|uRl|W4ygRm)Y4}7qh3r$|*<_DK4KL@VFYx5k;p1eV z37;kVY}9ZNFHK=tpDVvK5d|E~%iH63JsQ3g9zyozsNoRAAIkVGS)0d_yE}|=_cPJg z!q1U?J!&`{X^&w1R&0WAhQA~GR(Leocf!eJ-wp33`(F4-vd6=_*eLH#q#nuK-{qwr zgXe}6i#^5Bz zMTo;@^-1K(jC`7rwKYk#Phn*JA&4AR6D}tojo)oK6)kldj?;0RfnySm$vCFqn2Mtg z$C)^$;h2tN29A0h4LD}v2yo28F&oDm9B1KZ#4#7gJRI|JoQ>lg9OvRV56Ag9F2M0B z92eqvHI616ufeeZ$3h&7a4g2rjN>947vm`5Sb`(OaS4v4IF{j9j$;Lm795xIF28&@ z?rzJP@M-d`aF;6)TbhZD-oqQmB5E9>Rw40fCVuuX#B5VDNCy`o*=0B`$8iOYD{)+f zqZLORj&>YtD5jA={IY9JcoGzYt#}h%tVM!#@Y&V8z6XA5Vm%_R#?gVJ6Gs=0ZX7*0 zdU5pOxP}r}j6sfLYr?mXABQSm%h>rmdu2`7O@2IL`;q=S9IxfoUo-ZEwDjw7;RYNx z;&>g7n{W)^cs-7raomFARvfqCxSayZtN#XExC6(XIPSu6H;y;rxCh6ZaJ(7Ey*S>2 z*7Od=bb0;P?`bFXQ-M9ACllRUD7u_!^F{z+s6Af z6t=D=`~vxU2;E6SJ9ydw*C+2p^v;OZpdjeNW_!EfOq*zH?`&*$ZT% z#tQgySA&J%b&#_+qe~YDH`RoH9Rb+`h}%bTZ^1{T;eI0#cQfK9DDF%6=rWwaxLXmo zuj2lIj}*fe#@&v%{TLVSUW3y&;CC1qeFviVSM(%C-&qqr%;>uieSo4%jJ~@je2UR; zMD&4*zMj$d)P&NnP({C)r|+%_?_+}K3Eg>l7AG@uTb>6dHS)M@P6`-Bl;Lcf1Rff)r8+5e;ConB3ira zYLi>-HLERO@q4(qa1zPMB&U#^N^%;AjSm6*(*QB}ECijd=&Lba1cEN?ApZiQ&tR5G zy2gPa@Mszt0)iHP8iPRq%)+az4bMHPO9iM|ch?+^Z+n(&k4-$iw%D%n4o>~YkDKRx;b7!GUl$tx;;7d zX!vdNXu$81vPo~{1LOBK;dpyC;O9n>Dw0#)kUTrkb>Y$+U0TP-%3slwxU6R> zwuu7&Q4{XWC-N4x@#r(|?;4e8+J3lLTN{r_-ByxIcn2eE!xMPtqiVyu$wy<4xp~U; zzsxi?%`_jeZu7?_7<^+LH|!xFhaBf9#|YMCo7!++@@)})F0-6Raz4ogB(DOQ82$z0 zYpe(+s=ZLLH}fWT0XRy-kJ|7bDt4lK#5aox5(;jTh~wGs z?pqt4N4{TecqjS(wc)PJazJf(BKd)};hV@`o{D%4MJ$kr`U7$4u-dSn{P5cFE96I{ zNf#>BC5%0)Hheev(Y4|4$zPGCTBKAQK4V2hk(hkX%V}6-g^e8%aCK8jy7Ok`9tik}i^Nk{*&?l0K4aNUkO6 zC%KN~wItV*+(2?8$?HgNA{iigJ;}`^w~*XQavRC*ByS+OgXB(l zGs(RqZy|Xr$=gWYPI4c~J4oJ1@-C8hliW}89+LNxypQDlBoB~$faHTDA0qiM$wx>Y zB>5=G$4EX-@({_xB#)4Mg5;AVpCWmb7?zKWzRydKll*|>ha^8D`7y~)Kq`j2 z*rA!RW#e6RuE_8t$x|dhBl$VWFGzk#@+*>GlRQoG49RaueoOK!$V82)v9=PwImB?a zC)v5osEuD8BJ`%n@&{#MuNpLDviwoeuVnPhOqS=E<$019NdBZ$9Igj*GMWFZ=r1w) ztW4&=Fw0*_{-y-%kAu0He1BK;-h9r?%jEk9v;32Uz@>nQBO#20C=!AQoiO4E$q@uc zkQ+g41gQ~l2E8q=jwjiMWLpvfGla(w{z9+|p)CT!BLs&K{6TOB!5h$r;sWsk!UqTs zp!I*P_iKG$>-k#0*LuCy=h1H>2N%4xz^w&sEnsWGS_{-#kk$gU7M!)ftOaE)AZx)` z3&iLPA%!ch=>9-#3A20*Zwr&wQ}0 z)9RiU^|W-S6*et|!E_**t5Sdnf^f}9OGPjXgt%@4BpnDBQJ`B8E=d6Ef>HS_E;6jZ zG(XoI!nHiR6lb=`uoq8PrH?xJ7q)XH7I~Vp$W(Sm8h% zXM8oos~KI*;A+NJGqjqKjrzB2Ub%9`%8R1qiv~(Z_K#_4()qwm1O3}AZoX(iv~+dz z;-*!r7p!hxg;)0eF}l3sl7Zf6;SpPG(BHa|udm$#!+reqk6X2J(Ge|6uRP+)1&bC9 z^pBm{IA_7iRn03=-ofi3e`5dEEh|?njux$6vSRtFfeZ1nvqk?_bCReu-N@w&mNgIb z*Umj?pnoLu4D^peHCL`4=o;uBy*ygB5Kh6KkcBp}}EMMGw153JXEGRWESj^@u?S{$j z-Aj8Y&4P5)k1f`>0$uxiPoBaum*7bt+l1RT+F1C*5O=)-3>d*zHJ*kgqI4?nsdN!i|n)azu`tG29Or@gBep*L5v_#!yQ?}G-1<{^xi5^Gg?0RP{Vn6<1hQmXj)+bzQ)^_HPOISlE2gij~a+ zr3?DU;B0yG6(~D38j3j|>dAmhbY~~9} zOJ+q2`Ckf4E1Dr{QU8clt5**6zbxA>6AnLZg4%BaT8~vfs%gRE#m$Q`QmySDv$7f8 z5KbCuASShA*h|{715m-(rpCIn=Fe!XL$@<`UgL~e(@Ya9sR@~Mn!age^QF-eHV#dC zZK8~OpbWVhdIOqDTdBRG{ae-kxS}?I^nClTXZxe!+H(!ZOl@fGOr~sW$C=uyWNLS< zt&*&@Z^*TFoEas?t=(I?j;-BMiu(7-wv$@gHPZywNTtis%%v+{q~(wh2~(ygW2 zDjNId()fvJHeD>;9v79i_$XNXU9QD%&o!QM-h#Xko6Q+Fdp74S)pFj7yt$ledsH}^ z^VaG)Z$sWpZAUoA%I3Unu$;G-_Dk<`By(c8nkYOg0ypdwE*E=C=^dr_ls-VkzY&U` zP@#C!ADnDL-C1t*&uE z%@UQX_3dS9{YmMkr6)>H4yIOjIhK`I>(888pR8=CpP+dqIL(<=Yn<~Z)pGt6c~PY_ zXI8Co&YxD#`2_N2a%R;U=X_$YoKHHnqRnEpZktzYECM&2RqHdQ-S^q(0N+Ja3Z!MJ>TUeP)HpkF1B@u#?=1OJ8wpS*T zRoDQTk_f?6K`K+74Rlr}lcjH{Oly35BuPi3)PZ%UmR=^DHOSd}v@qNSap4{$6G#pu zIRpeQ5N;0+FL-&@kTp(mk1HARgqd^r+=BUTyc>*cnlqaYQ0WDi(+hU0^nw*F<^(69 zYKap(2qy@)VPz8^I2LE&aU`!KIiBPMkP06-29f3igJqGum6`Q{j7*lkp)w`W_4d+{`9M$J2QtDUx{Ja?YlcPfe< zId#%Jynu{ptV1N<^hP$;O+wm{b?5T!d+e0iv*yj1H6Is7&A}TWU&v?ALUkJJ=FM+J zVAPyR^XD?#oY@UvBWBM>lF>8fO`17l3SuYEp2e$~S~q3pBv!F@8Y@0+_B=M*wApj#Pv*n; zOk_Bdw>RpX=`+|;BWKR$os5Ri8E2k{dpUFdtSR$mu=*pEXXF9R%NHZvth#d{AIaG& zBhPVdHESwGj;@Vt`N9)g;#RsXg2y$d(95t%DTKwbt#+L(!5N1 zU9&TzGMyxBI=l}V*-2(+xw4hv^o;5@(7%-?P?t3?U%krak#>$B0O_qaSz68vgdc+F zfGp6_zCet@<9K$TjD?`MrL_Z22dRPmk5D*h2BWAC2MYPgD)#)ly;YDR7+R1q_c znOZ%HOG&L3vsSYjcF@>NGla@oJ{cE&GOiN^Bk^_(J7ubaTM9qR6!x%Tnrsi@a!Yb) zza+PMmWCZU99)7N;Uy$XNtTf;Cs{$#LUJj|N|IG1t4Sh~%SbLKxq{?MlB-BsN!m!- zN!F08C0R$Zp5$th4w6oiE|PAN9+F;?K9Xxlt|jRwxsK$uB!tU^H;~*&@;Z{6NCrq= zPjWNKEhM*++(vRc2~9Tk#8Gg>ybH=v)nwsaOnncCJhEzI%P+$(GYuXHp9hCeZX>PJ z8LhvC_kD+RH77<37nDjezMPuO@MLzdKH6qtc$ud_7#m*BV=}G*kJ&OgwfXMtVTG7P zOJY~0K^rUm6on;5IX`w4Gsqv~T7^OWh_^^5;Z>Q`_GJe7BvX_*cBWpO^Sf-p%BIE5 zizh{cFnx%Dt_tLcb zM=x2tbjhmKco?j{vZWcTI0MqgKB(nZX)Udi|3rqQlD%;y(Y?{RWT<~bitz6s6{E|) zBGN{e`Ggz;HSCkAI;LjA8o;ZZms42Ie4HSahucp|Oa#S7logaUqWF%fnpI1dU%a$= z$zoH@fuM`<$3VK`Mg2?GGlzb19560QZq1D%!^Ukd}J6E zU{J(ySTG-BHVjnGW_UKoI&)@YxHe+$bjHt}j`4`i>&|2e9G28hV`v&fSbM>63Q0y_ z&L`boQ)W-)KyTjkIt=P+ zIUc00Ou_MZZtgsei$^z3Zk#j)1GvZrNnwQz+2i71G%g<(2hgtYxQK0;PYj#jZQ%@%?}v(Bu}S%nSJYI)i%wugVJ!DDwjT@Q(8W zrm5xyinq}Pb2~2>O-l}sK%VeOlA}nDCV2%(-pIn^5Z4UYA@dy1>nD($NJ4N#crwW; zB&U*`MshmI86=ZPCX-AdnMzVeawf?%lIbKfNa{%%Nb+^~FE;dWCYvK5nME?2WDdz$ zB#k8eZKsew>y#LGg=v^^FJc-P_p8BS+&g8BJ9%Zqh$uF3ybJ5GkTWfNU|77F79SWg zEg;-C!m^T)Tl|1YO0(Kt;%d8#)piGa($Nv)dSmLU;Y@hr*sM1ug9JR8;-{zVJo5n6 zsNv;!TJ6w+Cntv#DYNJ36(}@$YW*U9j=Ir9gp1}nm#L2VSFCU~E6i24FrQEl{g-FF zm=FD{nE0S@99b!(yj*KLS&{^S@>eEGWMu~sCX0)umOeWXl@%m0kd_k^&pu@;wYyb{ zg$`P&v`RLe)4&_ltP_ww8*2?|*}4g94Os)%FHk0 zP-xQ})a7m{J519c4UNOGTry0{HcmX=%MR0$@n~uM!C1V#h|{3kwKQ>*iqZ5%P0h<% zRs(v%@zAZPa$;GR$BRK*2#EqiO0faqExb7lg!nX}NH>!?5ucZ^VF&B6oADtD01F&; zEWpPXNNO`o$?8phQTPQtY>Zx9+Hyz zkO*{<w^rG7w(rh6u;)>4YsQxV8`WOt86`N+4BXJcBbqBv`* zCn1I{^&?gx>K`Aslr_VUO_i)nt49f|H&#c!v7SLOgEtnhs<9v}-&nt8^{la&s9Iww z-Wp5Ut+7T8tFbDIv&MQJMYFMf$0|hq+s2KRSppv1vc}Gjlc~Kfsoq|l`S$uJN*cVq zcvbBMVfpr|8IiOX6IE+3#anwRyS3NoVYOF9an@dAQ46-$2-YU*-!5*iq$P&5FT8RA zBc{e$TD`Hl@{P4UiW$7IcvX!BVfn_|k<~NfVxnq|rFd&BWw*u}Gpxp{D9##dcNERW z;sMhlsZ>jxJ#^5c*t7<6-%eT~ltedqI6IE*|#al}$yS3EV zVYO66an@3Yp=h?$L99Zy&)R^dI&;RkbvC~gEU(^FJ^7|O4uuTfRJ^LDg0Os39nZ>H zQ!!Dsrc%5$m9kq?Z8@x_swmEy>NFJ1raF;T$TXET!_ZcBCRV)Wng6Y*-c-H$rkaLA z25%}}RZ~G&zNu!ga@JH#RIRBLZ%w7_)>K;!tEnoAv!Yb#~9w%U4FZBAbqHDwkM(YZFFb~NC(#>5*=R636h@ggoK|h| zs`oHz5H4|*evsA4zcx&oJAt>5yf$D8fEDKid#>UYZ+3dGbFZqL$+uV4NAyN@3=2J$ zBw1O?&dWo-P4K%)qMBow_y&>|SJx3ZL0$KbmrOW_#X9x`_g>0MbKL^gsdc9jx!0ZS zipNB^{vFR@q-!1;MD~wtzM^HNuxAYu@SZR}UH@oWfcI9uYOY0{lGlYB;zVTJ%Dz!*N$`|nR zm5Pk#N?4mgrAj75=kG0%8o$qHqz+JVCCitcILX#Ettmt&EtW{lf^9Ol&pHfU(X^V7 z%hpOnX0bN0$lQCQtbp&03TTGbg*D0bPn(F~#io{}(W)j(PwRLi%1Nwa%9dL6-V%M7 zUUX?8cOroVR+z*cyd%wUC27LD;%fghV+eGgH#6};;ca9kCCfF2lcmyd_nKnP$&v+3 z#P^=8lb?ZxPX4uP^7$EPx59$8mZ~$*_yE_d>E8mK$g4>^aD?xH&sA5`6JAYdntubr zW#iQJq*qfB)bx~BQxVkkGq0v1sOjfkO+`@CFT9$Hpr&7XH5EZkzw&A-f|`Eq)l>vE zJ?+(01T{V5)l>vE{l=@Q2x|JRS5pzx^sHA?5!CcMucjiX>GxhuMNrcpyqb!jrayW$ z6+lh=hA~A}=u;WuFb0KH2sHt0MGjUGLts)tF+_tHi=ky1ibGo%H4yG$AS#d00QzjR zUsL7|CaKP4JhY3<22{l1hCABIr!i~mJ1pBSN(F&~5Fr2B6D z5NMq61(Lsl5TW!v;&Y<@U2_N}{Yt~eK$N(xeHNlrLx|G0F+?d^n%O>S?8pp2DKLQ2 z*4gcoMj?Ur$*%w?jl>lLC?#pa(TEd32}R}sN?S8^0#IVQob5YdAw|K5v@w5Pm@Cn(Qp7IAwCSC zi2mwmtiO-4pcg_Qp=FiN-ljaS%gVIT5@TA%- zJZbTg%QEn!lTlowf+wNKJUr<%rcU5VOqa8K zCoH5Wcn}LL6XxMb9-idkNpK4fPg>31KO(t|T}md3aJG)a2nwg;0}+Clx|X9-dSPHFbPEQ0yO@T83S7%V4+ zCryQ=gcp)503kf-4Dbqg($pDeZwx%?~um!;=c3CJ#?4gql1&sSs-N@T5Yh$-|Qhp(YPcDukLmJgE?B^6;cW zsL8{V3ZW(sPb!3(JUpooYVz=;La52ZlM10G4^JwDnmjzI2x^MqNd~PDJW240EIcWT zs}QOJ;EFs*BMVQ;gER=j7!;(DLNpvaNr(@FC;4@-BA8DMPs*5&!E#b~(&a$ogg1~3 zfDoRviujz0-IHcd&BK!#hul5s6xdbv$K6I|;YlqkF3-S|?m=-0Jn2=0C+*rWJ_}E} ziT}md3aJG)a2nwg;0}+Clx|X9-dSPHFWp?p(c-0DuS9~oRVQIgi{iZB8yYX0xSft0Lmhd*2v7SRJ_kaa(pw4CDaR?znSD+kr!@4hyq#KxQyP`U zDXl6k&ES;2f#MRJ(#5uo(%39c=`mK00xNJzUu8{&Q%cf=UqhU5N+>dqQ~EYjCpaah z%UQk?7E%;Ehy|7j^Ef4sQ}Q?^xW!93G3b9TvKlvhbulNP{4ZK|vZR zM8m<8g!nLclApORg89Voq>T9(EGLC0{Qzj3@OhHIfDoSaUE*`f;Yo9+&&?TJZS{37(6LS6OKZh;7KSl4^P^PsS|h- z)8#DR2@5F-9>fC6gn4+9hbMV>65PVWll(SH>>l|Wv9{NuHk}iqQ~l=Yyl@7|B$CM_ zQ%I(g)RCM?;=Kut@KSn{hbI+bSQdDAQX$mjUvdheCJ#?4gql1&sSs-N@T5Yh$-|Qh zp(YPcDukLmJgE?B^6;cWsL8{V3ZW(sPb!3(JUpooYVz=;La52ZlM10G4^JwBnqqj8 zK`R7L5_}>HPs-vdgsK3zA`jBY!jtkK4K5}Q3erd+8V;T$#D~F?{FHDJ%qNB?Wz5H5 zIVn78JD_pGgGmktAv|du@i~?7q{a<`Cyfeb%$h%U!2QA-HbL4h7(-~%xR$16OO{8g zGPtCZP)@RK(zW*UZsW9VQf+DX3kN2K$Fo)^s8)O8ytL23>j&Dww+>&)DvpPpCrNr- zNhu;}L}~Z%RHi#fZbLaaOE(#k071N*$`BB#v|B?eBfM<{@|P@m*84JIB&4)oDn@=6 zoL-}w;d1;5Bqx&im3=fqFMX(w!^&~ft+(_~|lAB0g>ixaxDyh0N zCgWTsoC*sGr-RT(PNy|R{W~;Y(Xw*F;ipZI)#n{#=Z?BLqrbn1}Cq_?`zG!5Lplz>(iwjJ54l#Z?GZ0dPegSCNJ9 zyz9+#Km1x>z;Js{ki;vYrAbC27KL z#0i{)67%4sYneI$Cox^l?wzoZqToN;87D8ygOfZs$%B*Nj2@ii_ZCBc>=DQu0c=(n zG7nBF!l2CWEmjPup9d!u!kauesSs-N69$D)lLsdiLQNi=R0uVBa8e=ENTFPGY*8-8*3+MZtgci(PqP9-QRCNgkX8XY}AC4^Hynq|I@ID-TX8 z!l2B9lRP+y{pSpldXff`V@QrAxd&EUI=|Fbx+M&0(3g-bC0RzYoMZ(_3(2JcBZX)5mCw+qWoC1auL7L(E7nusq$ExFG~xtKLWy~B((jl$ z0VgqC&hDMCkfPu}EU-+N2Pb)Ok_RWjEj&2szit8eN&nleF#OGa0oa3+iZCei;3N-D z;_%Xglh6pa&|_~UxsBv@l9_CsfMgcQ{rJi8@I54F@wAa-F3CKQ=D>rK*k+skv&n;# z3ZW(sPAY_&{PbHP)a1cQg;0|RClx|X9-LGNHFQw&ZrXobK@f=^_@Nm*QlP!#}IA;% zzjhbFd}44?#(WHxlY*0802(LUZZybFAOt78PV(R+xP=ENd2kZD$CF4-COL)VRFcz3_*=2I>!LQG6VOu~>Qr8+BRP}AdlMSr zrSv8bPAbBn%!89WIEme#2PdHsJUHnx_F0#cTtRXr$yFq+ByA+^Bx^|4lB^?HPjWR$ zF}%rxlc=lB9z*8ANgJ!C*Vk@=EcgqrW7gM_Tu*WX$&DnhBe{uWfaIkd@_CdJTXeH` zD?eRY2sL?>QX$mjQA&kSlSe5PLQNi}R0uVBlu{woNrSVym(yS~>X&wqoP)hezK`EWZs!?7A zN@))3sV$U}G+`s+gi=C@d6d#QOr4;Vm@a4cPFP4$@E@(G=Y@HcQjr!#e(E=UDZ=dS z6JAY4P?O(6sR&Q|z@wB3p{AetPOk`^-p{?7il8RHg;F6pJ&#fT?jRKa8e=E8PV$?+z!_i4FMvHbsR)mez=M+tp(YPbDukLmIH?e7^5CRGsL6wq z3ZW(sPAY_&JUFQkYVzQuLa51ulM10G4^AqCn*5qWA=Ko-Nrh082PYLmO&*+72sL?d zQX$mj!AXTslLsdiK}|6@$)FViClQG;D6T@N3V!_8w8XqG58hur&7I z^#kn#6H8k=98zh#3+U&2vf!j`2~OI!MWCdn$vJe=M^RdWPI|!5Nn^6;qz|)h{5_xw zbkc`dQK6HPG~q`OCv*~u%%hVYV(J8)#B@2scfvx7f&=LXi1NZbI?1DxJUYpvlh6YG z2j0*h`yY7Y5TKW&kK`JXYf1V^{vE&e;n7KK`OW^(PRfHc2*MZ?q>(~296Cve4?`#U zp-mCYCq^e_%*S9kDLUyq9-h=NBs_^I zjx0QByDU8E+#EdV85EbmlRjebq>)*8(yv%K3ao%9{gO2mJSj;N{u*(DC!xqZJn31c zPT)yQm$Q5)ETkxSkZ>q@VIH33;Yl8z1ZVV{CwX|1hbL_j_HYc~;Yl=wd(af6^Gj`| zTf&g7atX;&l4T^zNmh`wkX%Z#l4KRhf1NjZcoNmV*`vrjJgE?B^6;cWsL8{V3ZW(s zPb!3(JUpooYVz=;La52ZlM10G4^JwDnmjzI5Nh&kc!f}thbI+6O&*?92sL?lQX$mj z;YmeMQw&csXocWOf=^`ONm*QlP!#}Im2WVOLpr()NTWZ5QvJ zl*cDcK)DG%=^?`>jm_ed_G0ZQu>zm8C$1PiDM=IVjX2?xP-Y&Vv_DfP_#~#wnZ6Si zQWRW>1(pf(_#}@{^7tgUg~uoT*C8tWkGmD&fgkwolM10Gk54Lunmj(K5Nh)Hq(Z34 zWm>p(c+{DukLmKB*9D^7y1esL8Lj7D7!PpHv7n zd3;hK)a3C=MNm_WPcmqQ@JWJCWbsK^T!l~-09WKe8d-c&9;87K#-JdL6r$nqNkV)W zKFN2+MKGTjpOi5lgXN_7q=SLR2`7`B2||3*fyC!T{k!D1PpSYY%^nhUZ7-N;{N}2>f`dIho`Xl2b`eBRQSq43bGC zlS!tKOeNt@%G%zH-kZ=8FQqqmd{Pm{Wr4>h6+%rOpHv7nd3;hK)a3C=g;0~nClx|X z9-mYQHFULJ%I=@EV{ZSXJU*!#Pq0Br!sgJ1>d=k^;Oy3C$DGDyc0?UMXe3Hi} zc|--=;-y4XJW+Guz{K!)dfN#kCz5#J3mRdw`r$CYe^L=1_3YC&!nc#W3xq(WYYEh;*hr}YsWiw&N+V{?o;TnyO4to)yI?0`l(uSVLVRZTqz6$* zf=>Dp(Mh{DjMDB&wWZyOT=)>H^kG$LPn?(bIe7g*JNVY&2U)-IF!Cfxk1Hp|C5l;NfKEk3d%djx0%kIgK~1tVKO8Ef_OQVk#!D} z5jzJ9988RaIOkAR%sD1`=O~bKRG3Wc96yBS!=HlCIlfCXi2ApWor9*G_l~owLR=^I zkDM}TE*>4VGv^HSZ?!yHwy=5S>ML8C2l_`%nH>a^;)hCqZR4B)AyIdw_l$07TC`vl z&bcSwA5eJWINyikgxlaT4d)(h50$4`(`QJ2L-JdaXGwksQUQbgH6jfLJ7m7NE~$f! z!PDnCW;O?-@SJBj!6C)M!v?ERDmeDKGQrB9eI8{6ODv>Uxzki|JTlA*o>&?iKWlS( zD2?wQIr+SKbpu^^+%~*`wjbBhl+lwku=UuC242ao>t$&E8a98pC9W+fm33HS5vdM~ zS2MXU}Kk z==m-%VrCuV=gv64P8rXhG!t2-%${0@{Bx(_+K3smVBaGqO`eMjbLTZq;e~k+KWfIT zhPv}`Hf1`_#!hN%oOE6jYcgUk%Nj9dHgb%ZH;Lg%b48~xX3{)_YiBZ|?p%1yh`DDq z4m9&&R<+&aui;K;LptiVY|E&Bhg5?T4nJ*z6kNJIm{n7{qT#@-r-s(n{iEimM)0T7 z_8~Q1+94cAwqxn2($2y0d9|O0D}$?@SEaZ=m8ty<3N*DdO*OSE-qg_lN>}ck>p^KLr4xKIgI3Rk|Rit zBsq%YXp&cu97A#}$#EpFBsres1dL45_^_u)(@% zuy;A}pR4NcguMRdpsd05$E(ucpUd>u$SRutn5LTk6mR-tZqwiI|0DEQardUb^UxC1 z-#oSmJ_gC_FV@YF8XP-6P9_!prK$=~%&TxAiW^*oyebv`rA&p3SxZwP(^ONT;!TCj zZ7Tf3e}oDv?%q^*30i_GyofD=*W|nk6Xgu4!;v#*&zfc~_}8lXJ1MWf)hKIl{qd^w z_t!H0UB)V!{+Onk{uFQeV{X&mAO9otS8?~Izjm|)^>+nZq~iG}#SE#tQS(#f{jI9< zPR=W@8wCxnJYJRZ{#K^EUe?c)$28THr+8BybDQ#>`;Smw#oe3ou18BydDpN-DxZHF zpuIC^oLe{5T<`BywRcKhd$*#b!L`S$(%#?8w0AozXxd|%YT8r0X^**0d(ZzzXs_b# zO?&sCC8)hS*di6$GtCU`duPs^J!zh4?;lmQcWPdH??6d|YmZl@y?>Nx?_I2*X^&~D zX;1N{J?1v;z3?BQy^6ax?LB~&p!V)(i&Sb)n#pLd855>_v&}VL#S5xlPw|$Te-%$( zP_^2C7n(`j_X6!E>Yo@-sd1ic!pu1nG9ypTkZGE20%phHg?o>{`}_Rt z`@36{!7R?Gg_NXTG&q=ZXqd-gOq0cH9_Je3;nOqp zj{jq%2!8@nF|GF_RzqL6=M&0*QD9o>k(hmYimB~UKxsMub&^ydI)S9fRFWPGs97p0 z?qyi^pu!{y5?Nv@ES{`Og{fO%u|rFe9M_NN2Gy%h3?Bb?&c8PYJN(P~v19o0^RXoN zpqq#%1xx3b+Df;CA$yKXNS2Z;BUw(ef~1AyQj(Py~b0h1D+&{C0!XWMuVJzGG$1ijH+xR)dHGURB2mxxQnyp@#2RY4gDb8veh( zW8H(sY#l4jIoGk8q07u&$0|d&nV(C2$GSOuEIRaqJMLI3v}_&gflxj>*1c#1QUBIS z$I8C3>MM29srHXvwR+`}>DQs(h&H0hTQ0{bb%-8zC;K?X{R!3r_(u2{S~l!n1KboC8u( z;aRK*E1V=zg;7$z!t26Qu z-2&pBw1E_J5yU6AI&te1jOT835w3@eK`L%_AtH6FNfK2P#pG|b#B_G6l#{zvlMzw^ zZk3l)8QELS$WYoWoGv3n5*&h6^)LkMfqE0nzvuMV)}2LI)|`P2elh(2{N!Zv$n)=4 z$uP5Q{)*3insr9kW-o63DqI2c2v>vf@gLG;fMAWS+09+qwAH?~74zzqj^ipQYWFe*eh2D3QQd3@QkCl`RsM9-Fgzv&{cTLTGm(d+4+Q;I7X?d z&v4?ni>Y1r0BJd&agsvHfzu>KrjlaM$x6z05}7+kFTs{kJJ_)3bhZG{?s4oYyZxI{ zT5|iZ!0l%sqXW2BF(|r;)niY{tLlItIlW6xkle~7WxaM<8&qv%x&$&Q?Y*qB>b>rT zgrrteSSzkD*ih|FxK>fCdsrlE#jC0n1m|n@RwhYy%4ND-^P2?acP|){pf;b_a1q}y zs!Xe-lU2>Z0qTx4kIR(J!S~7|#%azylVW071m}9$f>u{8Fjz%xjQlQCnoja|RvzC# z#JZ%Ds7WzUzJ)%rb#7`m*YZ)mrBa6^_Zl1;fTc!TCKEFZn;M{XrJtTSGG{if@BVSjtG8QwHaJb87&My zhw{TOk$e?|w(u#|DC*xeYYQ``PJn{*CNX+&lSnbonwgs7Y$28ib6C0HO0$K>QCeaP z0ei~}*pJ1&`8`~&@XhbChV)Hdm2aYiRPnU;A2Nw4o{4fTY}u9BIi|8p+YRAhewlC8 zs%4l@J%#evjz4C(QU6ZyUDKx$EO_i!Xhb3_*!AV=Xq}?P3zib7Q?xoR+3-5XJ!eve z)$wUp$J?!rJI8fg#Y)B}d`oU4a6GT+{6isgrtU2Blu$o#26ewv<|$Fu7yKDDNc4Yl z{J3y^!SlFio#YFuaM{NO&$0G;ROS=P?@(}R5_E#UGW9{>4P>Q|0&^|ygoP9Z(~@H` zQ7SCk9hflnr^=A+4ziLY2jcRQvfY8J)?HRD7CUIQ(n`7RK=)|f!S^?!JIGYtnj!5D zYPLd)k0#j)gl7DARxIj2Ft&=EiL9DnP=)wn)?{ec>zFy)HrX+WMkHt(dX`#Qqnm8o zWJWg}y$mf$a$Lhmk~Pc^)iC)neRw6Lg;$Ym=wgo_)1$Gj=QPC)BsY@0j^rki0g{c{ z@4U*GK06A9?a(YrOH$M9r@FSMY3|}ovxhbj_3r`e)62RW+Q-JY%7JI%K|T$VZ`(OD z%DhAC4cfgF>OI_E3MbHjHtq%RbW}b3|5!}kT>Yyc+p}z`t%?EhrEne<>|ThZYiB^= z1vm@$a+Y&8FGc+?OD3OaIvC`{mNdu&ac-tap9AS}mzgO#oR7Qh`M6uzv|!Pq=2fdQ zvqguX^yFE0FK3I~SO=pCKGKiE8bvr!RV;hb9fU}Yc#}Qktq;eEcJd09cGO@!PBL#wR6mSGM9A@vF!^ET6s&2#Hc9J|Wv)RMxj2N#;TG${j z%LX+~o3+7e)3os)m|@F~upXv){HLjQ{4Vg6yCP5}AGe)8C_ z@~|({y?InJS|l7hoCkXd&n3B#WC6)VAned)gQG*+(^jV3$WegCd9$;?#vLnXh$}jx z+Ih3%xvz9Pg9F$DVS+gAfsjgbk(Z(9qz^he(+6Py%wfQ#tmjISRV1rHD*B)nL|PwI zCAGRAl%IA#=-t9An8msuip)8R6Q`o#ZTZ4eadFR6CXQ7YRGfrCW^ym7xVXnj#l^h? zD=@S~$%9zlN-8n#Z&Hcs7+CfOh?6kaC#d<<&jeR#PW2MV@O=VDC!5KJ?^XJQGPRqT zXZnP2EtS|w(o1q32s?wTz|k4(n(GWUz8|mwNx!;-WLuL1^ZmfQ*~wVs4wRbo11DDY z1Glh>x02jOayv*xKX5Z5tskh8T7H4@^ZmeG%wqijMdtj%i3=$V&Xq4b6_@P?OkC`^ z%9tI2WF<--#3r|sO3ZcznF_K3gH|xD9%S-XQgPXSAR~?iZ-6)nbNzst&-#HYeLny* z-0Xh%VUT_xqjod%Y(MZ87(#d-$^9e`fUqBUBX~tW5DyA$#dyf^fCEx+I6(i`yUok# z|5zq<1DXo$7NZp;y?fzIZ4gSdt zE7_cWilvH2mTBCqF!9Lni?D$3Dd$*UWNI*uaJ;NouZBbrMxNdggrjsl>RqPbKOV zinSP2oPi5bIY!JIXH{5%a0p6*xz`w-u%tFs+RSj3wMO_X~Emj2%`RNQVZ6ulhwavNi8<{ zm_>GwzL@My`^CrD&VHA+#Je#}Ksm|d>mQlNmwPqY3)ju2_Eycx)++a8HHFH_Csdb< zLQ~7;pm0B?J}5lJ9$FNbGj1m=q$oTIWXVjF3X7jtsW9~m%8-3K$x4zOh|5cgA5~?N zSmvO0N^9iaPIQCzcJhKBWx`N4li9){pDAS;H!DovPA0Z*L|PGOeBRVg*+7EYo{hi~P}PbJ3wno5+vvKE7ilQ76k zZYLENJ8dd19$&HoLrauAh~=%M60_q=yJc07Wp98u33GjcTF}OqZGLfFXplNE%7b0b!R=2aYabk7`|lrVuxvU$BqQ;`K!h1t+j80FIs_nw9{` zU|-T{A#pK^O!|Sb=m+#sg98&SVC5F7a%B(b*RZC9PbWzj+sn6RkI8h z5?%^IS1Hk&u$NHkDuCX_hF$f`?DWx@J~?x4vM=3a+vJHE3*)Z{tU+msm%NN#!aeMr zm$b58ZK_w9mt4h4(o2#gvTziX_mXu?mpm0ID(5IBMnWKqms2rWM{#1Dqp-}u#7Ky9 z6xGHYWvX|S0y&DQTaIET6FW*bG#_3ALPxopX25l6O)+pz(}iODei3KM0p&EjOg_cd zcxONNa51U&j9KdTDO^ zd+1PP&Iz2jkiy_h`NC6iv0J6$;vu0jW}iPPvxCe`1zCYXE0|UfGI=Yh zxaenGRafv3lMCPk@{y92t9#U&jZyg*%fg~`NS`R`yM;d3DL zjHhW$xmg1Ip4tSrsrie%_{6`xmVD1A59xCQ9e9WhbZW~A+&hgTteFr+Tda(2Nxa{ zht~eL{u)KPUa2+jDv(MJF5*4-5-r={Vi#v8#~CIr-BZ{`83_3ztrz2Y9xG+ z8&hE6feR4UYwio_$(W)sUUo2m3x9#JVl2E5&6WKnuc{}8T-R0l&D#BD?eMkY>?yoI zjoHS#H0RvY*bH4}?%vvD=;~O#p?gCwwxQdz<#6|GIgoyk-BuaDa2yXjx^h!2>2rlY z&D%fLpwdeg+is&1`G^6Ga>3%ocyGN3YuxsfIT`Ykr_A!QWjp=G?}@5O*=y?wtQMa& zNfM2#QA&Pnek#+M8&gisjZKCmKoBpdGO}ZHlM(JtfLO_rXJ1=0VkE?k%~cf}o7ZV< z?gv|&|6q$V9Tg@MkIkpTLc-}F9Gjm`YXS}=*={`f`SA&f;m3ZG2N@@RlKl)p!>~i> zBUs%Q2!x+CXj^4rK<1&i#7(;MgRw?js~C)(#rn}fcvXWjNcV#=zXy2{_aHx;#+-ce zLW|CI%WgCn%LF4Rn&!y(E?2~l5$+ut0 zm}HQ5-Tp0;{&}E3-M2Keua}8%Z6fMFAaT>g_mZ7)tIlGU#vZ(WpdG(7x^=wbg+0Hsj$8Jl4RYC& z>9V_H(1fk{qUDR5FIuu(J5k3Q^O@IPhbkpryF=Mt)P1b{H6+)9RJd#}BF$y<3FRIr zKJT*EGqt%arR99XNeU?kK9&@jO3J!yMv^jQUplgqBnRSHTq-H+vL-2ZSr$8JwbDvC zm(>lL%l^f?ENrAGE}JnQGsM(oZ-xbgZy>oFgf4p{Z33T7R&m)E-!C(LqmhHUQ@wPY z;$HhDS3d${{`fAGm-yq$Vt;f?a`&-j?@-OkUi;t9YVxfnNuoXgrKC>CcewkR&YX~P za@KA#BmshWIh7H+Un(PBt!9CPiIEWZ+OMkEYyaNfT?*nZsk-%qGn0v5`#%WHhYy0# zPu@#2z|V{%egf}MI2nKr^0FW9!%4BMYg|Fs!0+$qZ9iJ&R$V@W0usmg!ng`h9>s)@ zsy1c5@hMh~zL6x6U89)1Z+wpF%r__}XVfM`5+I0|QyE#`Fd4CLu)x8@NQm_gNMC0p1i{V?{0faN8_w?Auc!R6`D=-k6;S7Rb9MG z-BsvbL-wYn9l{@y?N~ahv~%!njx*d2*=`!^_qZDVf#i=M74O7W#zge6`Gk7^L@DWz zgR=+EvnaD*N~>bQ6D0@Yk|I+{@xY@@5(^$wlH@=fi%TVC--$DIVwpork{tI!th=)p z;=lP(3=E_wFT@#*n-!)n#D9kcgtg;A#(>b@{zRL|4rbs@6aJ6A_l~!#s`f`Akc5&z z2)!B*OhT1jq!^L_@#G|sgkHi)a*~rt8c7ZidIw<(2#2bOSip+)f>%@&ks|uDf!GyP zyjKO3YrEI>zTYwC9COaK=Gxi8_j&K--QpY^O}J^>Mx$dVPVDMzAv~tFE*txiX@f=m3upB%UA|&T??7v?yk)BXjP(nd zKjGmB%pW$NsY$dqEU_f|RWgp@!SS5_>`JqDqu8Bd4~ji0_JXJlYh&@HZuPc3Pev42 z6|-$9vTa|Utl38JEZaP=n1gg{GAIIv6s!Q#tVUpg1$v>U5jc#25!el84d~Oe)7(yt zjwVAv?!|~5e(@Tyg-Eogz0uKRpj&t%u>9~9*PkM={UC4^umu?k7@deMZ-mIO#b{d5 zsnOA7KSvO543S^!{#JgiyXNK}q}>0<@3fBO5&~!r+Z=(fn!werLqH361jTrYV<;w5 z90$SDxIaEo8pr0P5i^Ha>HORDI2~wyj8vRY>{OjkT*P{apMc4HV*Vm6gr`HV6~f=v zXA?wmr;3r4*~BTdn42ZmL}mie66+g>hM8=q^^G`Lawvu%0EmYpMkr+wBg`hKun9&$ z{A@x@(b>e5HM0rCadnt-$8bwJDP_WJVlEis90XIQhiu9=)u(hgmvFCVZpeG>lbbs1 zr3@D{QSj_TNt6Y%7UTM`Cd+xyXvy-YWY&N+CT9(Vb<1gEKgBr^wYkDFe5rGVLTedb zpgv|0p9OF}Ptgn_WJY}tC#sP`x$+1XlBohm6B^+J44BrJ2p47p6*y{d4<}&2WIrNY zm;hAZsJH_-0rMlM_)ed9amkty6dYU~Qoa&)<#;lJy30TWcQwWJ6gNSz09=Sq6o7d3 zdGe7{kJt6Ky?n0exoR#X&dzO1!6hgKj%kmbuNC~zov%OFbDucwPBd|MQM?19P3{+3 zOFBSbp8I!GspdW*i|IfxKiANTtTpJyMwK=BC0 z7b(6(@hC*CUVIK;>Rh4id6G=9D^?D!Ngn6PS~&=wC6fnsghfU+85Ds-4XFUj`DqPF zfdv-mg`P&>P)#bpw5ZYIXfM!n7e?SvS1Q0XrUPJs1-iv10;ih_>f^AfKvO!!7Gx-3 zXd-sHsgT5vR;&`R!1}T%#L!KJm)4X;q}u9G7AvgP1WwALY$|*mv~b_1c#h&n6hEW* zB?OD)llaUw6$%q1**s0(X<2_Zwewls8lpwE^I~}!r%$Yjm_B`lA3A+nzg`$$#!>eQ z#qTJ7Pw^_ne?hb})b90;h#B0haSih0*KnP~9so??jhiwtT?H~qLc3QW`m$K7pO=Oo8 z8e(DNw%JZ>r!zj{WQm~|VkVM{ha*O)VG$$jv{GRcjDYx^Rxw2?R`+phc3Kg~)ghH) zxMinRDdSVgjRiy89uQ2Ioyn&3u7=4IJKk!20~15GTGy4!weGw4LA(_9E0`hc;mM-& z&+lD4SJzL6K$9iOQOPvH@30ch9Z1^_q99gna&Q2xl~>FZS_jQBbGZ9-C{NMMA!J5* z59bJm)X9}cxR68@I5{hw2&8ZV22ATqgbTxegmaJTlLTl$qk+*pz^0zwxhstO5mCZU z-5tOQm>)P1xbRTnE7lC0pyBF}?Uk@A&69!CjRz6jF%*+2j)P$CAC6Dt{x)`Da{8oG zCQdzyH#ISz8?Qtw`D(L)QmK*AxE~9CfEsoJMgv zM4QAfw3gI@zC7_~0>>vlA&ZGXFl1A%Ji?`kpTNmkrNlSefN6b+aB1QvaMa#tU9=6D z>_>!4vp#{N;tt>h%xAs$PPbRDT$ApIRlCM|RXOvIsb~JhwCfUzODQgcXp{Mc){+d+muLPJ!10++$YL@O z3>lRxk8o+`Cvb9BDf7)XU|L@yT$=d_9JM!E7i|M3`w`*N%unE`xC1x=^O-Nc)6BnW zP3D7xt4ro5VONf)nSULK;BKTCptuu)nST{Nk@@kWY8>xZdQmkjfcu8K(VX_D>*4GC zdib241v=G#KNQ(i|GfHomWP!FUNHm# zKs+2VLS9FVupXwuCKv(n>tQiP*Td7-tcMZD)gfbIxMg9alnLwM2fz^bNeJf12g#;% zJ$%Br$(`ML2FWiD6BI)KHNp z#uwR66N5MzqZLCC0K~%)BTWp&2#G<3O)vuD6GKeV#F()rF;=6*s8J>)#uH$Odm4g? z@hI7p-rzre>V%0MX2#fRX0-f>v232-L46MBmKg7$zgHhkc7?rS&|@j`v3iQU zOru^Aqbe!#5-ntkSQDW)^yDe>Uu=gI8IwGA$xOt=Z*LG@ev^L#EyV5S!F+ijOC1C$MX6tQO(7C~TBvo1U)U>%;8jbjjDQYk1G@ zHaNv?OR*gVpW3WXhn44H@R!r_V>h`+U2pJ}d-vp0jO;JVBvb}mDK5M6jNK@9r;t}& z@dZBmrj$4GFIwsLq1cyVKZ^Y+4xl)Y;vkBHA$phT>nQ8J6rQ=E=sXEFh)%%k-@Ui_ zHo)nVrr6^}My1c(b>1s9K|*ySLgg45Y{UJNAG!_ql{`Ld-W(qumG}TGkIyDCK5SHq zk9@E3k<&Fk*RD?Ssr6Fhvz3p}ruF!YpE|vLe8>)o&rRj{Jf6pg&70%HqY@v0A@O*|{E{8RJ%xKTKSS&)duKc_NPwn>WXYMWXY zM$;mB)w8 zo8!Zy5+8u&@i{)mhmA_{k?%D=a=ON+wK~P8)=Q1gX+AzD7UDBwCHcddh{R{09G|b| z@nQ4k`0%L22Vi-8=EnH2Q7Jz1y~amQ*Z90`b&5}|ml~giK0bXKpW(^;ZSqRO!#Sda z=hkv~p3cL==FQ>ZQ3(&g^6;D+!^1|U@W}TX9ywjZbHnNso?0(8JQw@$tkCeJ)9Z;R z9ov5T#_7An=eBZuzLv*_&70%HqY@v0T#)pkc@saN}K61Lo=f>43KDAzId~Wpd zxvn0c>Bmh=ff^19Yc9aQw zvgiEp;Q0HX&2~(lMaihVZd>*n-X|tic68p0FJ(u^ng}bQCKeEq`vYvJo1VnUn5`IM zD-wi%x?X~z%u9qoyLZ*BSRB@kz z;O5kaNUL=BbJCO<9bOgSPFj{FcQ8wEUv13 z;z=;XeGP(n@ny0p&5LnIPOs+$XD;8a66Ra{Vz_>;vRT+ zKDA#!aUliUxr=ygC*CO5H42oGpH_80C^vTP%j5e|+>6RC*BziheOLX4Mhj2bsX zeuBse`}YR*o!7NadcC5xUO0PzKiOUf=MS`oD8fK%C~rds#(>)ghPT$GqXwjRJ7T2R zXvxfQ^33^*=IfC0pD@mbj0Xl`PIcm7n7K84AnN4{~yP3u1~Yn1IgvW~egU zz!wSXfTGftMq8t$fHsdAYPcIQqROO!Hgo_ffWW1<3^M%QFhYjk8&Z9zsDkO!VMj}c zhuWn>Eqr-8yb2@TpD6x^;-3`jjL?ez68_UcI!rvSO**&`%o~xTr95~Kx}WC3PPn>9 z%H!(Zulb=@_dZtVJptV?nCga85XWsKmRDv@k@rofNR)O;CQ^*j2w-{M6Sf#40wU4{ zD-1H_y+Q`w%gH6)o0)*l`(~&q?>(yLJ#A^UHChU2^O&KgyidAM8#;g#K;Y6_2AT5S z8+s?JY@fDn+x<%CJ?|MO9R?n5$NR*74PVatEn%eFmg033J5!8-;7n&EKEeAjp%!x( z7(aE&^cmHGpe^&WfuJ>vV0hJp31E$j8pO(xDDiKVlz4R>Ca%|@FnrxRXf0ul3ml` z>S$=Q4s5kf?V%!X#&I`};z)||6cZ>WLe$2rBk-k;S#8hr=EQ#Ukpz=?t`1zp&KT@* z9Z})CCVLDwWO2j|Su6-CjZL;U+vqSR=Q5vHqd{dMospzs?H&!k!w}UsvIvyIL%&jGkB0Jo$mD zwQ<+rJK@m(*kW~ru=elN@DE~b<6Z&txP7XOf7hNtAa*BdlX4cauOIvFVc!xjXFz>qS8jAqDh{W?4Yvp zSB0>Xm5G_Opf4_h-GlhQdzj)82(t2%Y{7R~^5LN}+#3n>fBJNZ8Y@Urj%=CXNAL?Q z<~c6LVM+!mxWh(z@yV{}N0=a{!xV5isC9}fr~sdca|}0q2#d}HoSz}nhE;+VVDEaG ztdC)NQC$BKE;|Z45tZ)Hv?K_AFXC8y>t;}#Oz}9y(-hx;U}U=RiOB4mP97PXWK#k^ zZBF7gO@HXj9fq(wv2OpY9ypZK!>rdjan$d%nsDMMT<o~sYDM|3H~Ann zd8>5toSJWTWxFO$t!ZtNHl5isXU$E%!I8K2N}Weq1mE(~*4E>==4KMUi4R7R3&4wh#09nLW#w`#JH;&|sDB zkHKg^^!^411GFm`^sz}-nD)P8C~vk%u2s3ZS*mf$fT?bK9%HqO(k+nQ|ah7sbtIxn3_sMHc>-= zu7++;o5FTvYM%*A7o=!4H;Go-tf(E#e{7WWOoANK}berL? zS_*88FEs@^L=ZmjmgL6z$c?RtqRT;Q$pYv#IO2oKE4R@!gp7$A!opCn(xge1hze+G z>j+a4vMDMNs?*HIZp2PNO_QrlLs%?E4Pm-O3p-FEDxj$lPg4?x`lv*@LE}2rAox&| zGsZN8K|g8;gFfwAB_*N)nv9mFM23Hi9Z`vNgC?!I2Em6KQqnXG^`ld&AzdNonTe4J z4~CsKZv4c7)+TAr`kk90)AXPWQhOtVahV})rpv>lV1Q-rl90K6j5-nr-FS)#5VZsy zhc7ij^M=x)y^}m-tv-g0H4h1vxy%DO!oVq}(SSm_r9i1?3jOE6opo3C6ZVH6HqHH@ z?vkC3lcC?7ad&XW+AL%4L>#NlGEShu+*{yLnPu#sbeq$rQ`us)JDeYV(k$z_0_gWT zgb+Zw+r8yF97E`=+TMbq3je6t4}7i%e{-lio!|Ahc1-(k6a4f`&dNrtQ(Nqn-C6k7 z&82m@pM`G+z|VMdeX7fkD2cll<79L9dxEXvQ9E%~%jTIOC#G-*+ayEs#!*JjFI+`SI1_ohCr+u&vVx`79 z=xBcEI_T_5%G`=W?luaZSW6in19(1fC^6Wlk}`Ktjh1VI6=OgcpqWMkO7kTE70Pwm z*L>S|UzCrnD&?bhLGQw(czh7bt$3UpJfXGvejKaS>icLdYc-Ebt=>23Gi&vS*}}Fl z*(~d}0?6rpAD94ItG5!q(Nncrt*BBk6jEpVp8z~r^--$nZ*8rtN?bx}g+1Zs8l9Yg z8xO)nApO=o2o33ibw4 zWE_#PYi9)k9wCV>lT(j`Oh$~D@|inoKmz04!P=tk*sim(oM_VG6U*{F(CnV0_z?uX za~Ew{Sy@gJVpP2>bI9U^KkHI$-PV59=KIR!=X&yXyp0NdmH4OAdnndbsr69z2<6%7 zuVXh~PcepKcZ$6!_NO?6;^z=_!Z!GX6OK;z3_92?95rpy(Z|fdGn1yCn2lczyLH|r z)w`QkcGcK@m~}CqB?`lK$FQ~qEg$K& z9brmB{zfHI8fj)@H)1ECrpeW&K@!_9i`7wK3xO7PphQ$aQz4$FB;7(tqCw+2)gbs# zlQYINq+1BS8w0EeeR^KoXp~|BO-4&ol5Qa+-JnUU)Q#8)s39dyL%N0FyWtlYwB|J` z5f#3^?QZpLM7#Qy^iETzRNwvz%iTXI)*T749>s8qO(?dY*am|2?N4-3f9r@L zYpiQ5`Y-cFt~9nU@r&X5y{^YMMTaF-yEbdHrTZ$+)9JEe>LhPRZ~h;67#Dz-E$><0 zyRdiB^04R3{oQi0ZLM#OIkDSpZvUd$%jYj%BwNBgOMCdYUL-jX^SRCNi0b7%0zH#0 z^?oDS)YtOJ9oB_&*o7%wo8YvqyL>l}K?A|6b>ryJ4QnFZfV#XJ-M$;ionA$Y(UW1;6J9IC zFS)>^g-Vuq9YD0ag`L{G)+y<_bnMi$V^@aQ_~pTg?9@pVCqvYFb^^ZC4O8Z1?MUD;YYHv&?cd}+V? zhL434r?AhsnJaOK^!NfOw;ul>L~Si*R3C1(=zHDR%LXn&VsGfqm;Uwb#C=o3`TmUQ zntZzYwx?@4_C?v7o_=@@6~$e|bEKDjJIR`+2etbWeu?52O=0yrw0Gf>Y;Yh8dg}yi*m7E1w3=kLp zEh1`w*eDB-o-QZr%AdA`rT-SVLg}ug*6Lm# zWbt}Ojhj9rI_6l$8t;OQ4&FyM_wRuw3-rD#WoM4!4L z(&0&-`!$MhL68n##wX~oW0elmk3X`R2}~1?##vYVNbEQc!yh(ei&JDIPCa}&TOn52 z*ptj5bnH2f^$7E!cwpwD#fy5iCjAT$R+C-~HK{ZJ{gld3jAQ@;rVT*TU_ix}z1<_k z*W%%P+rKvqPA_=l`}&f>=@oU4^G{i@LtHFSstlj5>mPKR|@4crVcxY)OnsC znmWHi55xO4{e(}b@G_dz5-0Rn*}^80m4J>Z!+GoPh_5NbMp-};;}FjrBZRVoL4%qw z1i}B(SL~1+>HcQ)cA>mg@YP1oXZL4lbAN*%kzb{1Jd8DWKW=W*$M=i#aVJ|cvrVV0 z7z$P;8+zTz}JqhA$;S@{_+IIPQj05ULib!8oFmzr3NO z4RpqMvBYi2)*3IOWxn=EjyMRIsfbAk)!AG7vZQ@Y_co?T& zk<8U~;fb^umxA=`Su2+I&c$-YdL@SM1|??rV7&HPexTs9Z43^&u@JQwj;3ui-G5^VsDul>)x^Tlew;0lAL9fbFH^B~zt~_+@`3aT2A%5`$2Ml zC0d4y)6rAMUSQ62K0kEMG?B&2|JvPGah~a}Mw1#vLe~Ubnn(o(bR-8W@U_I(Do&h? z8HsraBM(Q6z=?SMH7aLC z$v+ffRELC_forKG4-=3 znlBTTQP!i>)?}L6G|9nRIO>y}lbG?zBnOqXHpzJaCRwr%56SM+jRJoQ>nIc-Zd?2e z2L;rdG`xu}pglxYRjMq;oQ|B|goyDj+9CxDszJ(%bO!8pZ~MiDGzZv+!Y zPNZ3!dH6VRA?BNn2|&z`49r8WoIL!Nl@mB+dLQ#af%Gi-yyVq;Bd1kg#G1t_TiVr9Bzj z2gg~GG&AZ+gROEr2TAJYMli;b=GFRs%mz5_Hl!FPW>@lNeSE2TlQ)zgKy#iqBiLGl zK(s8J9?200ku;SNDNULLNlr+Kks=8W;HWMlrNl^(sI$?gXptv|^iGMfWTnJNv}n&G zF}8$Ow;jcf5X_yCv;()MMjmkf17gS}j>;Atb3- zIV`QY6`gsZ2By2&S~mntz3>egSPI*cEFNJ88`kPxcyz$`pwNw>19tZgn5nyy2iP`y zy!=m3*w>iACdm>W7-m;t@Dn}$evlSr9n$Oj?igypgG+H(5=E}>2a<8lYS;Jsv~B9) z1Va_*V|@ife2Wcij61D$CQg|!;O6tfO&`Ts6bmR8QY@laOtFOGY>K56%P5vp^i!Nm zaUR7Ait{Nhptz9YB8rPCE}^)T;xdZMDXySc<;Uyxoziz<-|g-Oo-9vDx`}_io#JMS zTPOx7Zl$=5;&zHVC>(`cpXVhc*p-&hweyTDzTCvz@nxIld@7_WWFG63#>Fl{8V2|QC5zWBt49sSwl*FfOq0h zdfnzV?k)7vR0tO3qZpU|)}9@ba>~RLCr_F(vCPc+y;<0a&+n{pyn5Lh1a1opj^QA_ zWpXQx=f7U`mXZ847-%E;MI6Z|lWBHa&B^QyQQaxhrOHTt68k4J;Jl#>8Bia&%yNDP zTaR|LmCFRoxa`3kp+IKS8o>giBbYE+PDmM@A_)%Qs4gO*02eEMTDHcI6QO?9C5@k%gMB%`#lbOcit%0v(h27;bG~}0L z$0U|;@*gGqbbe?Fzc`d|zXm)PhunD-DRe@L}{9mFq&%dNFw01pfI<>uPJB~CL+H`i9~ z{KaUq!IGtm=kXrY*^38sl8U?A{7p?dD?#%NeqraJaTj_(3rj4(CzM7rVAiOyrK<18gz6??A+iZL*Bhv-Nwu zt(|8}w8nIOj{8#Y4726lt}pZdcZdJGci}I;e7J{SzMDesWaEuBQL|JH)hwiRpN18r z;m2tWZ?UmRa|Ru}1-jkE3r6`ZHfnJ6X>0g`o>{%z>Qg_82ew1Cr7ux!KgtiSwwHzn z?Z7nl=$mFi=633Frj}luQOPG>l34qw}utB7`pb$BIDm~&o5Yk87 zA8&X5FC%B9uUlynC-&QGa#{^vL@ zIJM5dKzq6GZcW7OjVqo16`PHAKhn2!O7Y zx@d?Ds9h()X)T**$XxujF5k~#?*2c&hJUTAcU!nSsXVfnXXVjqB_)d0NlKEf?Gs;X zxWd~0C34|a2o|OPWElEeJ7k<iJKXUxWFI0IZoODg3MeSpJ3*Wk%*{vIWzf1PEO9$ zqynd*ODeOLZP^>_Vttp;jp@1#KV7&V8PwCgH@1%bpGV3rvP}r&+oVgMWvDANf$sO& z>6{dB)C~3BhK!JgKjitF_|85@Kj0aZ*|^5e$`?s8O2zldI4ny>?WGN#BW5q=PvP`u zc5(dN8Y#(<5-Ir%Km6ueJteniEOrp{DmxQ4Tv`)JrmZU}Ig0VK;gU#MO8TG>11ZVF zhLWbFqU6J6y=xdThiCo8y@Mcz6rl|OkMv4K28k>nPu4*tSXi{HuCw47vw@_ zyYj%11@Hh58xK+s7!UfiX<_<%}Wtl<6-G?7qagVIm znqC~0(`vbDwZ@tV$A(pMbspg@S7|_rsl)(NdDu`=rYef0k~A$a)y(irr6J1HGdT%5 z1JHuMW)cKPoJnugr{{^AHBy113)y}t1SvWnpP=aIl~R;NE1wG0SSlBockbUh7XEdOTtgncP#f2F1@aU< zEaY)lc-CC3tidlv^C=*Qx|}4OFUu4x&GKYwCKEHC<(%O8`SR^HNgn?iCdu!wO_IYb z57z8J+r#tP-5c;fcb)InPTH-}4U^<*Z*Az_u+2PK@?o3er1-pAKHLfeYWZN(VIGxy z*rt*Xx6^K$3A0(265ieNy`}^KBqhdlZ5%w^^;r0;`%+vF@N1OYGlw9$B(|tA!|(g zl|-$yySzg&WS7@N_9X3lO6+S2nKcoA?@$TZ)9jOmj4-8;5db0MVS`9RrVwGzOIjB~ zW@h-1(GU&Ut*uQKPJ374?$jUAna@+a=so%#nvh<`=jA^R=t)-K-nLP8(Gbstn1t&|*>mxo zdZ2W*aUJ|-UN1DxdbK_cu$6Vhklgb|@mgq2l<`V6*Q6lRsV zFr&hSqiE*Q6vv3IjaEOX;9sLI*jaLOS?ZTFmV=uZft_e)!z>Mj+licyD8tqB)7#EtRh+V*mGMd$OxDL7UT0K~%) zBZRqvL2XSKf}qPJwL=2j{kW`?3Ul}hw3_F`;@%2t+zk+n$Cb1kKO?QhqvKIpez($+ z)D!>rmi>QX#s9a{iaW%LCjVO#@f~n;|KCN^)&ImP`Hl$iKMzNY)c*;Fw5_L^Gb_CR z#SZoV6Ycz;K&yGd|M$WgcOL}(|4v%i-`c9^|D66@f*#!FUQfLdRDWG0_j;<4>$fv9 z+yV+{y^S;&c{9`Fqu$6jQ)_=~e>0NLfAvqebI0P%3dNExRX;kP|h*aRaWo^)c0#&hF#@vK=G z;`t~Ha*sohbf2g3czdPAsS?i#6UR>;H*I1yuKHxzRf^`A`VUx2OML%RR2jgqzbsFx zG2@b7t~?9FExun2@x_h>seFib*2P`<#c=&zFMIIu+7iL8&B?UFh|6me;i21xDaq$v^|tS=RW#l< zmX*$=BfLyesF>N~#^nNb@}!=wgY;%1TVdga&P(|$Z@B~cA27l4<`dxw<9;S2KQ#TD z7+I-{f2Frs7p)0v8dSv$;Y?$#EzwNNB4ILLDue(4hldR!q(p=W&+VbQCKSQ&JH|9b z(xB^(hOk6zNe(qH)Wo5%+ieIzW({H|Fc6yq8J#1iPMa`snmx8>WkDGuzt{CqaerNp zH-g9`f0$-n2Wwq31v8f{Sd0U_*=s8(xA;65;^XU@{04Y)+DgP)N!yHu%g^!hhHeWS z@QP!+n0VXp42>7tWS;hD(h&sA)aGa*bP+9tjy5y`3LH?`j~p;9W{G)J-yTh{0GV|* zt!0W9%%kQG00mB$K?qx@W}CLFW{K76RL?SbPt1SBgC$$hqzC3C`se;n>Mj zX9(kPLA<+rT<7F#t;>|G3Sf8@#=@;I3m9p^=un`ZB&)Jx~8u@V8W|aJ5pdm;V2`=vxuN;&aw-nGVFE7qNXgKj27 z&AQX6meyGlvFt?f=hJ`R$SQJ74fWyNE5yHR@Dn!Ucs%t_K3}54Eh)#TaW#UU0 z2R@eu;;2a~z8V`UzQf6G5d^cZmlpK5Her-m?CE05wvY0uMEUywwVSP>lcu2IbiC75 zYr90_aa@>U0B~Vy7C-dD)C-j;oQFeh1w?Zctcker@k$gfAht$&*W(ON0ME z4#V81A?W`P)7bvj$l!l*YzGk-a-_T%E=z%@iXr%AB?O5f)^_xc!uhq74=OauqNWauU10v98J~`5T+CY0w4rDY!GP( z6e5H`S{FiKX7~`$5DkIrECe-6LkNBX!`urH48iwlY?1}y*jfa*SU;96Q$uxjC37ou zmxrahoL!~eox$_eV^iBeD8By4gAm%+Invhy2gBkAheCXd;DfL>4hR{|V zw++`h+fr;tu|35O6tANgMX@8rP86@F*qLG%iZ@V>>_@Rb#Q_utQXE8aFofNom^erM8-nzo?B2@|4AKeon;)$0iH?#qm^S0C zj%_ew+BLl&FoHv$6HTCX`69FKzd^0dYkw3n3DZ0-ME*hp|C{1} zAZpX9KjTYXgX9fmsa-OgrPB65YPW2}b+Lnb{cdwvx&Hr?z|q~}BDyizN_6XP1#+xMF&u(C zUJIY#@eW2dPX6P6>WpUHIjHLS1Nj9WD#-(~5=96P;on>g=WEdr%(xZ}W%po$q}8m3 zDwu$-qlQh6h${W34IMxVAaJvFczB6tgbapKeW$2`={HZfx|bJsc3<1Hc@mY0{krJ) z1xR1D>yD;NH)9O9qS&5dM+ipqNLq%Qj%*`?(Ud)u7*g)Rt?F)yJaw2}JS zP{8ms5wl|EoTWYI?yD>Fy`Jc0bXSg;AsQMER0sAuh@s0wdhv6yqt5rkG4|ECiEre|#btcegZPGESH_?nIf( zB`KKv*~!(8F|Bop(!t(lDK`&AZf;S_&1A207RY+)h;tkqv4goY(*(Qs$2Ny#?nB0^m@Q|+&JWOn8VI#hv zLy2{@2|pxlf`8{a0Pa(SbWBU_kJdv8UD<>mk~iWDFKJhMcs)|deJy|zt+m3o3*9#aG0(=%mx{{b>tp^ zeO4G=2!+94m%EWYxryTK5Ve|c1HROnkT-NUnZd&e#z#o=IMc3qAW$z-hh zENnAM{x(n&J>^*SY4+mZC?2GE2%^TzPvJ}DWudkE432s`;zeUl(Lcx2l$>mn1C@nFq=Bxmu4_tzs7&nF@Fi)0a{Xm?{t1ewDZWYZ3+*8&V_U25!)< zMrZ*BKKs@L9r7bW3lnHkdt5;Uc!MiKhD6+8GOg(VT7ZFH_j1UT2rVYlkWO(072sL) z!rUz429s&SDnSddPeT$zmsn3VrD4=4JyE`7X^2F82}ZczQ~ZhIe<=P*vCh_-jsJoF zARG6zJm3;*{G@5)Cr|vB7g<9T=QS>~-i!prgU8LVnVO)<*tI$OU|Bk7U4H`w{K709 zj)QI^ij65oK-9*rVfa#0wb0sa!t+K_YziUy`sO5GIdk2T0Gh9ClZD%Ziy>f5Zb}n) zNZJS<#xq*jh%e|+VqI;*51E_b`;m?IwZ|89D4{Ex@IwYid?6?8>Hu7zp`pR^A%`P) z$TgbODZZdXNjk;q-Zn_;;XpQl9!XnLrcb?5a4mp zrflZy14DD!ekr`=iNy>DtFq9OEQ`_-A-f+&1kmC;-Y8E zx%%FBf}QSs7|6)&C47V0Io$-Jaz%emf5JX^iAEh}?_mui^mr8A+5 zUOJU3t6my6y}KhX@%1+1rSb4mwj5g8vuMuZh4at%6Th>d#eB6@?kj@S7&V_}(N{bw zz8YWg)k3yVU$IT*D?MAz^}gcS>MKqwn|xKnR9`KJD*9?MRpPNV8Kb6+n=)Z)_oP!M zcH*(t`muR>W7-7xZ<{n1&3`lXWu(0Q_%f(BCvG2{=x-0d7zf=YVrXUJd=agtE3JuK z%bk$;lXv4?!Di}D!estb2mt^N4;w@nStCTKzf{+RA{hQ=1sWpbXV(WCLKCrN;862? zk#*NWjk^JY5xR;x`&-+VBV@NKto#mfzO$*k=#dG_M8pfPX*M-8XZ7|j(X#MPm|#)b zDMZO{M#-z7?x4kYip7;s-A?mmjgvQYcj16n9M}8YH+dJ&(5SIZ=5LQC@1H{0Ol^)9 z0vORk0BJ)bpuho@{m22sTqpuc)Tq8anqUDk>ui|$M6_TYHFp3gaDG6a2&(C3)HY26 zdekFUt5ZGKxc!1)Q5Mq6d0SX(J-!^A@8s0G zosX(=Zg$Ut1lcXIB`_=7rm^VsLa%nIEerp>Gj)3!Nd-Q`SB1jAPb8lsh9cALsjGcQzzCt!_x8iMh8l$KY2 zI5KI%DD!dg*E~)wYYmz-fy=F>^ZFO|E?T}!19CL(BBSSVH}NC<(7TEEsJQ z)Rsx+erqDWJG$cj=ZLNDCrrux1c3W_*dS8(D}=jA-7l>R?l&{M`)P=}e@+|s*USs< z{|T&dFF?@!-=~Fm1)^0rwLGl7O;R2X3qz8hIQ#=9xPOX0mGba+8prZrO@v&N61Oq8*8w`+ zmi{8b+lJf8By$E$JWf;QEiiECl9_6`+&4BNwe3vUR(WCla2$6VLDbS`81>RTYa-^o zrDEPlHq)pQCX1><2mo++*dWsMNg$}J2|@sT?$8R&oq1$O>7K6D<+j75_v*lq;9J8N zZU+c5c{4huzqOHs)+aZj9F-+hJD+=2e`aVe!nvhRQy0iaU_vPzVxm54s6ACmR7l zE{-rc33-zsi(TeWEp$@m5ECA@LKlJ?3tgCgvM4s8s0zJ5ndMR|!G{~hiHI#zPBkwi z*?9KxXy3=d+Q%)k;X*sC%ft?Tk<53{wpfr`>y4W>ZQRK-F}0EIPWFSx(xp8s@HFTp zD|GOf0i8CtT*^E=l`rqH*HFqkZI2TJE91wpv{qI#*2Yc4cS88dl@vUIjWq??CiAQZ zlGo=VKvb3kg(#6U2@usb0tpPzGKhnC=FN$6od zxh!uoUVY=~M3j`7bMx}|9CHRY?>uXn&E zD?#@pQvuJz0e0Y43WB=Z@K=qDx8O@HMupbifSinX@eCy+5wj3^G)F9iIoEAyDH#~-z6d$5^0D_c!Cq6;RH)NDFI)(*2dD%P&I`3dV=Zr&Nvdf~yFnJWjR1`vT z4~6hferO?lcS1lE1{T7HaMXQ<;@>GgOYty78x}U5BGH+e434}loGQ6hPq8RR$R@ zaz@B7k)!%fQ3ca8o&%Rwu|LmKyg)%k?&tWcmKi_Ams+2jPI13L6N%@sNqX>hjsM`eTB+G43#P{v zL%@lGDLHP)kqR!2YQz;(fahJzaYLd++`vDY)E-)Zfp6Z+p+mMrXkiS(y0nVu8 zxFKaKxU`~ETtNkR7P@dhK*SBMpbe`8Ex$qK!<=Sxlb7&VHAlq^|3A|qd+BmO|~ z7mB}A4B8fA2n5se*Z2l$xl@*wSZPl7d;QanZ9f)O(=piwiSwgcIv$5~+_aF6nulA! zHp|23lRWez)ChK96N-@(n^J5BQOm=P@ulWr(xYt3u+nHhv#U2!ULogM#!6#B-=PVWc z#>>AjHVx51XTY7cLB2?srh61|KvMP}0W*yK+YgSL$bNzFCfa-`1p(b*_^U?zL-3^% zztCEOG%evgSA^quhH{>WS%f^ABNhUk>o&AN_lV|>gn`(jlJio=r+@+nRO^fZ2C_#$ z2`kk%niq`&Bs*egf#eY_Fp`=(02DY+MtV=ztbc6U6^VMpYISNtMt2OXbCW5Kqc|Rd zEF6zdurR)Z*zZ3pi`o}s=N+4FMhE)UYVdR{2*@=o2;R&OT@dV$5Ds17wC4;Qbu%f> zr0AiT1<{6eO{YjSrYF>6%FY3nrygO8p&%e4TCl<(1OFkp$#2C3LtRlDuYbRt-3Rio9a776->`?21(299qr0(Ep`dR zG5t6T+T9Y0Ax3`0gX|oFKU2LZ2n$NQ+ajHn#d;ejD*PC_inKnYG``#P|d=l zJ>M={)ieBicu#<3>(Vl<`FG$ROnr@d(nI(q6rd^~v9iQM`i3bsD@p_T6Fikm-VaNwmM--*$Fi`b;9%B2|rRNBq>*+0ypJ0Cq*Pzn!YqkZR{|D5#mmtXV z8>q8-@03K_j~t=0ptM}i4=pW+GUmx$Yk$HaWno{_$YM>z zXU8YuXV?Bh?9uLKe)I`a$!)}d=b8M5l6qcIWU?Vm)9T`bXJ+{7LPON^pKRm#1enV0 zSvc+=_@7&6JN5kk@DLv6l&!DC^SEkT^m;zTk7*5?KX>NxzTQPMm-H-MKF}IEW#WmG zCr!a)F*k@0-5zKS?p-uz{@ekX9G!x3cKDK+IByy5AnIK-+uy@I5;oa@dpyH6RUduC zUJl1Ww-Lq06eA#NW5O_esVl5PYp*)L1hW~>U>#d;>}3NNVUn<6r9YS?3?R3|ox1C? zr+PK}GW232=|$|l%x884rgh^tq0)dc>;}Qy4rt+C2T|+6_OwT?Xy*;Z_{$Ok^7`)U z*;tD(!7}!FAkhc?@vaK@PKtz?W9Zt$(m|J&Z_-P?Vj ztOoiRw^RBq?7LmwqpdG$mM6B$1Ht9FLGsI1{ys*2<{`gdkh{0_2HoX6AeYk5;a}%c zoJX;O;(Uq=C@!S9h~i?3ODHa-xQya*iYq9tq_~RWYKm(puBCV@#dQ?dQ?w}FMsWkh zjTC$%hRlFvLL&pZCeN)ra2v(#6n9YENpTlMZ}&Ub_#*hCA1R4ho;!$nBXy#0Cm9BM zFytP>FL7fi6Ol9cLwgr4S-#?M*8IEqmX>#Ab9hZXHw8E5NcLTYA(n}YA@)Fi=n%VJ z$X=aWvUP1*6_Qx6WVpU6WHQXfN{q+CtXh;a+ z*edfo9i;sP7#lIW!nJNsiWv|L$S!nte`~vRN+ny3N9m6|bky|s;O)~ts|OQL&V;f6 z`B{+6m@rc3b0(Vwh4G(TqBaZa)05IUXo@CSfV3Hyp@kVhLtuFK`36PB&#fwiviJ!8j|-$B-VJk1fDKw-BN> z6*~)G%Knlyk@@?~q*`$ma5kIiB#bawR~15Zpnp7U5W)Qw2{#Mu%m;2|eab^+#CfIdUGunLtb&rco{x9XP znmb020BA}LD?}n$Elul563y^chK6XB*|&*| z6=XFoRGH7Q=U;*#3qM2qu+v~3Hx}l8=Qb7xn5>V(_ywM_I~<>J(9p3@6Xz7zs#pKU zHrbkvB{pzc;$T2NmFQkv!R4)Zz?+t$7;!Vkid|=05zbrpn9YF)F>)#G%A|pC#CG5*3^rd zDn`Xt?X*$D{Sf-x&nSKYK~6kJ3;J85SI&u*6QRu>TJ_cE={DMFMP{5Bn1L>Fg7Xrs zewpGGir+!Bp+}*$gyXb?BTS=L0TVew&|)|Uhp6W24KNTT0t!*&q!LlgI6zuw3@xxF zqPd^)Rl0qmzR|pB98lR215Br&zQ3M+RNeugz@mpZcwMD+}s*!-3}BxQM>_y+}s?W z;AZ~7q2kb4d+)c>6T5ww(BNNrG2+?LW+?T`ly-&mj3QZ24rE0^VIkd%{dyzC-W2;# z>eG@1!$^O&Iq7AL5_WYdfePz4jv^p>Hf_uY7>`rM!eouUe+PY{Mm@4LCHDM4Zv z#Mc_WBtcNV-vsUM%@h+Tj-i+g!8|z-pU9IpS%u~@yqG6CYqoYPtK|9By4L!@oWE$n z{6)P3vaFk}7qJWu7v=Qkmy=+SZJI1cD!7ff?vM@T(+AuMI9yxl9nVhijvOA9mEP=x z<6M-T!WLNRr8_x^N!ZP)Tut1-Ec1`Vm`kur z&cQ*~3sFng*|dpPTN9ag&kd%J>H0UO|GnsbyvW_#_36Tw+;HcDXg(or22HIQ>r|uY zgIXRojUjatlpoRWecC$B-ULN3{F;q+OSX1BSg?>A!x3Aks^)pBy0c-8>xUpK&!XkH z`PN8NV`UPaP@wahQq;<7SnIO~q02lJVnz{boj!i(TIb48i2Zu{VjOapKs1NanuufP z{R^SIjCNQkskIbJ>O&}b*fgf0^eBGJm)3<)n$5V}05_~%%K@znuM*9^Vl=O+MDsRUdAnFynfa+ExYe47Z~Oj*Xx>RL zSTw1%6iw=bS{^oyX*4~GAF8BvA)02jk0$NbXqHMxh^AsoG}XKi&3j>udp`uD`3_o$ z)n{`wV`5^eziOe(h@hM5^NXRpx>92wpot$76PrS5O~kSD|AkO~f-bO7Qfn!c)Q3>= zuxU&~=}`{W$+)yGgwm|`p`_g!%7@z2SjCo5s(B%l{|;;1=OGx%Ptn4*q0Hlvlv(bS zoxsymlXVc*@Abwr4#LvsuGiDrhW*koG#jQ^>mKuFo~CBzPbM__8NssRUzvMUhW1Ly z2zLlcnC4)V-#UHz9W?RUS9)3J+px%n`xT_(R&hV<|KOne2E{iaY6HdB@ul8olQ)z( zO%zI4bbY4B1kK>{EZ8kVpwx699MskhOt zXqBJQvX8nYc6GZgG3~lgSo`fMjM?lDpwIn;;sppY`Mb2BzqN;zo;I_EmF+*MBUV0b z$XL^hDn(Xc`tTio==9Mpx#EiqE((6?4wfS+IG}X6+#W|ccjK2q0jv{#or-F zjo;IP{?;3tsgV`Am9QhPD{Xd9vpWzs=v}a^cm7;?+@Uzlp~&Fn?kFvKL>E58c1X&~ z4Pmn4H&ch=pj(e(eToerY6KdBFSWK5T6@L$O^6%u3}X>NvuJrhM>qsMS8srUO5FZb zK#7f}j*9KXa{Hr&I{>0)`+hV>ZXeAXitP(3wjaXA zdfAm=Sx`NY*p5(|Mgt0T3$4Ab zyyTuhg?7^jL5q|n9N5YZMD+$3a)32W0mZ#?Qi-%?93ZVTh8D6QqJ@m2`bP7jaX@89 z3@|OZzP~=LsJsI}f%Cbw5V@t-2)?%_x4;Iu*_gQ%!zIC2Dp_t}qI?>h?RqGBDds~k z#ZJN}Qf!w>ius!&+9VoReH^&50Qy18amWG>Y;+ScaL@`|Whu}>D-7szN=nxhOOOCa zg@zTFnqc{vCiOPj6|M4|qFq|A?-ow&hx>)&KC4x(FveQH&AV<1^tt5}=Rwe|d(Z;x zbE#W7BVgf5og9Ca60d+t5zBvDq+Vz@5m}$8`l}b2j4VbySX zWjN$6r?>*5R?6b+BX20qUkrz>)wjz<1HPtTMKz<{-u&njyrT1ogczDq!%9ivvF_7L z=5k7j6lN6w*#W~!NugNz1TXOx=655^ciH8Li!dj`TON{w%ffM)8{G+wHaGfl@T}f^ zb~}#ME=kAfW(@IY z(lt0K*KKHlCKWUq(1<2jfV7?%S|Cb93zdlK+XD(5P}va!OvgFxtn=lF$~yoQIA4?K zJss!nZK_GpaH;s}RI5p-_@9JY_YlRy6pui#G<_7GC{24uV(68)FtZxf?k=)Ih4R5` ztveB;Mf$_LhMhHW*isZJk`P7eL;TPp^~DMkpTr^e6vbC5o`z_{#6oMYE0!xd?dyQ? zOeA74GQ>iFbKQoPGBH7ulhSGxcx{#e(t2WODH9bf+%ZJ;jpjw;fXa>-V9G=f=$T06 z9RLcPXW~+0;(OXLF>!-BHD%&6Q0u-=@neeTA;`pU;1f*TZ>3DM{hItT^Qw@seyK0) z*@@eh75Vt33Lk$>>wiP>TZ)$;+VHW^+Uv^s_zGY=ABk9u53vyDT(_a6d`!^fq!J&^ zGC*2S3@zoOqPege*v`CY98lR215Eko0mGJHXMh6d`MAvZ_}+GWOx&POP5JmIsC9p( z_$S3WuTwt$p4RlY4qPc8n|BEtDOoR%>D{%zXj>&mL ziNo?_IOc?T1Q0yw2v#H=VGv8xXh4B)6(DI};+xq6G-@-TK(z=I#!M=0G%A|pNx|Of z6#kGVQbZLpTxzF{6mCoCbK6mjf?#zTNel2a)|IlsZeVN-{+L4aJGddNzsQO&2UcJ# z$VkjAnFzDwu$#w+uDC8%#YKm`)2iMHNiX zdB#ca2K#;!=VKRy^BO+myu9V<0BCn_qBw$LJjKxv%z(Y|i3~Vor5VunX0X?i6)PF7 z&nYIxW0k}>mVG&nVj9JCiWv}X5~Jx93FA4H#5e(1J~0Sej3EI*g@P3ZnI=X8DQA`v z!)yd@-Z6tr6GI_GV$hIAL!+61Hg_3nniw9{&kJZq2dIJxXnM;~)5P$oesVwyIz<&s zpBRjjCdLoiB}U?F4PTxZr$f7&MKPDcQ7nRBVw{9eB*tMKCdR)oU1(TC$wtq)#Vq&= zvtY}v5!hw0pXHoOyMFf)xWq7{u8$8c^VR1PV(X zIi*B*GYNoHXjp;d36>`g^)}iSt@3nX2X*dncoSWs3aO*oZ=;L50{YxF6xTzLAQ#hu z{?_h|1XMkr8NY{)4kMwm$eq(Z|=8R4-!BdE90u4t8K1UsmVIHDaRYW& zdnxXRAS3Rg1^unP8X2)FMWV28iOo#E0aGa%vfH=nGTOPf7wFN1AkxSv zM95ZYoo1_#ubJT^Lqjw&*SCvI&At$sXJC%|9t0!v4Vu{B8f(1vyY#k|Sxu^rf-^ij zOYc0rpct9&)+6&g?R!D&YswjGB5u8)5}9AHPZ}A*lp;d_M23eAB8`kfIDa>(v@S%( z%PO4ohJ6TUf-B4v5uvA!VA5;Y?bv|FKFradV4P~Xi0~=_iP$8)9YLYDyF*O-p0Fi z3vPR}F_M?N1e0bIK9MwIGD60?BZ`k&DC~Inm{mzxnX9$Z>$jJn zypeyH3~bL2oeccEPQpWQ)V+z~P>RDK+K{l&T7q&(g@i`{CX$e##h?%lvCY*RU`j#- z6z|JPY55D3Hsb(koiVhOgx);AGe`A}=0)Rx%8nRdNUq2if zzR|pB98lR215Byt0X-F|yaPai^Hih*m5P6FN5#Yq>eQ5qbD`FqMX{J-83d^~3!k9k zo=sHD=5<9jaz4R(fU-XA%r9_ri{00ww-MeJS3u>{Fy0sE-5xu+d%wChp~wBM07 zH^D47Kye2I>EFU9Xd~M=9nr@B(j(yw`M*BD|9w2{Pp68hXj6c0m?@(<7g>}9W<9Z5iol*oTDl|BTbu%Rg% zVwdZ_CAjFH79p#$Ct;CQ#lM8A=r=9DLfan~+bh&~j2+>or8N=SU7nDFn}T0uv(fI% z?eWnkNSXf?MKAzGMTQdSkf7X4BKK+Qlo1Jvs?e7i&=%oB*JrrAEQBv&3)QGUK2rai z_@8?Qf^qvA4;3B_=I{2i0o+_$%Hlj^o&LrxS&0k9uRs{rx1YG_=JU^Co(1tAA<4l` zcKj#o$WJMLM)5pEZ65h!e5vDGp|w|=-)Qwqo?+=u&@8eZP?RIIxq1T(L1wX4KnVnG zXhah%Kw4)EEhK(K3wNPXeS1KG11dXWfayHa_xBd%AeDCjC~&@R{tUl!AWNocKck$Ahz~}2AvfZBz zWc@yKQSW)n^=YwJgP(YZg0#cF7ox_`weh9$GjAxN zx;iO3tjO!JF?Sf_3m_8nIWyq%=H(Sh>C4($gYX6#`%&Mr<-JSv)fR6g_f*$}A{hQ64-JvJ(miY)7T`53LlDD*h-y*sOBHttg5b`8;B4Y}2CToe zNub^I858v#)x3}uh{uID>sA(%bcks`!1y^4$FHIM@mu#l*3<-qzol*I^Ti-o_@>1^m!Ws2zgK^*5euUHgrv zIh#ji6uU0*Gp~KWoq#&RuuaLy^1WVA=h@=l?wgwSa*Ckh9CrtF(JQx5XMbyC?v+M= zw93?Mtt)59`a^<$Js67Dn~w5k^C^CxfOn>pQmk1w+wD;hkdoRWNAZk3l7hmck zB5x=Ob$#MRCe#Pm*w{+E%#j{TRD!Oi)UX0)BbIQMnj3)x258h~Kxvk%Q36Db?STZw z4=3!M?%3VelmZn)wZ~Ri-2>3)K1J~v2*&(Fw4lGWZ65QO2`HlFWKiFw!4@93kP{3q za^ik|Xin@BV(ceiU&0~xC`3be;XZ339BL(jWtu-mY)ucs6nrQEz~Nzo2=S~yP*)R# z0Qee2E3^iUYYIRGS&a)d=xOM2--Mv&AE#2h;w1Mxb0_yXry?AJG6UD<7g*^J!e<gyJ~}TDt*0VQs#%8%Nw^<0bZo6RP^oi&NXPEtkSI z<+Qj+{H_7BxW@u!4dsV2YhHcc5T;vo0O0VjL4!V4JXv7){xsgo&``6rbNc2qnftySNk?C!8gIi7t;Q}mT<5-f z;Ye-nYp&)|nKQgS@iM2WdlOK-%r+(e$oD$;<=NukF@9 z|IPIy(nph3_+sc&4Wh@u?`zSHS~>XFUL zp5?s@alMiI6w=!c2FuoZa~AtcdBuyG3l=Y4q8Bx92W@fD9nIxC*+;qws*SUE1PA(Q zb_)mH+bC{;s7+}#aPGxZ(R&K$EQk{};VjAX^Tm(4Wx zgvnyB5CQ-k9yW+H#}WwYYJv~|pJ22?6YMGW;%-(7xsa4xSdvQsXYt7YqIaf;N}u>t z!xC!C2jF4%Q3z(yd+BauQAh_S(S)g^z^IT#2c|==(|{`P>?SlR2@jyWkZUL}@8*Y= zmvcZkx$n%6uMgsodkCVLZPrAn@<4@c|4wXW8(~URApqFM!v>MEErF1x>AJ!b)@<-> zqZP`wudZR+TJBEf)LjrZzfOFrVF_&eBBJ2F0ztMt%*bFQtFX;KDlMfPw{}=Rv-tYj z=~y`tU@Nj#J42LX?F?5R;0~sj-1DFVGXe zf*@gjLL=;I1brE=MogZHnGLQ+Jc_FkgR>&#JIgUaR%ExKG|~GgO+Vy^mZlp+2z2Jd z*0q_BIiE)*thXh8=Pc#F26aev6acOB97=YY|=NK>veN z=$=1Pxm}hB?x{G2sl-&tOsmmfvrT^~z-fr?dE71wyMl3PR#>X~5aJf;udz?xq*(j) zYU+TRx?8rSusTrNB-OMjZj!3$*$htiz2qDT(j5pmCus=y+xejZzcU2f=iCM`&21?5 zRaO_O0*PWxBwOxCV$W}jO1CNg=eC3(jW*_?{?@2+Y!c^&IYEAlx_Zm40L{Y|WDLcf#ZcT; z2?ft}qr{x1P*@Xj;+>UH?8Gi;8WN`DK>{ESJZunY92CO&I7sXNi@h@sw4*56J{JOn zum};^m&lf|Z(-jI5QI!_5<(O~1Oh~Envg(}!y*A>74f6tTgaxS1Bn%qBXo_l6aRdw}K)jd7SX&*3XB|7r*Cr5lg ze5*r0w7D0m-G=6k+th^mVLf@?>R+wY4>lW{Uk{YeyhoH$qsXkdK1}DtIz5OpdWOA= zY{+_3Jl8p-HGTHfY5pW)7xOHRU0)2HXSds`&D*IB+e_>qv7^LJCfbIwZS6yU?$h*@ z+>PRfW4(K8H+?OpnKYNWH`j_A*1t85M6IE_6Rr94HFBs?D>_Yexp`4*m?+heaecMQozQOUA6J!$#o$QY%o+F9AdlNp%RSilMQu9ftsUpTLt@dKl|Y_7}+nS zwH+mO*576GQDGo{qA;f@-%IOUNT=wZX^riP3Ipd0h4ik>H@a6iXDZc@s4Z~4&=!a( z%Uvi$UCzl*87KYi_IovPvao~KKBx&N-Bk0=9W60a;y4o`;}qK(85<@s1%tY0APYF@ zsYG2}{&X$T>QWdY*fG zcj8{+wGV2_y$_jp?xPZOB`z=_?wxI0*f2CS4aN^wI?NN*(6sZBkPT zw^xAz)1%!{Mc`DS!hLKh`+A>0I^|TN1s(akO^*0^+r1s;ZEfy_dD|Dw8+W4#b~+3F5668quV&=3CHRbOm7 zcfCzssRy)|)1pg~;Op%2E*;^6uVYHV_uuBb`MImUP297M8zNS#lZd%*nkj|qYckpD zU$GRbiKA`Ze&Xo3{!>1^@SgN98II!iIofln)kTHHhUGi8@x$_SicRuk(f8RdcfX0Y zY8WFF4}oBpCSel6ekk2OOj1)9CMmE;Yj;$Uax_uFp(#Q0NJk%Xg31d|(C;^dyVkcr z(4U$s?qL&(`j6yZ^?F_&Pk&2Q-Q7rSfnQ?WJ9>8?J<(rmxU4q#&j(`W1>$kJ@dR#E z3PcQkJcP(yRtf%7a@_}CYU+Y71r~hmjw;gNCn`9!Ciu~ZJoxg$2mihf!A}%>-$L-8 zHCNn=CKUXqA&aXK?Hw{|&w-WeZqKv*I%s-2@tN{@_VImFr{zU{Epsgv`OS6hL_dRT z^Ds4f6*Xd2iPa=lmsrC@TU}e(KJ@3=OY`b(S_vUyElN(p>79Tq|x^2iG_f zwTAvqw1)Rwb<&DXQ(bOe)Jp&7XT-U$`ALj?Y4o-5)KtfyMXh1}SNO^~fqdvfB??Wi zq7_x9=h?ZV`IJ)TyVZ)$d|s)p^5@xCH_a<6{`>CB`JX>DvVmE58%b;?v9-i@CX^KG z*w&I_KxH&(o-NB=C`4V(Nd?fK>%6^*lZ73;_CZZJ>Bg9MZk)t^68oDF z8TYcSk#YX#*&6BVZ1OWc$BM)wNmOr;tUwWZw4-OG!jEO((0 zbvgGGJ0P#Q7%L zTJQ<`(6^v`AT7A2(t-mtvJ$a!MNVprpV4Ae6mm$RNEC5=nn0-{)R;C|i;6<- z6pBKnmC;703YT)Ct94{f@18@#Ge#RI?DHW%yM^`1>1d2;`1hy-k(zV z?Z=SIIho(~RFl|t`;(r8Rc4iXEzc^F&pJP*U(BkMNtd26qm51#F6E?9 z13f7&??j43zkG=I5$IMAv*&&w@t_Hj;yd!dej2m1##hO=wkMwkP7QaR*MLi-P^_>O^SbXCl(CezC^R?FYsM-}fUz%#-j1E(!-BC;E_CgC?C38(GP>?Tv z$Ps!wd!WPOhj$Of-nTq)-Q#A)J#9jr^O$Vb-0P8MY;#}K%xt(zV16^3tK-bZA70FC z{%ZcjdBM9vm*q2?7v=d&5`Q+))^9J^hkjmAK9Cf-I_W))!Y}LK_L)uUew^9lx3KwJ zBk09zMg z3t!q|B+(t*m2UF=U5vy-E!1vhiPcRgua{Nu{k$e&Xg;s0xgjE-*C?+_eU|5xQ$|_W z$K;DSl@iJ4RG8QBm6S`VE;N}!8H~D8F2(%p^M`g+gqoa4N_0=8&vY8X68*l>MKsq@ zAUBZMIFIJv)%uzRh%~gtomIn{3#RiiU|y4ad2WCEyrw@n6a2i5XsV1hv-z7ger9t( zrMQfg%iBn7i{F(s{h~aU4EfW3mA)VRmYX7_4CEjjANw%YosF~R$ zJv(e5Gn)o-IMqUCHeaaa!GVDX`OId5eAIW^#usnE;u{ZPH2Xr*`I?hVRJ&s5OH)mp z(P65zJ8B8tUTC4KWUfgC3i6qa9Pu-o2RqDcyn872zJ;01yVUUam{8|TlFgc#P1-rl ziCUT2ES=1UYLoR%{%#+8y4tVp_@~9bTiK!;q zy79gCp`Rv{4MMVk{%RrLA% z;B(G8DRIZwxqBS$8dc&U@a4;iqvFd+!O=d{l=$LRpuqIVN>q_j&Z`I$5SeSz;!XH_ zr{#!e-4DdNg_91WTzG7(ty4m-oo;>L&NiW3J6T=V>R&ZsjHVhj>nHh}#6;^-*W{FT zBDD2zmAdy&|cB(oeQkF!hCU`HzC$s zCjVOfYgPg%=J3%lLT!)qJk<7rG4Eb@$D4oKLU_c8w%%YPrTV~TOc!h8XG|xDj>zlm zO}5K@N#bS`4ZShKVpu*9BHfVmiAZ^i%)~yCzM4K6fhJQJjYeIeX9`{Pj4U@QLXByY zwWug%w|5Ffve;P>YVzrlTIZ)rpKt2uile^8#Vq;`v*+%S_?8Lv_gCdXtACxw{?@BC z{AIWfo$fBNvd(RBH`ey?DWQ-3y>L3Z?Or%f9}ArJGUVNu^rj7H?g#SKpIqyhn!xeh z{9ZVHoBr#)vT1TztCVQDpXBj*z{h9hn)u8w6Wnbp+7O;!f}ue-)duI(5S)C_*4Nyl z*sq)|#1O?p$g7)@I4RQ~E8-+Ib#amc3$=Df6@ly(6&zYqhN2I78Il*i42^AKXGODj zE|j6C%oq0u6QbpB z^kMn?afugA$mtpKpw+)aX;C#De%sDFD$Q%SyV5tLzcfQEzY?KsaHLajHo9p_YxQ=T zHoo3|FwiZpxBswR?w=BiY*15g^C%iw$_G-~Zcdn|a4l*YtlrL&wLbLK_J}l@!e}(= z3KUFq(vq9|b-wpYb{qXFyvmuPHhQL9-H9pQ1Wb_|=}GEf z``;}ivAhW}Wsr_&^>5wClw{$eD~^;;joisr9!r(y7Mm8?lg?94m}>FSis%~VL7WR+ z94L`jAbs7fhOdWK;tO*1axYGR;xDllAFV4*wd-g zTlsyn?=?HI;#^zl2dQvJ;8CR?4wrkXIPnmIcx$B}`gEA@2dOFbgI9q9)1%!{McNOE z3fXE>fr7jr)7pM3~Fwa3tVhm|6` z-OYbItF+pCgPDy5>Gn#uNSnJ32P@t3MR}`&6b~V$cT~FNCLQLxMQTdj;#Hu)^k{cf zk#@Ke%c8nDc$Z48;b&7#i$P$?vg!ZKvd4 zljCZt_7cu}lUC~N`I|bzT3O?wx4Ue4^2~a?i*$2?zeqy&fO=| za!z}3?#_yHKhhDy!^*P`EA@g`z}NF711X>bvd2W~v33>3&05(+&8s`qdT)R^zt;}E z-9u(cynav??bR>7Gt^!!qlLFKezB+eQk+t6TIUY-Uz|MKckNwi^u+KadcQSOF?!!E zNAK6RuPu6y$u~t$dx_p%mFPX8BShfhHq*i(yVB9W=qQ`x`YROui|;YMw|7+dmvgZD z&0Tv_MZ!dD2t0hrG)HHb8=OBuW9NJd`g69^JumSm6Kw<1v-Y9C%u+tkyQ;HLEpSRcG%7@$X;nMQ3Mriap`lzM9FqCY+R$Yx zOO55^w4^P#C(B(ZL|uNid%v;3-?e=Wt2iBVpa=;5`|#!^p0`H)Q%zrNxQW3M%a~AN z{7rtf`qv8lR$7djL}u7)?TkomNuv8Z#ne)3Jcs?~{;iebuutG$ca^}A<3YPm;5X-f zKIQ#fshq&UYfTB-lh}E$?Ac&OqS=!V^Vxg1%?6aEHWPbX8$T2KUO83QFvHEMs=cJ@ z-IY{b%XU;!H8F3MWU)!3b`Qykh96jR#$l0KB1g&2jZIn1&Q(Ho<|_juY^U4ML|bAG zmk%l^@eszeA0)2ow0=_^=Ce_1O8x0opuqHKcT^Eluuu^=E^|#<(2-AA)Qn%S$paPMmgB{YhTk&naO zfi}=umg}&(81cD}krenC{kE$Zc?0yM*Y0C9!7Ql@$I4>cFiqP#X854p$B*5!lvVfC zM(O(@N_h?)VD{XB7_Uq% z`g2}JH}V{kBR+?|*j)~_c^Y!)aC6KZX+j-vu$;BEwlYZ--L@<$CL^KFF0s#~QDgVn zqtp*yukDBXD*bSbd^i>#n))FgLJ+@R>4#bJ+xLUi)b)cDSU+fYRFU?BR}uPwqigyh zx{>#T9P#~dU3dM^=4t4M_nBkv6cg%)mYlWouyQ~6_;%=t%CvREzU8y;+3;e&%IX#Q zsp>JarcRqN-48M6n4vhtJR7>D?nf#=AeU@Z;q>8qA^sz>ksTAi_nOXO>Akk|e!U9c zPK(^RvQ7slKm6G5qm)g@EoMn|F_8O6jF11-pwoTKS2j|VjX(j)18dU0InEZX~VtZG7 zVa9juXUvV*wJ(RRwW_7IU1~f1S=D8DTS>c1>_b1dFCWOX{`;ZxEeyImbcGHc?(S4` z`<(QZ;+*Tkq$MDuy$*`HLKYUf7$9W1Q4wlPTS-xv%omE>_q;&U5a47cHnz)=Qg z=_~b0t_w}3FdB`zQmS}e1YMRJ6`{to$y!vDQYCjPr;3br)`h10Bux$UR2knLRT8g# z^9!nc#~gIum-wLxQRRPZYgE~=kt+Tat+u47=4oB5)Rttqi^Z;d6w`_s5y>7^aj02i z(PSqqf|+8!juwWlTP?}lPjjDd_Em4q(h}l?iTjM3l(X7bwz`+qmM>@I{riBwY~gl#`Hrtfb*uu&6P;!H3OY3QPnspb&uH*?$;8(!R-pC ze`OzfPS+d_hklq4Tj$KlFnB8{b>=i0DrJS^Le+5)u;ZDB4V9~u>+&a}qHM1_Ik ziNa5;Zkp^D$U1pg_-9(xmZ&gY-3aY1tv34W<6-1l)PC`g$-)DEd`kD~x_8frq?guJ zFX=T@ehu`WX1DG9;z&NP%)@|=je|Sk0f%=h6mK7CSm{?RB zzc8_0Ag~Y3ingaYG}=pOepCs~N>be`q;LDs>_=I9lYONrE^V59T-LO& zC@!nWnq5dP$EA3aQ+sUUYmt@YngSJHo$KUD@sprGHfqItOhr6@y@4vQksTSUz{Y`J z`Jgu3cB0FTFx0k$vVnc*3qbima^c5GXf^t8qJxLKm&Ld~^p&EU>vGa$4n}jsV88F&*xxFOb zZlVJN$_LVG4^|kkkIX~{NMCKdrOEm%8jZSA26$aCfaB{J5M45DvKAGk3`i8oVxvce zKRE-`I?sTI=gWY^h;Mc=of&UN-6V+vOo#!Q+_0C0Bz<1TfcDy;xr_$5QXA4?wHUM* zvhs##koGI+`_hn_c}+1m5^+Cv$2Qpf{f}nW{Ym1_5{KqZ`JKFJ^{-c&<%}Q4x%n{7 zL=s}xU$8DucHC(;$TJB1pOv)1dYk3!*3A4Q{U|dUE7{1vV?P||du_wPR0UcirS>u$ z{9nRY4Q5B{2tOR?m{MTn1||OXSDqGeVm66AP9-isOToT!cH#Y{iJ}9wKH=%KcMpr#ls|D)YXR<)Y#i522v^ zBnh4-!e{6(A3Uik1<$KMf$7ois3K%}p(4!iWUfgII`VnC9P#t?ryKgA&<&rxdpV!o zxn{l!egw&K`=vAP=^k{cfk@kdF5qd)AnzW!J?+H2L zd*Yc6J<;Y~=!wsoH|{DE>WNF_dG!Xz38UVz-{`J8!d`1s^h6vL{izd!!$a0FcGu}K zGmgsp<;!L?_RFl$FE*&FIa20sl#e&zW2JL$ki(qvmk)GbvK_g(@|EAO=o`MDbWAD2 zxi*YiM@6$`ZK2Clu)Vg>W%8j>A?i%4+EG>*hRQ-A86)$ZwV}&YmKvL$8Ea2s9ER@T$f9YE*B1)exlc|I`Fhm4eRAi7kbfZ%G>oqSL@0`Z*W@id>D{kwB{KmRxHuC{>gKDA8E?4q9vH~WJxlNyDAwl0mNx5N`FB zw$i_qACs5bFsc1#gxYU~P+7Q`?R0}A7MEDUL|XzbY9IOmpyp_|r0vZu7o&h)xVp5y z=0^b?Q|i53CFYF*We3VCL)sN8-Qt5yU7!DQnt7>Kbeihetg_aSbcxpdjf?W7(bvLL zQ>|c?wT65vw8AF&(1l7An%+`GL#h=jL#oMqw_4GeXT#4e8~ponZ)nPf!WjPh?ki=3 z8>+^yB(a*r+7j!UP#P?2TT6pY+S8!mus?Sy-*i+n0_9yAl7JCt{+Un=N58P)NC{xW z(GYF?aCBxe99iZo5jL^CZc~ZPBsQ1WLSicu9a5t4ds~JtlN`TD2ByK593yRSo*Xhz z+mBLhbf|NxU3o~8igI++a8eaC1L@Ou{gA0+O5L05#RsEE*^3&zp&%4`L(a;tM%5@b z{jq^HszW&_RKrNQ)LAcDO}PSBqc@a+LT|_r+#K$E* zA#uKm4%ylGz4P?txj_SY`bd(eOIe=Iv%Ptq%1UiFOTV$D&a(y!A!92RP?Qegp%TOv zn3+o38!UvxEi8nZAU7MEQ8dCV6#;HA60*230zu_r7Z%WOX6V8O3n7^c3nA&`T(?Fr zVU`+6C&GwqBD1AAd1G(#>8+z2Mjk|-Hz%K&e)hr$TWkTjfSyG*ERU1(dYTHDWWCQ)0s zx}8tBGdnM`{?g_Si`%^@EsM48aJbi;iZh4LhM7aY#wuOzJ8HxK(uT?!>$lY))>z92 zy6@T!`tp}a3DP3wefox|%5P6PH=MYKz(2+-8j`+y`twZG7+zXLk*1T~mm{8;g%kx(MN~OdoJzUTn_YYL! z{#b3?udl>i-*T&AzKQYi5Q6qtCGKm;WzTr2sf)W5SlqQcsz~GRRfM>6bWPl&8+qL2 zh>!cr9pc{RUWog;=8YR}LUCV9o>$*1vCp`y!{x<5_j4*?|8;HHZ>WS_-*Q`Ey(#SR z5MuW0O4zrO$3E;*Qx|qAu&`@)RFQ_=s|aD|=$f!cH}bH{5g+zfI)uH=y%6>t%p13h z359*6Ja50QJ7M(1E&~6-O5lG}8~7V5f!DX(Uf6F6d_07x{iYK5z2&nHywubMUJ5Mm z+8tG-f%hsx;5oV`@X?Jt@N&cl{_h+(9_d6!~}vVf$?*@(0UlA9<;%i@X$AJYj;$U2HvX(f#>L&z(+Uoz{?RI_)(#I)UIdDDez6r29`D2Iiu zLN%v1a->_mXf@@f-V1A4h29Wl`7j{WD9+ESMGU`6{aDjkbzzZ~c)sMRrBxqRe!gf< zxEm#Ik+@CbZi#Q0P}*H>TT8q0Z(i;<>YedEradD{Ut&>ne&I>fKS*DWw^8WX6)MhC zRtuF{c6#?YZT#uo?aD7btzvaTrTbpn<-RX*pNY05<|4Jq2a-!qCAp+~>VF_JK9{7d zwkxE@dNf*$ibC=fibA*YmAd4KE}23ZjJiU$6uRgz8Etf`a49E*T1R^H-r7`aEBbw- zBO%;R%%1zX#G@wE_YcT}YRN77-akOlZBGv#HF4so0}cuO{kz)!zQg+4y<52)yZkA6 z^gD^CajUVKmJcMlzpHfl@6{mRuC&V& zr)0Fzsluha%hfvH<+pX}@v3?Kp0r_#9z{Oy1^1lnP^M=CG0~#rj`#R;7?ar zu&fUDEReD^;O8&8h>f21=W>ls`mFb&>8LTKgVz}7z*kZ_q`J^#3S~L!O6ibzC!?u% zC8r9PayqD)o({KnqC=wJx4xjmie}HPBC)0k(cw+RwItxNG8+Gk zRRQ(8RsChO@m2pvq3T=3Qq|wUcDdmaBP2F7(SZRqN9VTI%NEfE6RRy+ao*U}2SQ2kv$J9v$=hq!?+2 zHo+9;ER^orj*(XD+$Mjb-oVE~Z{Q;u>i8HPHvKhCjj97P3)L`FE;YJbIBd$xA&hzh zM=N^eN*8+3YRY5RM7`;BJ_)hB=vFUUO}V-y>P@HfiC#G{AiZeKvrCllbNOF4WmhFU zo+(LoF_+)poN(`!I8Rw*TBeDXwpm;MoL?iQ6r15xQkb6F-!AMq)MH z8MnH`8WL+ttR=Cw#4w4sN~|NXuEcs0>q~4PFltz_R?{GtaRMPQs^g;I;O7U_}q7#zU@11 z-~aTEYiqdgxGO9k>bOf47rPwL-f^}{x6fV&q+O>cjg{b~;N{8fl-ia#u?ocG4kKKV z++l>{E9nTA>Ozw#l)ak(uG{EN%MGR~v0=D~h zFhYmb24Ct-YqVTe7-*sMkX&I-3%*xJi|C(eRa43e12+nVa6{%B-7B0km8JHEd2XRC z6fRlrLLusM#yxM0^QU9)X__z=cJSH9E?ql49%f!jT5mfu)J+oTX@}V`=o+^wY)$)q$^tYI|MvRcy&_r7vZeSn!Q@8SgB;E^^X~L#DI3}L-mHI8$<)q2F zKN^j?LZ)fTk?YDiz*o{tOLd{i6v|-K6;i9vMeoXJqf>=TIeXMVKTWu+(=@^Rihked z$R4+~*>l@T>}*0r*<2oUGfn8QP@A`YK9neyY+tmxEEd>|aT9I)jByoFK^N-sNy2E` z<@ST!xg*J5Mj8pXdG~wW;dQ~yvBN`cJ(}W3T)=iQ) zK;mE%BHmcr8u3QVw?cJZza0SgDl54!)v|C^V_{___eisLRmLvXSNx)!RF*n0SLdY6NKiIjs0%4qQOCE^ zd`s1#%yh=K6zW2nRn*CPqj!aGrc>=G)P?LS)P;$-YI2kjDsi7Jkq~@w10_N zt;6iW9D6@fg`k_|1f4cz_8~^nL-ONT%KwJ>7YTY@LeTu2@(wlTPKmoD?w0tP#62e3 zsCv77=&9QAd-S7uRghL?;G6ogC#{s1Ix$!5q}IsL)_`%}`O-WhAiKO(bNs^Mm<6iT^{ZQgTiH9T}k$B96 zQuN!lwG`d5Gk73Y1rxP-n#h|*Zwp^dcMMvgHe_F+7FNlNE_9;N6f0nb&X9G5&X62(pj)*l z&GUlJBIkNP+>{rEHCo{Nu#^|>FJ|7oBC*J(CKi`i%7hZ)Pjao*ziBBU;&?TnWEjZ! zmA=+bcd4|EV_Rq(TiGs2m&P%BDs}pi)AAeA*RUqU;q3M#b@I0~yjksBQQ|EUD@m*@ zv5Lg1Cfbr{sD0?iwE2J8t!4+)`{rwrQ+oD%EvfONn+_^9fSA&cc`V?&B&n5mjTSFD8WE3Cj>`PhXC449pAUsgGsO5FsZeI+KE=uo*je$VY^U#4i8BSq2DJGb6pd-9^CgKE27x{W?fURAV*dR1r- z#RNTdB`Z2_R%)7EF%YU%VSx0LdyVcF&YOvHP%GL)l`6D{w2)_A7(l&QiM>`a5NcFm z0Qcoox7yKdCd7$~_E4KD+U3lEw4>Whl$jICQlUKrN3IOq0P6E{0X6+w;+|`o_7rxB znP+TUxo`(5E{94?k(eqmU1FBR@g`I!_P1@@SwZ_GsM{Jb--!^jpQUVa)jHaKrF@pn zekP4KQ8y3TO7oyq+U7x1r`y^6k+b~5)4Aq;oCtjZe*4hRhPu>1VKI-3!lF^DUuyhpNM=gh$W7p)acurh7A8V&H;*R2T* zn4L0b3KOB+R7}XN0hz#n*@?6)OoW!U*S#^sQ zY1pLkb}RIJWYa095%%LTmjQ=#WOO@-R%+Ey;y_skjh z1Bo9?JS6dm#IGfuFrhl}E!$e1*rHS?;#6p$HDcg1q426sW<>cfGuGCRk(wB-+B7lB z-$Vb3wImOsnzOXkoPS86pD5{|5_586PNs~3UHV)x7D`aX7&_Aulp4d7S)6}>D#k)Zsu+{! zjV&wMWfnX3M#WgDPKB{h{^WNT_ONC~n|i-uFVw5T9@S7TcWVq&X0e896=R`%RgB5i z0U5)TSuFFqVk}g%!dR$ga&+MKux3VM9#!mx8CPMCNXo}oX$(_lQAt}d#>C4O(<;W~ z+iNj~DYIBk?WOW(ZVO`}lJe{|+rwHuOi)UO@KV%On`V`T8wA)_n6_aeUu0W+GmG62 zi8o2SSz;B5H6`9Ev4OSt|8_hm7I@s*<@7nm;=R?U5VP_dSdEQ#LcuTWgwV;U*J27o zdHE$_DfhiAG?m}N10w44xVHSdeH5>85)&o%mpDk`P>CrLPGY(V)!jX9Yjt<0Qr)ee zvk$E1zQ*Z$?Yw>4j`Q}d{fg%4)}lCXf4sdYpP=TSDDggtlO*OyoGfvQ#HkYRH_=v^ zkGBu~ynP^QsLbYkxiX(AU4F_gTO~r}cATsjF}rlPu#;Bi#187x%ADH4iW!@KSPDC7 zWlrqK-^TV7tubRA!(Z4*%d)oJvSla}Sgj(i)q9jutKm?4Fov@9of?rJ{W!c*aWm!(W z7E>6?%QD&O%kqky%5u_DpTzZL`9tQK`7a;x7_^mH3;)%M!1cXsdTG*oS^%*`)^e zcl!q9F~5tVr&EQV%}tk|Sn8k>b8T4}=|($PF=HJgQn3@tU&W66 z?Q91tW~?dl6+58-7Is3JmA74(!jRdk>Ew#3PzVcCq?jD-)(%$8SeZ-}JE0mDc0wJI zj{`D=A+r~GTQL=?VquCV$-RNw!HOAEepKv)(pa%0w_c?kteCNK7Nv7bpG6^ba_Y61 z!cbm^NmxI(9NJWeD=qbTTvLbbO8#I=cekv>P>Gc!R+m^?Vm*lsB{nsoa=e&rtsL)A zD#y)p%SqiXvgW0wyO;**tGQIvSB;Om*`B<<>Y!R$$biwSqkk0>p|n;^pedbHh7umzV0M~9 zP%#mzYGEQ&J2~3ekfIr8swU7CE1{+qR;UW{u?rIzFgxYGte6P3v@j78Rql0b1si56 zGNxiBRMNr<4UuO9GJyfJBT`mOgbG?QA&&-b0t05JOx;jC3lkw$^5<1r!A4%sNDx1- z9Mn|L3gd*(=S@vLb9*U9nZ$UBNfPgpc#p(k67MykLN>~_R>;b?XxGmxI~Os%SMAa2 zOX+^850>?x`q3{kp>3bP~)<_l8>Wd`t$0x?gMKi|0ER&0jf^|I*rV z3gYqjQ+W@SH)9Nnhwz^Hp-BO>*Os^w75L%qmn+()4^1T&<&l#PQzb)DOQ>;)mfy+I zJ}fqA@g~-ZS}0@LfeoEd=!R;tA732iPPPBs874%ale8;+6-c=+6yy4y5n;Y0ztg&W zlRj9M&)vahojyg3;lIMWS&=wBCE|Fe`WNT$x&6x?KI4#rLvta`N=BMnWMioNiZ-4! zu0opg6u7y#R3VKwVLcrWp`%u+kmi#L_HcKXO!T3tE_%{os$?i?NlBAy+2Ji{;pmz^ zh@RwqAYXhRoL}t&uMnolXu*_E+kfsd6Jp8*+GUSt*Y!a;q#=Jx-OxXz_~P8YW&4*c z{#73H9@UlYo}|_6o}}2zDYS!l4EtL1Bc6U98@kB9QS}S9ukFR0pO@qM?L_URyH`&7 zMfK`B9WmV9>yufBmHNdi;Ol+93`zlAG9p_(M6>ridq0mScgx6K=q0dTsjq`Kn*l}i z1{t!qq!;attcK%?ONiy7t;3XY%AM4=zP&BouGEL?un)p49=ywQ?DN|1bBse1w#4sy z%|2cAeNUBls=oB;>j(DD>6_=K8gx$LD2ZtjM@vkXm?3eD#IX`HC1y#?mS{;FCvm*Q z2@)qtyiejJi8&G{OPnHcs>J&xPLnuY;tUCXag|@f)UPBuewvRTisMJi`28$?%ZVQr z;@5IMqT@a)@iB>yOMF7&Jc+py=SzH2LT}Y|dK0U=P~swqPf2`Q;$n$QBrcV>OyY7A zyrxzqEeW^pu29nMu>N-UVUs{jU7b&|N({kWM2~+z*?UNRma(yH~6o z?tUTv`%u$HNGUKqG7(jz70jz3FF3SH$ml>`yX1l=8e5NKfZM2jEqowvm z6)7#f3TVlpRa!;|a$3p-Ps@WA(sJOmOv3HEE70;EBEn)@cv}8VomNB363e=vZ z6L$(s^ygeUJ*b{@*f%BjN0#${jxRr&{_dw96J>NSlGtrZ&?4 z6m+AUFY9~0tX$F_Vwy;xvM!ab3CYC?xz=h|GmYMTEXS8nW+s$W# z0_;0zOW#3#qb!?4?`U-(?Wp<_!~P}nW3cNL#~cz5AKJH>+tuvG z)s-!j>8lhcjyu^-w=@;?hcIR=mgK$89p9$IhWBk}V~*6MY;sR7oLtgliXVStFGO!X-bQ%M_@3b%{+ROULjWMB-XLt3R z{Q_jpri81FoK;2HIORa!a>k*J^NONNj=u2Qlbfu+uxPAo*)q=-`?AlLeZ3QXTbl>V z+sVPq*@=Bi6H|0TX3LS2&Ej$;i_4hB<#LOQ1dE>WeXHkY>>J+9yK*zb%*@jJ4VMj! z&9#M@4g0ot+bafpxhrhgbKlW~@ezp^B!<{7H%ww<8@=7(5+_RRDDkMBmAaQD9@ckf z>l;rC-7Rs0#K-m7RTAIVajWZV=Sf_pfA5sILH~YB;;;I~@r#-G)tgKVS>D9zcIxl; zmsnO}ro`sj@vy{`61zw&yMl?|NgS+yua=l5ak|8A+Hs!56%w~fOp=%?F=D8Rzpi9r z=*lLxkr=UxiTx$|C2o}Xxx|oFP3$JIpTv<8CrO+yajV1w5>H9IEHPv?6YEQilb9~? zafvTWJS?&7>L#|AI7s45i5Dc!lekXeTM|E!7_x?m`z4;1SY}NVn@UX8zh_EZE^(8@ ze%kRpiKiqcOU#j&D{-yFptVd~In2aXLK7s`kvLW2H0}6|(6=O>lo+zMiQy9CBw7-m zkho6bI}*Q?cu8WezOkaTY%g(u#OiYG>q0-5*joSYA@NWB`^Z~O{8geQ@ezqjCGM7Z zlf zOzbLgu*CZ%?veO~#Pbqct#4wS#K-jSqCywS+RYN5)W0`Kd|%=bi6bOlk(e#9@&+ch zl-NgNvcyRe=Sf^6aj(Rq5~~e2v6saD64NEdimWe6yj$Wpi4RL$A#uCJ0}^NH;Jc*d z(?YjN{6YV&FZ4@^S0sjxFtMJ*9uh}NoFs9+#B~x6NG!6UiH#)QBhfGMO^K%@7S*rp zt|GCS#GVodN*p6`w#26;zAN#B#321%#(ENOmpEJEB8lT9ZkPCp#9t)V(`ya)keDK| z#9$LUON^5^RAQ#YnG&CrxLV?(#Z25UvFIQZ+eq|Dd`jXziJrwxjFcEJak|8pC7zU6 zeF+oeBug$a)s{5TEAf7bFG<`d@v!VZCG-!Ww=8916Nz0U9vfm}iKR`fCb5IW z!4fA(d|YA=9Wq^FuEcE;Yc6AATZ#LnbiB}5={rx+SYL&NY>BfZE|<8?#FYLeCLca~>Wt}g zPo6s>Tf_t>$o7lC2ioJ%bFyXo=4H!qeJmTw6|(HjTqDa?;wo9TGS|tnRk%`?t;)5s zY&EWyWvg?&EL(#sX4#rtGs~9Ys#&%+*UhqFTsh0u=h|7e0awqm;aoqZlmTkgyv}{YRq-9%kEiD_#)wFCIuBT<&az!oMj%#Y!_FPrVc5u3`mhI?tWi8u@ zYirrg?i~HMi_`VBY*(i%Y}sy3*VwYRIbCJTMmb$)%XW9V(w6PXwYF?DSKG3ExZaj! zTye{Kx#pIQ9Vy)$}x|WW*+G(wns`fkCbm7Ddjv;M|-5i z^GLbpkB6~3mqw&Z=>9WZM+ddCK_}1c8BtUthiG%?!nnWQabMbM?7Oqcv$ACZ@yC2sMO57Ph(zJDU)kzPHpMNc&5`alJ6XvN zb3fC@@uT9|QT(iUHjN(^&yMD&#k1-Bu6T9~zbu{|%g>5uv-n~0tmX96;@NRdKQ5jf z@AUKH*$Mo>cy=N`F`m7T9~sY1;%COQIsDLgb}~OTo}I#vjc2Di{oHtV8b3Imoz72= zXJ_!EaG(dN8X_9n$q)nv@$isMv$Jai#% zUvJ(npV#a$Pg#^_Wh+GX{xPx_1bc;A*-#y$ax&&mIz|=6Bc=&_#xw!MGy%jk0mL)` z#54iKGy%jk0mL)`#54iKGy%jk0mL)`#54iKGy%jk0mL)`#54iKGy%jk0mL)`#54iK zGy%jk0mL)`#54iKGy%jk0mL)`#54iKGy%jk0mL)`#54iKGy%jk0mL)`#54iKGy%jk z0mL)`#54iKGy%jk0mL)`#54iKGy%jk0mSeR;uHh~FF;HkK!-*~Fm>QFCJaKY?9Jkj z@_EcN;t#9=!5R>(0l^v&tO3Cq5Uc^g8W5}j!5R>(0l^v&tO3Cq5Uc^g8W5}j!5R>( z0l^v&tO3Cq5Uc^g8W5}j!5R>(0l^v&tO3Cq5Uc^g8W5}j!5R>(0l^v&tO3Cq5Uc^g z8W5}j!5R>(0l^v&tO3Cq5Uc^g8W1%c^g!(MA4T-zh#rgxcJU4P1A=Elt!%|uqkb>` zFv5a3MyQp&B_8v?@fZ-t2(_}6bj)_PgxWOTuBOrO!pg9_rarvr|MTd(g`D`*YaWDG zeq%@AV7o?;UFuHI<}&wDZ7z3LYV#R)r#7E;Ki1}ojx&|lDORg2jFpADyUE?E3uD!F ztX5O3bfP@w?pUcnR4SoXwt76~u6PWHV}x4S8u6Gr<1rwP0WHwAZp}9~CpS?=Z#dU_ zohsN`3w#9DRs{CdY4^3fZa%xNl?~Hp$LO=`-3oRmLv}+ed#gS>NB`XDuF>YER<@2l zTh49=$iB4jWb|KjQEFYqm|A#otYIM4us^2(+ak%c!q zEb{u@?64uxU&o^QzwRsAeAmrg&o4x8BmVxuQSO zRuZxY7e@cz9{nv8{e`C{|8CN6sj#oMfsy_pH&UCQxv|>(yp?Tbmo|sK)$lK7Wn1g8 zBedgTcZxQTxKC>H%T_j0yABv`|2*!F(dG#^R?BhQXyBV=>&qT-TFPf!Z~!qFf;yG` z=Va(fH$u9fa@{X#*G?<`n=U_WqsVuP+<$!pwp9dR)<$uf1%g?i1v)<4E$|H4UJ-b3 zgq4mzbWD|{bh`1Z+f|$A+yrf&cZX^7CpTN07u?y}yyz~_<|X$9ZT|dUd~tJ!1)e)Q zF7Of9X@QTx&I^15c3I#fu`}%cf?;H8S-qLf7#zQ17kK<36YDQ0v7^ zpMKKrc&{SGN7V+OOMX#$2tw*BvvU zRyHo~xhn1vYGvcKXMtYZm=Gc`W~gFwi@lG*qaQ}}Ohj4<^Ltm1XpM-pDCqaORrO)j9=C;6moe9?a;V;Tq>AIwZ8~aot0Md9JAL+F zLzS7teyeO_{%$txA$c}){AL~-1{IHZhCoO7=Sx65UjpL!5)jXqfOx(H#PcN}o-YCM zdOF%qd0^<1+5YLx@c)kS0^CcjjF9Gp<35e%QKs;Xp;`tH~&zFFBz68YcC7{zn zIC%erZ#mDGu$SjcKs;Xp;`tH~&zFFBz68YcC7^SoW}YwMGoCL2@q7u0=Sx65UjpL! z5)jXqfOx(H#PcN}o-YCMdOF%qd0^<1+5YLx@c)kS0!^!q}MB+UTo%!VKa}7rwfuI)%dV!!92zr5_7YKTRpce>wfuI)%dV!!92zr5ddWy+F_l z1ie7e3k1DD&Wy+F_l1ie7e3k1DD&Wy+F_l1ie7e3k1DD&Wy+F_l1ie7e3k1D{TG=}zyn7YKTRpce>wfuI)%dV!!9 z2zr5_7YKTRpce>wfuI)%dV!!92zr5_7YKTRpce>wfuI)%dV!!92zr5_7YKTRpce>w zfuI)%dV!!92zr5_7YKTRpce>wfuI)%dV!!92zr5_7YKTRpce>wfuI)%dV!!92zr5_ z7YKTRpce>wfuI)%dV!!92zm*%vi&2y{^se$X3iKk^VoQL=>tzM5cC2;FA($sK`#*W z0zoej^a4RI5cC2;FA($sK`#*W0zoej^a4RI5cC2;FA($sK`#*W0zoej^a4RI5cC2; zFA($sK`#*W0zoej^a4RI5cC2;FA($sK`#*W0zoej^a4RI5cC2;FA($sK`#*W0zoej z^a4RI5cC2;FA($sK`#*W0zoej^b%@i?-ITA=8iGXi9dRIhez;-&)^RT{(#^Q2>yWJ z4+#E%;13A?fZz`Z{(#^Q2>yWJ4+#E%;13A?fZz`Z{(#^Q2>yWJ4+#E%;13A?fZz`Z z{(#^Q2>yWJ4+#E%;13A?fZz`Z{(#^Q2>yWJ4+#E%;13A?fZz`Z{(#^Q2>yWJ4+#E% z;1391fSl$zAb0_SQy@46f>R)vB-F|dh~??8_P@>D+05~qd2D=n(&xTBfuI)%dV!!9 z2zr5_7YKTRpce>wfuI)%dV!!92zr5_7YKTRpce>wfuI)%dV!!92zr5_7YKTRpce>w zfuI)%dV!!92zr5_7YKTRpce>wfuI)%dV!!92zr5_7YKTRpce>wfuI)%dV!!92zr5_ z7YKTRpce>wfhbQPs04yaAgBa_h9F1_E|rg~~sOKir@N;uxV;_U>51p4Bmw zFc2IA!7&gV1Hmy690S2I5F7)+F%TRB!7&gV1Hmy690S2I5F7)+F%TRB!7&gV1Hmy6 z90S2I5F7)+F%TRB!7&gV1Hmy690S2I5F7)+F%TRB!7&gV1Hmy690S2I5F7)+F%TRB z!7&gV1Hmy690S2I5F7)+F%aAVQNlp*0tD|s@D2p;K(I}yl^qmI*k9~_D`9Nr_{}^v zzJ%#>U&27p3k1DD&Wy+F_l1ie7e3k1DD&Wy+F_l1ie7e3k1DD z&Wy+F_l1ie7e3k1DD&Wy+F_l1ie7e3k1DD&Wy+F_l z1ie7e3k1DD&Wy+F_l1ie6%Fc4G%K_w7W0zpF%qyj-hp;q=D(TkVryeR(g zQXLTd0l^;-`~krq5c~nb9}xTj!52qJ6K+p>Wy+F_l1ie7e3k1DD&Wy+F_l1ie7e3k1DD&W zy+F_l1ie7e3k1DD&Wy+F_l1ie7e3k1DD&Wy+F_l1ie7e3k1DD z&Wy+F_l1ie7e3k1DD&;KyVBM$3Sol1jj&d3; zKyVBM$3Sol1jj&d3;KyVBM$3Sol1jj&d3;KyVBM$3Sol1jj&d z3;KyVBM$3Sol1jj&d3;KyVBM$3Sol1b0A`Fc7=|!8;Ho3oWR_s@E?SC5?FY*xJR%-1RF#kL9h@63qi`n-Vr_I zVef`LPFdKyQIAs&_HNwcl!3jQ^f={T@1{LY+1I;Sk5lgTZr^@~wB99;a;U-L}Um*Lt_>amuvb?R%W^tapbVr!4E;vBxRL zdUvu;KT<7JCy%*B1mUI%5SRHuT;>OHnIFVueh`=WL0sksahV^)WquHs`9WOf2XUDn z#ASXEm-#_l<_B?^AH-#T5SRHuT;>OHnIFVueh`=WL0sksahV^)WquHs`9WOf2XUDn z#ASXEm-#_l<_B?^AH-#T5SRHuT;>;QWqtO9-BHS{oqJ|wQzNUF_Qu@lL9f50x^>UF_Qu@lL9f50x^>UF_Qu@ zlL9f50x^>UF_Qu@lL9e@ff&O;6bTSTLdZ^o+05~qdA{jPQaO+l6A&jRAWlp`oS1+( zF#&O60^-C3#EA)r6B7_8CLm5sK%AI>I57cnVglmC1jLC6h!YbKCng|HOhBBNfH*M$ zabf~GP1kEcoS1+(F#&O60^-C3#EA)r6B7_8CLm5sK%AI>I57cnVglmC1jLC6h!YbK zCng|HOhBBNfH*M$abg1E#012N35XLD5GN)ePE0_Yn1FbW8`K+#KQ5x{>_?P*gszWh z-?&#VQt*5Ci)d0nt?ZbXPEW>k0+CK2(g{R5fk-D1=>#I3K%^6hbOMo1Akqm$I)O+h z5a|RWoj{}$h;#yxP9V|=L^^>;ClKibBAtXRo!HFrn|W;fS`r76P9V|=L^^>;ClKib zBAq~_6Nq#Ikxn4e2}C-9NGA~K1R|Y4q!WmA0+CK2(g{R5fk-D1=>#I3K%^6hbOMo1 zAkqm$I)O+h5a|RWoj{}$h;#yxP9V|=L^^>;ClKibBAq~_6Nq#Ikxn4e2}C-9NGA~K z1R|Y4q!WmA0+CK2(g{R5ftZGYn3{n|ClKibBAq~_lTa%=Hm1`PF`Yo96Nq#Ikxn4e z2}C-9NGA~K1R|Y4q!WmA0+CK2(g{R5fk-D1=>#I3K%^6hbOMo1Akqm$I)O+hAxkGV zbNpr=8=p=bNIHQ?ClKibBAq~_6Nq#Ikxn4e2}C-9NGA~K1R|Y4q!WmA0+CK2(g{R5 zfk-D1=>#I3K%^6hbOMo1Akqm$I)O+h5a|RWoj{}$h;#yxP9V|=L^^>;ClKibBAq~_ z6Nq#Ikxn4e2}C-9NGA~K1R|Y4q!WmA0+CK2(g{R5fk-D1=>#I3K%^6hbOMo1Akqm$ zItjJ1nK7LnkLd&=oj{}$h;#yxP9V|=L^^>;ClKibBAq~_6Nq#Ikxn4e2}C-9NGA~K z1R|Y4q!WmA0+CK2(g{R530XR^nd3L}*!XnfK+*|BI)O+h5a|RWoj{}$h;#yxP9V|= zL^^>;ClKibBAq~_6Nq#Ikxn4e2}C-9NGA~K1R|Y4q!WmA0+CK2(g{R5fk-D1=>#I3 zK%^6hbOMo1Akqm$I)O+h5a|RWoj{}$h;#yxP9V|=L^^>;ClKibBAq~_6Nq#Ikxn4e z2}C-9NGA~K1R|Y4N~hjEdU$7+XA8;1K9WhOmCaHzIr}-y-o1L+Y7>E=DkzycH3`B~7NHGv81|r2kq!@@41Ce4NQVc|jfk-hB zDF!0NK%|(Er5Kwzelw4aPcaT8#XzJOh!g{nVjxlsM2dk(F%T&RBE>+Y7>E=DkzycH z3`B~7NHGv81|r2kq!@@41Ce4NQVc|jfk-hBDF!0NK%^Lm6a$fBAW{rOih)Qm5Ge*C z#XzJOh!g{nVjxlsM2dk(F%T&RBE>+Y7>E=DkzycH3`B~7NHGv81|r2kq!@@41Ce4N zQVc|j?USb%pO9i8QcS3owUkU+j{UFxxsBsu$@oAl86Zjqh>`)KWPm6cAW8;^k^!P* zfG8OtN(P9M0itApC>bD128faYqGW(586Zjqh>`)KWPm6cAW8;^k|AUzgUuYjna9SL z3=X7ZfG8OtN(P9M0itApC>bD128faYqGW(586Zjqh>`)KWPm6cAW8;^k^!P*fG8Ot zN(P9M0itApC>bD128faYqGW(586Zjqh>`)KWPm6cAW8;^k^!P*fG8OtN(P9M0itAp zC>bD128faYqGW(586Zjqh>`)KWPm6cAW8;^k^!P*fG8OtN(P9M0itApC>dE^GWdj& z0it9GwX)-tOdH!TSoik+H(wP!VS%WOC)!H&Z1v@MTd97Z{d1xInPC5%v_Q0fjx~8Z z`!V(2iPq$k7l^5QCUD#77&#ML}dX{SwK`45S0Z)WdTuH zKvWhGl?6m)0Z~~%R2C4G1w>^5QCUD#77&#ML}dX{S%j>zu$kjG^Vs;x!huv45S0Z) zWdTuHKvWhGl?6m)0Z~~%R2C4G1w>^5QCUD#77&#ML}dX{SwK`45S0Z)WdTuHKvWhG zl?6m)0Z~~%R2C4G1w>^5QCUD#77&#ML}dX{SwK`45S0Z)WdTuHKvWhGl?6m)0Z~~% zR2C4G1w>^5QCUD#77&#ML}dX{SwK`45S0Z)WdTuHKvWhGl?6m)0a00mTG`oQI?{X4 zfAeYn2SPK)%v9CjepC?mqk^~}6~z6hAnr#6aX%`E`%yvMj|$>`R1o*0g18?Q#Qmrs z?nec2KPrg(Q9;~~3gUiL5ci{kxE~e7{iq=BM+I>|Dv0}0LEMiD;(k;R_oITi9~H#? zs37h~1#v$ri2G56TG=@|4| z2qKCgq6i|2AfgB&iXfs0B8niQ2qKCgq6i|2AfgB&iXfs0B8niQ2qKCgq6i|2AfgB& ziXfs0B8niQ2qKCgq6i|2AfgB&iXfsW)XL6{QJfK@2qKCgq6i|2AfgB&iXfs0B8niQ z2qKCgq6i|2AfgB&iXfs0B8niQ2qKCgq6i|2AfgB&iXfs0B8niQ2qKCgq6i|2AfgB& ziXfsW)XF{-qc}ZA5kwS0L=i+3K|~Qm6hTB0L=-_p5kwS0L=i+3K|~Qm6hTB0L=-_p z5kwS0L=i+3K|~Qm6hTB0L=-_p5kwS0L=i+3K|~Qm6hTB$sFn4{C>|Z72qKCgq6i|2 zAfgB&iXfs0B8niQ2qKCgq6i|2AfgB&iXfs0B8niQ2qKCgq6i|2AfgB&iXfs0B8niQ z2qKCgq6i|2AfgB&iXfsW)XF{_qc|-_5kwS0L=i+3K|~Qm6hTB0L=-_p5kwS0L=i+3 zK|~Qm6hTB0L=-_p5kwS0L=i+3K|~Qm6hTB0L=-_p5kwS0L=i+3K|~Qm6hTB$sFi&r zM)9Z^MG#R05k(MD1QA6LQ3Mf15K#mXMG#R05k(MD1QA6LQ3Mf15K#mXMG#R05k(MD z1QA6LQ3Mf15K#mXMG#R05k(MD1QA6LQ3Mf1p;q?M7)2MO2qKCgq6i|2AfgB&iXfs0 zB8niQ2qKCgq6i|2AfgB&iXfs0B8niQ2qKCgq6i|2AfgB&iXfs0B8niQ2qKCgq6i|2 zAfgB&iXfsW)XF}_6Q>_BJS+RS;R}1;W7l6kVHaZt+rmTd!S>I2_RqHZ=Mej6uA+XG zZmfSgJbswHtCgJ}KmMIQ*2B@+ds^8i#_6f(1l?h?R4h<`u&FP1qw53Ki>)59|L0T2lR%2I`l%d z;dNi8{7+h)yod@ITX-(3WwY4BQ&aoKD$1FyISd0X*7ztuA62xL8 zh{Z?{i;*A}BS9=if>?|Mu^0(rF%raLB#6aG5Q~u@79&9{MuJ$31hE(iVlfiLVkC&g zNDzyWAQmG*EJlJ@j0CY531Tr4#9}0f#Yhl~ksuZ$K`cgsSd0X*7ztuA62xL8h{Z?{ zi;*A}BS9=if>?|Maqp46Wr0OWK4DQ3#G)ift8>{gPE)Py*w^XJ0Gc&rr#elUveTUA zOWEm8lcnqore-|J1A6xAg*|ICwR4@8FS8Fh ztzBmQP79aWhn-d}vya4{{k&quS??Wj4F<$nFX#=w2J;2A;lfpooiqLe*m+Hi_*Y`Y zK|~z%hL8BQb_y_YH4FGLb_(!CHRwdWC+pZA_dacB*~6^Y>C^D8yxZ*4>@b(Vk8Z%r zWxCps&2qZbkhPqyGi1j(U1a#TtdqUg)8gwDiDMP5R*ySlQ`;VI@wq{tZm3UB?D1#K zC)uYr>QmD3+DHx%S?4VY5mp-FP9TTe* zh$;no!%yz-j%}D8+W?{spf`LQz82eXbZi5NHh|vnZMa8mxLEo8%~)r@9^2Iy+XbRs zpl8;x(tJUW`@Oao+BwBHf}T>LxWp9u4{p;>?@>c^qTrEE4m?6Jd;d+P<&A#s@GqSw z{g3+U0SkWbr+WNtVUMey^UyCW;ji-@NZ(Yrl`vzR!mO0>h!W0c|Bt=`E-g0?!A87nvc?BfiIk^Tt$N2Y4egr-e zBM?ja?C~*g;+(B2UWB3Y&Z78V<()+~lls4-|EgF5s=Twv&3I=K#mfZs|9WSUuX$$? zd1n!MXA#A#2K769+R@j%!HB%Uh`hmwyupaP!HB%Uh`hmwyupaP!HB%Uh`hmwyupZ8 zw||Q_82Oqv7?C#^kvABTHyDvO7?C#^(F@w`y|C%RCT}pZpEnp$oG#YU@CGAa^9CdG z1|#wYBk~3#@&+UF1|#wYBk~3#@&+UF1|#wYqfp=dLz^NGX;Xwukx-c;HsiXP*wo*| zWk*wlOc63g$P^({giH}KMaUE(Q-n+rGDXM~Ayb4*5i&){6d_ZDOc63g$P^({giH}K zMaUE(Q-n+rGDXM~Ayb4*5i&){6d_ZDOc63g$P^({giH}KMaUE(Q-n+rGDXM~Ayb4* z5i&){6bbd+KbBYDKT&3CIY(I4eKh8a!_^aFzBpWwLlMOXt^NL*e`@_W-*z?WEPWIOlC2e#bg%K>R4vTEGDy<%wjT& z$t)(bn9O1_i^(h|vzW|cGK4seeM_+VbFB z7Z1+s%7gRC_TW6MJvijS3H9BlVxNO|E#L9_>4VeVXRSVo{dA1={1=1c)rR!OQI6Ld z(wjz0>uTfNYqPQFuC0$OeI~|y-YCCicy<0?TOhM75E4kJ?>^i1c|hBT>=Wv{&$WH- z-}WKyq{af4Tn6?kuC)9Ue zYWp1B_96RFyn8>rZIoBVyO-Zyo|At$zQ=p|4~p-Jug{VbDzem*+X?mESK9aePWwLc zedPPd_tAns=6$u@uluz7MeY~5U*vv;`tED(`|jPok9;5bKJtB`zWev~efMhLN4}4I zANjse-+ev4Z%MzF@{J`w0^eNnBk-*yKLY<@1Y%WRxW7R($Fr%f%kfmIOF5oLbr0&h zZ^u8~GPf+2KFb_g=4inmF22+5!yfHEko!Oj{yuy+?!!CxC{yU2i{QH<<aU1vb+PIT( zC*w}Wos2sfcQWo|+{w6;aVO(W#+{5i8Fw=7WZcQPlW`~GPR5;#I~jK}?quA_xRY@w z<4(q%j5`^3GVWyD$+(koC*w}WJ=AwUY2&_b8+S79WZcQPlW`~GPR5;#I~jK}?quA_ zxRY@w<4(q%j5`^3GVWyD$+(koC*w}Wos2sfcQWo|+{w6;aVO(W#+{5i8Fw=7WZcQP zhx+cPZQO6)#+{5i8Fw=7WZcQPlW`~GPR5;#I~jK}?quA_xRY@w<4(q%j5`^3GVWyD z$+(koC*w}Wos2sfcQWo|+{w6;aVO(W#+{5i8Fw=7p}zZB8~59_aVO(W#+{5i8Fw=7 zWZcQPlW`~GPR5;#I~jK}?quA_xRY@w<4(q%j5`^3GVWyD$+(koC*w}Wos2sfcQWo| z+{w6;aVO(W#+{6NsPC?8<37{Ios2sfcQWo|+{w6;aVO(W#+{5i8Fw=7WZcQPlW`~G zPR5;#I~jK}?quA_xRY@w<4(q%j5`^3GVWyD$+(koC*w}Wos2sfcQWpwzWaHM`$v|Q zXEJ}W2%fS2XZ&lu&aq?660dWR*9d6Azeezjb{}?V_kr98TJZPbm+d}m-|hps547O# z!>`(X*sk3Navx~H--rKd_hH+1AIN>61%Dr|Z}(xFb|1)npap*)ZfN&m>vkW=eV~PY zAJRIbb|o?-$#@9_0hdcJ`v{%W->olViEBIhK>@ zM*GKiesI*M+qWB>{ann=xXG&6rOM6wziyt~Jh^#t^PxViJKXFS+Rc)iB{xfMmfS44 zS#q@)wZ5fcypIF9`MN4*r6<5q@3#&GSd@j&@ke zFHxuU4S^y3%b0zJlniO8Pj@Oiluo$O9l8nQcGxk-5(w<*W8yZW{xdEYvD-#U5UI(gqZdEYvD-#U5UI(gqZdEYvD-#U5UI(fG`dAB-ww>o*Z zI(fG`dAB-ww>o*ZI(fG`dAB;PE_1BjU+-4;HSbm@?^Y-8RwwUPC+}7#?^Y-8RwwUP zC+}7#?^Y-8Ru7er3fPS6W>Wt#-okE2Z(%2IVJB~4CvRaVZ(%2IVJB~4CvRaVZ(%2I zVW->%!JFBA&70XnecEK08CSHKL1qS-8DwUVnL%a-nHgkekeNYd2ALUTW{{adW(JuV zWM+_=L1qS-8DwUVnL%a-nHgkekeNYd2ALUTW{{adW(JuVWM+_=L1qS-8DwUJ%FM7C z*UiMndz-7w4BujAkeNYd2ALUTW{{adW(JuVWM+_=L1qS-8KFLHYGz!pO=;+@aUSxv zIrZso!~c3q`(L3xZC3tQetxVw|4`0tHlO>`?4Ot0T$Q$HfBrLn-qH;%@Mn5?L7KHI!GO)4pIlHgVaIlAa#&B$P!6HAR&+tNC+eZ5&{Wo#&*5(cWhg}Ew7C4`b=qQJ2$eVp95@PhaiUf zZ)1S{$_DalWXZ3QCBH_N{2E#EYh=lsdWXZ3QCBH_NoW+r|I5LewlSZ2T8YR?L{=iQ5|NdNtVCob zA}bMDiO5PsRwA+zk(G$7L}Vo*D-l_V$Vx<3BC-;Zm58iFWF;ai5m|}IN<>y7vJ#P% zh^$0pB_b;kS&7I>L{=iQ5|NdNtVCobA}bMDiO5PsRwA+zk(G$7L}Vo*D-rpW2l>qn z4 z^hA0hJ&~SBPoyW(6X}WcM0z4Uk)B9Tq$ko7>5242dLliMo=8uj(v!`&ZYFgQ^khdp zk)B9Tq$ko7>5242dLliMo=8umC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW( z6X}WcM0z4Uk)B9Tq$ko7>5242dLliMo=8umC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0J z^=T(PJ)`uLb|1B$$$YC$fz!_A1Uo)4uvUK>^Nl|zI^K1&mVQ@J`EeL=>2xXoTr z#t>Y7yRyvwYb>+xXmSGMYm580xmj@CkN3xqCEsr3V=kqK9B(}ve&1HS>FhT0L$=Z` z?NLpiA9+mE7Z&{W=j@TX%4)T%JKzZUH!&?8A;}Sv;-}5e(wR*rt*@E13XDrW>XoDq;S0&+$`&IrgE0XZWeX9VPofSeJKGXio(K+Xur838#XAZG;RjDVaG zkTU{uMnKL8$Qc1SBOqr4ZtcGFYLrS!#- z(Bfx7l{0}@{jZ9Xf-jZzVF`aAu6%YlhGIz%!LsQP>^U8Ry{1F3_jCyMnGV6e(;?Vz zIt2Sqhv0ze5GdLoUgXOETn= z47nsjF3FHfGUSpBxfn$*Mv;qAp>lD?W?VNDoBE3k?C9bQxi~{E&X9{U95Xyai8v4SJ!wy<$uP~ zVnHDb3RzIdfX4%j>4iJk&Z}5q$AQ1>4iJk&Z}5q$AQ%s89FN(faWg z%Uk|)$@}y>zJ2=*N@CZI^4y!4m-L%;jy5D7fPagX$rEm}Mv?Pla(+zCkI55ma)wOS zDza9QwTi4&WUV4=6vR09`imX*+ts-j`S*yreMb@fNS*vWubu+Q4Pq^)9 zts-j`S*yreMb;{^R*|)etW{*KB5M^{tH@eK)+(}Ak+q7fRb;IqYZY0m$XZ3#Dza9Q zwTi4&WUV4=6vR09`imX*+ts-j`S*yreMb;{^R*|)etW{*KB5M^{tH@eK z)+(}Ak+q7fRb;IqYZY0m$P;ezgqy5TWQ8ItRH#qK3`hFUL{BOr6_JWaMWiB95vhn& zL@FW`k%~w~q#{xgsfbiWDk2q;ibzGIB2p2lC{!x48Q0CkrdDJ}6_JWaMWiB95vhn& zL@FW`k%~w~q#{xgsfbiWDk2q;ibzGIB2p2lh*U%>A{CK}NJXR~QW2?$R75Hw6_JWa zMWiB95vhn&L@FW`k%~w~q#{xgsfbiWDk2q;ibzGIB2p1K;E5242dLliMo=8umC(;w?iS$HzB0Z6wNKd3E(i7<^RC=-**UiMH z_GCvrk)B9Tq$ko7>5242dLliMo=8umC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SB zPoyW(6X}WcM0z4Uk)B9Tq$ko7>5242dLliMo=8umC(;w?iS$HzB0Z6wNKd3E(i25b z<;C8tCSOq#sVUT_`wcbuS%qpMHIbS~O{6AL6RC;RL~0^6k(x+Nq$W}msfpA?Y9cj} znn+EgCQ=irDO75*8Q0Ckrq*OfHIbS~O{6AL6RC;RL~0^6k(x+Nq$W}msfpA?Y9cj} znn+EgCQ=iriPS`DA~lhkNKK?BQWL3()I@3`HIbS~O{6AL6RC;RL~0^6k(x+Nq$W}m zsfpA?Y9cj}nn+EgCQ=j4tI1c?L~084>HfoWr+<%gJ56nkx*}bXu1Hs;E7BF|igZP~B3*?_S2p9inb_2>?5Hc!73qp}MY56nkx*}bXu1Hs;E7BF|igZP~B3+TLNLQpQ(iQ27bVa%%U6HOxSEMV_73qp} zMY56nkx*}bXu1Hs;E7BF|igZP~B1b3E6X_|`rw8b1`m#*9SUEc! zs4s}6#ezZ>6tbX@1%)gqWI-Vd3RzIdfI#**Y{qpnv8i?0QC*}iQWvR<)J5tdb&LPWKx=3B5E>ah%i_}HxB6X3vNL{2ZQWvR<)J5tdb&LPWKx=3B2J|#Wf%F8lK`@_-?8kVAS+EPT8BC-^brHCv= zWGNy`5m}1JQbd*_vJ{b}h%7~9DI!Y|S&GO~M3y456p^KfEJb7~B1=)IEJZfsx|!J2 zrO1w!BC-^brHCv=WGNy`5m}1JQbd*_vJ{b}h%7~9DI!Y|S&GO~M3y456p^KfEJb7~ zB1;ijipWw#mLjqgk)?<%MPw-=OA%R$$Wla>BC-^brHCv=WGNy`5m}1JQbd*_vJ{b} zh%7~9DI!Y|S&GO~M3y456p^KfEJb7~B1;ijipWw#mZDIf9vlbyexq`rzrGykkBvWF z6o0y*{OP!%wX<4lq&3nSX^pf-S|hEI)<|olHPRYsjkHEuBdw9vNNc1u(i&-vv_@JZ zt%XW!HsiXP*wohSs5R0WX^pf-S|hEI)<|olHPRYsjkHEuBdw9vNNc1u(i&-vv_@JZ zt&!G9Yos;O8flHRMp`4Sk=96Sq&3nSX^pf-S|hEI)<|olHPRYsjkHEuBdw9vNNc1u z(i&-vv_@JZt&!G9YoxVMpB|#8M{iv68qMVA#&%{Ve<)sxzTHgzTx@SYlfN6=b!YMo zv0ZN_-?jW`uylu+ymM^tIFnbzcKw-rzu4YsCZ8DF4QBdu{E+KuEmx8&$(7_vawWNv zTuH7ZSCT8qmE=ltCApGZNv4^hA0hJ&~SBPoyW(6X}WcM0z4Uk)A@OC!2BIOl)dT zcGMH;iS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW(6X}WcM0z4Uk)B9Tq$ko7>5242 zdLliMo=8umC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW(6X}WcM0z4Uk)B9T zq$ko-s80_adU|H-iS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW(6X}WcM0z4Uk)B9T zq^D5n$!1(P6PwzT9rZ+dB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW(6X}WcM0z4Uk)B9T zq$ko7>5242dLliMo=8umC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW(6X}Wc zM0z4Uk)B9Tq$ko7>523d>eIu9o>sM4^hA0hJ&~SBPoyW(6X}WcM0z4U zk)B9Tq$ko7>5242dJ2`EY{qpnv8g@TQBR~N(i7>4^hA0hJ&~SBPoyW(6X}WcM0z4U zk)B9Tq$ko7>5242dLliMo=8umC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW( z6X}WcM0z4Uk)B9Tq$ko7>5242dLliMo=8ujK0SQs>6F$J>5242dLliMo=8umC(;w? ziS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPodJ2&A4tRHnk@^>WTD3dLliMo=8umC(;w? ziS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW(6X}WcM0z4Uk)B9Tq$ko7>5242dLliM zo=8umC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW(6X_|`r$-Dut!zD!o=8um zC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW(6X}Wc6e>O0jO%7%Q+u+bo=8um zC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW(6X}WcM0z4Uk)B9Tq$ko7>5242 zdLliMo=8umC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW(6X}WcM0z4Uk)A?* zI#Ew{KQnsr_Brxy7g81}i56nkx*}bXu1Hs; zE7BF|Dpb0%8Q0CkrgmjVU6HOxSEMV_73qp}MY56nkx*}bXu1Hs; zE7BF|igZP~B3+TLNLQpQ(iQ27bVa%%U6HOxSEMV_73qp}MY56nk zx*}bXu1Hs;E7BFIiPS`D3iaucLrtf*nn+EgCQ=iri5#cMaf4^b{&R*^KLEVpDsvqn=1lq$ko7>5242dLliMo=8umC(;w? ziS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW(6X}WcM0z4Uk)B9Tq$ko7>5242dLliM zo=8umC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0hJ%#%8sG+B4wVp^%q$ko7>5242dLliM zo=8umC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0hJ%vh7HsiXP*wmivs3+1B>5242dLliM zo=8umC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW(6X}WcM0z4Uk)B9Tq$ko7 z>5242dLliMo=8umC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPoX|NT2FI7iRxB; z=KePG3&S20W3lq0w9(AUtJB6aE5DZRT&{PYRKA+mFTYkg-DPG~dTjjZkoePG%b!j* zdS5O-_#kaE<4pN~`^WC2gGcjz=uSFxlw+lt`;j~8@KKIcru@L2K0VGq_f|c~x0T{;Y#1>^l?zRXXu_wfI`$ha~H!IWaciZDPdt~{jQ-1Fq%1=$Bk2ig%>7?>)_5YsU z^x~#BHhrM!+NQ0`l&;?!zecTU+ot$sYV~XJivp{5Xxg!9r>46%?cB6W)2>asHSONC zN7J&VJ)8Dwir*zz|1JA8?c20p)Ba5dG%asBu<4+t6-@^>9ny4Y(_wS!(-S>Z$Hssj z5f9a>CdYSQb9^VqcZz4*3wjOtz2S}>-tHK=W8{vJJ4Orsjy-9(V~4dnM(!B7W8{v} zg1=)chdXv?yJO^zkvm527%liacFJ(a4rzCc+%a;;$Q`2vf5-B0#}00HjNCDD$H*O{ z1%Jm*b;p+U3CNQT!DF^8%lsCL-~{9;(?{&7hTtw+l}Bu=Mev9{ZTg5k-4IwS-yQ3x zwUVrrp+5cobex`1h9G}$?=rEsEzd4i8IbAcyvygjpJ^!K<&?DjOg>`YvfW`O$BQcE z7p(TFuOqc}`QwgdMOc)-ST&yebm!-I;?rG_<5^Glf*enIx)i*JP{{P#K{wJ@i*JP{{P#K{wJ@x?%@0O3T?^fj)_^!_}>fLEMhP-=Lj`8kJ&oS8D897F} zJ2S^HchAl-#@+b;dUlQt$&KXQ+W6zSqa4T0?utG=$Igzd zZ)itWa%2tl=`1__{BGr29+j_)&57CVKX24-KQ2^so9DPn(QT3A5=FOVj_VWMRyi(C zbX(`RGSO|5nb^=nlwnp`kl4$5n>zpd6PNx)phKY!1$GaiKdT$CZWd&>WW) zy2El@Q|J!QaY3OwBFEK)?jAWVC3HvTxQ@^rmE$5pch4MG5W0KixO~vvJIA$y?mly) z8P^ZGqjOw5=#I&8<)C{&j>`sJm*bj2mvUS%=pK~gYC-ql9G42Z}>fWJJaGkiHjp>cX9rop39vxjOJ_FXp#X6_38O`S{XBW-Ib5*Hds0CHd;C9HlCZ@ zIsVV4Yj%#u1?Fzog}=V9&8dE_abME&^SohxUee|#nV+FPoo^>=@u6)k?$ZUs_g>t- zmwa!iPcN|3jjQyppQ_wAvFprVv-5kQpSaBHd~Hth7kU8Vb((U@=z%#u9xN+0{rZEl zv<{^=PQkVL2wXTk0w>o;;Jo$-(DV^l%O{gB8Xkei)JNdl_6UUf^asNpkEnY*zwHs~ z(;wPHHfP5F^2#TPD%7VJm!0w_%b_*ht2`zCqv2alYu`e?g+91b+4sQS!Dgc@dULho|{&=S-0Yp z`&<5kKNwU{sNl+1Z!!nLThvRdD-x2eWg8GF}M) zdVTav-e#AwJ*KSNukcMv`boqqr$ca&A+Vr|b;#Dz>T8x(vb2(=l}s^OyP+@iCq`@a zRmF=vOy9YCSzS+!!oS<3dS6pLc}PNix@0=VUo{WvR<-m?{rc}S?VSNRx;@AWYf@-Y(fF%t4I5_(Si-m{v{ZhCIh>Lwo}@x4AqLOw=9K1M=5MndPe z?~Q9e^mzUF**@w0{Oy+Q?%wd~x-Aw9^=CB>jVKRrw z942#^%waNz$s8tgn9N}^hshi!bC}FwGKa|=CUcm~VKRrw942#^%waNz$s8tgn9N}^ zhshi!bC}FwGKa|=CUcm~VKRrw942#^%waNz$s8tgn9N}^hshi!bC}FwGKa|=CUck; z{2YF*IqZ=AsF=eJ+2oKN>eHpePLFImk)1+)dfl+oBic@6r%<0>KkW4IwiDSY)Thga zol@J0>=f$L8|-w^uI0JI>`Z=FY`gLt;*G=iKDd1^`QA{U-ejjQ#rHgLChu3SL(jUt zc3jhveiHKLVGJMG#*mC*s84SxJLMaPa8-J%Z&}i#_BKOs;_l_@z}NHBWAlydlaMQ7 z41KhQv_@JZ<4QL-gg@M^+~_y+9&y{>%JIpm?ln0+Hr2g0$7iOxOLKf+s(W3IPfK;L z&+$>I?y~$_-wzb;%DSz5UheJFp})oN7hFSE<^R;so9Fjz^PJ3cGSA68rv-mtynMK0 zN8KOZ0`?tq|JPzb>Addqr_{ZVT-LV_W zVxAr{lkc`i*&bg`VBYD=H+LAg%`fNgG6bKF5qaoLUNj$Ef46`3OO};?_QMzJ8}8T8 zQQ45ks*<)w&^4OkSzUgY;w4?|5uMNTWF(T`Z@``rF@FzoaMf zM-0>bV|boClb;ydr%Yc9{f$qM{)1tpM$+Ld#vZgmQnUL`vefq~b zA$kiWm^@m*jAyT+ef5_Dza`lH?{UKL>$kiWO9as6t)gN;8hg|(3SAWRWA9D4FT>T+ef5_Dza`lH? z{UKL>$kiWm^@m*jAyT+ef5_FJP@g_JJoL{lU#Jf~ zdFaVQPab;m(36LrJoMzDCl5V&=*dG*9(wZ7lZT!>^yHx@4?TJ4$wN;bdh*bdhn_t2 zvAp(hVLdFaVQPab;m(36LrJoMzDCl5V&=*dG*9(wZ7 zlZT!>^yHx@4}GXy_q7?<&BUg@eK@|}r++dtj*NE+{IYQ^fj*yu_W(JuVWM+_=L1qS-8DwUVnL%a-nHgkekeNYd2ALUTW{{ad zW(JuVWM+_=L1qS-8KLsH*o^CDQU~93v7_lirVE)aWV(>)LZ%CuE@Zlp=|ZLpEm`^5l=OqI+NcV+ozS&@lTX5=MCc|{psZi$Te-J*|)`Q#%|Q7 zYyGd^cs}&W;XB^cz9ZD9PyP1q__Qw{xoP<;SCvmC$KP;Kd}sXAs{F0K7Wb|^<3`4f z&RD;UKAqVVk=L)8G5)U^LuL$_F=WP&8AE0anK5L>kQqZ}44E-x#*i69W(=7zWX6yg zLuL#~f@BgZM?RZz-AwA;bS$!?lMQmRK~6Tv$p$&uASWB-WP_Y+kdqB^vO!KZ$Ws+^ z+Cfe`$Y}>T?I5QeV;3RMoI0>8tP68)^lfX&fByh5{k)@3+Z6tq^f2ibdGp?IS9T@rBQT`-~rKgpluPx2@Ell)2kB!7}W$)Ds;@+bL|{7L>Kf094RpX5*SC;5~7 zN&X~%l0V6x~rKgpluPx2@Ell)2kB!7}W$)Ds;@(=at%X-qzC|=;w z4n;d@&ryzY(q5w+-K4!oIjU*DcgvodA-~sz)I;ha^^kf$HyOztxdFbwoNM9g&VmN2DXt z5$T9@L^>iJk&Z}5q$AQ1>4tiJk&Z}5q$AQ1>4iJk&Z}5q$AQ1>4d?^*aen7`M9$^Nxg0r{ zqj^UqUr`gOiPS`DA~lhkNKK?BQWL3()I@3`HIbS~O{6ALQ>fHrGp?J7O|8j}Y9cj} znn+EgCQ=iriPS`DA~lhkNKK?BQWL3()I@3`HIbS~O{6AL6RC;RL~0^6k(x+Nq$W}m zsfpA?Y9cj}nn+EgCQ=iriPS`DA~lhkNKK?BQWL3()I@3`HIbS~O{6AL6RC;RL~084 z>1#tx*SDHTO{6AL6V0p1SJXsmA~lhkNKK?BQWL3()I@3`HIbS~O{6AL6RC;R6e=~@ zjO%7%Q){xLnn+EgCQ=iriPS`DA~lhkNKK?BQWL3()I@3`HIbS~O{6AL6RC;RL~0^6 zk(x+Nq$W}msfpA?Y9cj}nn+EgCQ=iriPS`DA~lhkNKK?BQWL3()I@3`HIbS~O{6AL z6RC;RL~0^6k(xq%`uCxx|7tbSZ#&1E^?&A-eLuk%mY^q#@D}X^1pL8VZ$$Y{qpnssDUOBs*$|G(;LA z4UvXOL!=?n5NU`sL>eLuk%mY^q#@D}X^1pL8X^smhDbxCA<__Oh%`hRA`Ov-NJFF{ z(hzBgG(;LA4UvXOL!=?n5NU`sL>eLuk%mY^q#@D}X^1pL8X^smhDbxCA<__ODAcEK z-ui}q(HbHRk%mY^q#@D}X^1pL8X^smhDbxCA<__Oh%`hR3YCUz#&t8XsmCEZYKSyM z8X^smhDbxCA<__Oh%`hRA`Ov-NJFF{(hzBgG(;LA4UvXOL!=?n5NU`sL>eLuk%mY^ zq#@D}X^1pL8X^smhDbxCA<__Oh%`hRA`Ov-NJFF{(hzBgG(;LA4UvXOL!=?n5NRmX zr*GZrhSI(xN1?ReDC_6v(Ug8jKcpYh59x>WL(xwt<4K#L>r&$|2>Da!5I(98wM`hm=FgA?1*ANI9e&QVuDHltaoP z<&bhnIiws?4k?F}L&_oLka9>lq#RNXDTkCp$|2>Da!5I(98wM`hm=FgA?1*ANI9e& zQVuDHltaoP<&bhnIiws?4k?F}L&^#D=|9$7IoGvvNI9e&QVuDHltc5%@s)Yy_{zL; zd__5=98ylGlw&iln~6<5=Gaj=q#RNXDTkCp$|2>Da!5I(98wM`hm=FgA?1*ANI9e& zQVuDHltaoP<&bhnIiws?4k?F}L&_oLka9>lq#RNXDTkCp$|2>Da!5I(98wM`hm=Fg zA?1*ANI9e&QVuDHltaoP<&bhnIiWs%d(D;evsMl%hm=FgA?1*AXkIzKGOrw8nOBal zD2J3o$_bTnY{qpnv8mQVuDHltaoP<&bhnIiws?4k?F}L&_oLka9>lq#RNX zDTkCp$|2>Da!5I(98wM`hm=FgA?1*ANI9e&QVuDHltaoP<&bhnIiws?4k?F}L&_oL zka9>lq#RNXDTkCp$|2>Da!5I$K7D7+mGje94k?F}L&_oLkaB2VIleNl9ABANj;|<( zltao1m2zyxbu+Q4<=9aDa!5I(98wM`hm=FgA?1*ANI9e&QVuDH zltaoP<&bhnIiws?4k?F}L&_oLka9>lq#RNXDTkCp$|2>Da!5I(98wM`hm=FgA?1*A zNI9e&QVuDHltaoP<&bhnIiWs%cg>aalU5EXhm=FgA?1*AXkIzKGOrw8nOBalD2J3o z$_bTnY{qpnv8mQVuDHltaoP<&bhnIiws?4k?F}L&_oLka9>lq#RNXDTkCp z$|2>Da!5I(98wM`hm=FgA?1*ANI9e&QVuDHltaoP<&bhnIiws?4k?F}L&_oLka9>l zq#RNXDTkCp$|2>Da!5I$K7DV^mGk3P4k?F}L&_oLkaB2VIleNl9ABANj;|<(ltao1 zm2zyxbu+Q4<=9aDa!5I(98wM`hm=FgA?1*ANI9e&QVuDHltaoP z<&bhnIiws?4k?F}L&_oLka9>lq#RNXDTkCp$|2>Da!5I(98wM`hm=FgA?1*ANI9e& zQVuDHltaoP<&bhnIiWs%e~p!s_8)m=Egdk*QB7Jt%F#?ZaFnB%bkHbAFKNXntK~;g z+D&UohmP`1TSq?Ax8#b#VL6PsF!9hE{#A*GN~NGYTgQVJ=BltM}& zrI1ocDWnuq3Mqw@LP{Z}kWxq~q!dyLDTS0mN+G3?Qb;MJ6jBN)g_J@{A*GN~NGYTg zQVJ=BltM}&rI1ocDWnuq3Mqw@LP{Z}kWxq~q!dyLDTS00>eCO_SShnVjG6CMXmX4p z#~5;qA;%a}4MjEeR=f`FYd0;Y{=a$c*hf309nwyyv|}@_n@PQq9Pi7qqjpF;q#e=@ zX@|5!+9BnrnW@s)YC_=;LcEu@xEsl{eoHw4vyy{%zhf;A>G({hId-_?m`DL!_ZlX~eLuk%mY^q#@D}X^1pL8X^sm zhDbxCA<__Oh%`hRA`Ov-NJFF{(hzBgG(;LA4UvXOL!=?n5NU`sL>eLuk%mY^q#@D} zX^1pL8X^smhC+S%@vUy?*WWX6i;pdZ}hHS=lGpYZ44cSpcq#@D}X^1pL8X^smhDbxCA<__Oh%`hRA`Ov-NJFF{ z(hzBgG(;LA4UvXOL!=?n5NU`sL>eLuk%mY^q#@D}X^1pL8X^smhDbxCA<__Oh%`hR zA`Ov-NJFF{(hzBgG(;LA4UvXOL!mzXR6}Op}Tap|Ap^(LbEEZ(3Ad3ZAEXZO(77Mai zki~*57G$v?iv?LM$YMbrF7j}ZhbvST3!8D>OzIu6SlH2GK^6{#eysrWU(NN1z9Y}VnG%QvRIJCf-Dwf zu^@{DSuDt6K^6{ zpO-^^UJh9z$Pz)8h)|z?uBUr$Q1TkjllV#e zBz_V4pTtk%C-IZ`N&F=KP>J7WTsIS&dL6-z;wSNw_(}XE zeiA>4pTtk%C-IZ`N&F;!5llV#eBz_V4pTtk%C-IZ`N&F;!55242dLliMo=8umC(=`>^kg%x zn~6>B$&Pv=J&~SBPoyW(6X}WcM0z4Uk)B9Tq$ko7>5242dLliMo=8umC(;w?iS$Hz zB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW(6X}WcM0z4Uk)B9Tq$ko7>5242dLliMo=8um zC(;w?iS$Hz3iauidiqBEpuy)XqQmO1hGpQqwhWMEfGh)K86e95Sq8{5K$Zcr43K4j zECXa2Aj<$*2FNl%mI1O1kY#`@17sN>%K%vh$TAQr%YeefGh)K86e95 zSq8{5K$Zcr43K4jECXa2Aj<$*2FNl%mI1O1kY#`@17sN>%K%vh$TC2d0kRB`Wq>RL zWEmjK09gjeGC-CAvJ8-AfGh)K86e95Sq8{5K$Zcr43K4jECXa2Aj<$*2FNl%mI1O1 zkY#`@17sN>%K%vh$TC2d0kRB`Wgyh2|0+G@eKsnqz**(F>-Fk7E&h1+Oujm{&n-Xi z_y%9TIR3c0{Eng6KCgUvotg5zpDIsQ(>djPNAah}Y+U~I{PL%n_|xk6)4AnOw<~|z z!>+Thh-wdrXSlSyNddm508#+Gx$XDXIrZuG!_7|G&61lXH%o4o+$_0SaOxmj|v+#?|@0?mH6)(e>yb& zbbk5MCgo3i)ECA4P<79X$pM@kz{vre9Kgu|oE*T(0h}Db$pM@kz{vre9Kgu|oE*T( z0i5n1%MCez(`@^i2jAB`_~gN-UzXnq?|aJuJif4UTsMZasVd>aB=`A z2XJx#CkJqH04E1aB=`A2XJx#CkJqH04E1aB=`A2XJx#r`7FVI)M9{ z12{Q=lLI(8fRh6_Ie?P`I5~im12{Q=lLI(8fRh6_Ie?P`I5~im12{Q=lLI(8fRh6_ zIe?P`I5~im12{Q=lLI(8fRh6_#R0thu7UiJHr0-AI$=(I+Eh(u;fLGn=VTVr0$*SH zbho-Bth>%S-KqJ#<#qJ#$@$~4c}o6HY@V8L^S`2dvo-{?_lUZC2^ zbb8Z^o8H*;fu?JlVkqmaY}2%D({@eUH|@~0W7AGecW>IcX_uy5n|5p3y=jl8WleiF z?bWn*(>_i6Htp9GziqqTw*#7%Hyzk?P}7R0gPRU%I<)DqIrV9C57oX~mOfuFQ-8h9 z-?a2ddbHrv+oH|8C7a%shTzNbcwV>&e&fPc(?@J;L*StI{#G6-j~0A++YEQ&o$XGL zJ3$NnPHa1!jN2K4dweeDiJ!K(kpw(66GJk+N>hn-&Db|O24`n1=u({tKRWT#M{_AWc+ zr)*m)T2a=UeSFLL@yA1F@-^`?@qsgW%N@)1l{5L;`13*KTGPIE^CIgxam#Lx{JTSa z+Hd&lFKd52**(;!{fC`i-*zH9h5B@Wo!%5LU4Aom`g!@)z1^eoljEQHyitBl{Ikv( z<#^q;+dRjMw%ryvUa{@A%<*zA+#`zPim_GIv9L$;B0xrF`UGF^totZJfwBh5B@`og}^Y zOiMa0%9KeS`*m03*OpB>w;b#bse>BF{yj$C%$b&ra$K_d|NTRIK| zlmF||6S?$6EJsj9vQET1LTR*17xfn@u07*1LQpn`eQdMf5XZ7q}ZG^9rU$5=tmlK=cvnvb5!Rf zUynRuUF|kmVrPSEbCw@9tUoVk>knCfLVddDu+s%?C$dwhPxrEuFFY%DvZj$WjV{}% zERk<4PcrU3?EK8OGub)Rr~BCHl31KQHHZ_2DigxjUe&(#>ZU8p3R(AyXAf0zdQs&s z@Vz%Ks7vdq#oC-qzcV~w--t)m14bS&@_>;Cj67iE0V59>dBEt##d!LFt*Ix0M|;4Q z^b@^frbBRFLty28Vq1(!dbHp#FWhgq6OU^Xj-@R)&r+G_xef5FEw>+qQ3;C8%pOT$6+o^od zTNlB5U>@Y3>rIsh#(i29?`|W@1v$|p_cPR|2M^!4ynQ42M)Hm18$*3M);BKcL(*}E zz&Py}V`rSmIMJQ=C^P8o>ldV3Bx~S z_t@P|WG7niEkD$qSkhDKVTNG;Wo6P_z6h3`hfg1|M@)y{L__d|n6!Vs2qx_#{d29q zJGHfvtd+Fj*UCq^6PNB;#{8X&V9XyqjQQQ#n3FN5v{$(^?<$AN#|%4f+;%2A)Ah0Q zyBEPdKWVr#8?-w^?hKt8Z#8;Px#y3y^O8OSo;)3b#~FgN?ouYj`)2alvAv>9j>oqz z&t4NVDgFob=?TOC`gr?abY6V-m5ZR0-*p%EE!XB|KO1*pzoz}0d`Q~=^Pjs`s@s;JRJ=r&sZzSJHzL8eMUrIi&NeKJJ*(Dw7lMI1Td{2z5Q6!@n z>eI?$r`NTe$WEa?oiglnY1@hH6zWqRc6x2wiR={W)2YKwuW37xokD$j^03pZ+fHPs zP@kSM?DVR(6WJ-$r>EM<{QT3{>9UwK^oAz8$JhGwG`sJyX({sBQGQ!&KQ+qRmtE7k zqkLj)*DIH$pFZ5`+uN;@TP3#|>eKJr-F$sZ>~6k>`t*$9f4#Z=uTY;>mH(BGihsff z%1a{89Co~{?MQEmf7MlG_37<0eHZMD`*fP2Skm!7%Mipza%aC76DH1=t9*Lh*L-@N ze0rUHdYycFoqT$oe0rUHdYycFoqT$oe0rUHdYycFoqT$oe0rUHdYycFoqT$oe0rU{ z2b3&qWML!Kh02?WY{qpnsdwFFG&{zXulhA_A@VhEA)=?Y|LZM8zUD1N92^ z7s1Ke8UDGamVfT-=OTX#0a*yhLO>P*vJjAkfGh-LAs`C@$)Ds;@+bL|{7L>Kf094R zpX5*SC;5~7N&X~%@(7bhI8>Gdn{nMt>cChM>}W|KO9ELE$dW*o1hOQMC4npnWJw@P z0$CEsl0cROvLuitfh-ARNgzuCSrW*SK$ZluB#5242dLliMo=8umC(;w?iS$HzB0Z6wNKd3E(i7>4 z^hA0Jm7Z+Ibu+Q4?^Lm)o=8umC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0hJ&~SBPoyW( z6X}WcM0z4Uk)B9Tq$ko7>5242dLliMo=8umC(;w?iS$HzB0Z6wNKd3E(i7>4^hA0h zJ&~SBPoyW(6X}Wc6za=Q9bfWq_#Ybr2j(xwl42nt3kg|B$U;IE60(qxg@i05q6tbj{C50?0WJw`Q3RzOfl0udgvZRE{@?kTsn~6q6tbj*`tl=TV@c7|*IG}cC(;w?iS$HzB0Z6wNKd3E z(i7>4^hA0hJ&~SBPoyW(6X}WcM0z4Uk)A@OC!2BIOl)dTcGMH;iS$HzB0Z6wNKd3E z(i7>4^hA0hJ&~SBPoyW(6X}WcM0z4Uk)B9Tq$ko7>5242dLliMo=8umC(;w?>HoEN z9^g?_-T!AdAd2)Nh!nd~6zm;)&Awv+EMP?hAVLa6NP!3`5FrI3q(Foeh>!viQbZO~X!3%lfF{UI za3rKagcOL70ufRmLJCAkfe0xOAq66&K!g;CkOC1>AVLa6NP!3`5FrI3q(Foeh>!vi zQXoPKL`Z=MDG(tABBVfs6o`-l5mF#R3PebO2q_RD1tO$CgcOL70ufRmLJCAkfe0xO zAq66&K!g;CkOC1>AVLa6NP!3`5FrI3q(Foeh>!viQXoPKL`Z=MDG(tABBY3Xd#HP< z4=FuTA?4OEq(Foeh>!viQXoPKL`Z=MDG(tABBVfs6o`-l5mF#R3PebO2q_RD1tO$C zgcOL70ufRmLJCAkfe0xg3n?^tK~q2zgcKYJDG(tABBVfs6o`-l5mF#R3PebO2q_RD z1tO$CgcOL70ufRmLJCAkfe0xOAq7%M8JIJn9RGp=5nK3>*a8t-AYuzdY=MX^5U~X! zwm`%dh}Z%VTOeW!L~MbGEfBE-u>~TwK*Sb^*a8t-AYuzdY=MX^BHw-#g4m+;KQ9y(gs>ol1tBa5VL=EB zLRb*Of)Eyjupop5AuI@CK?n;%SP;U35Eg{6AcO@WEC^vi2n#}3A`=#kJ%&TC>@ge~ zI_W=zupop5AuI@CK?n;%SP;U35Eg{6AcO@WEC^vi2n#}35W<2G7KE@Mgaz?k4WTCp zJwfORLQfESg3uF$o*?uDp(hAELFfrWPY`;7&=Z87AoK*GCkQ=3=m|nk5PE{p6NH{1 z^aP%^aP|1s``>_jZ?r6tzVhOm5tUui)j7+z<5`+RZ9a4qyC+(~vL_1$_kMz1E@ zt9uyWQO)e-$0gu++)H;y>9OBat-Gc9af$b_E0~%uhY=BCYC;)5HScE-rTRGLvkU4; zkkd=hZxIZn+jpwlGfKG<2eB(wXjgn%%72TXfL%dQ8Wsj6h@b>zd{BBIHHa6(K|l-w z%J@M%$RMt?f~q~_Fjq~5fA?93I+0H7k?{|}&TW&}J~uB>CAT0^_1}HSrz(Q>;830N zZtyVdhf6-bB* zBt!)gq5=s~frO|)LR26jDv%HrNQep~LkAK{l1m=`(XAv0`MgY zB0hxBM7}p9<4kQrQe0 z#xA&DkF}d!0*=^lx{G-B2m=p=2YbJ5)M^(xHr3`iRsZz6%EdF$gH*2k|C@7^+)1AJP@<=RM7p6;#k10a$hU{h z>BNVRnErgUu@K%%xjYlDE%NO-bfx~ZX*9c_g#5U!?~S3mguvn9e1@3MP{z;au?&Kz zU45e&$sLrCx}F1ZvoXZY#t=6fL)>f(akDYR&BhQn8$;Y;3~`Gw#4W}Uw-`g*VhnMM zF~lv#5VsgZ++qxIi!sD4#t^p{LmVK8?`a=}`q3+=*x$T(uDEdPb{UM6}5Ji88qCZ5@AEM|FQS^r>`a=}`A&UMGMSqB* zKSa?VqUaA%^oJ<=LlpfXivAEqe~6+#MA09j=nql!hp5{?+;$9c+cCtA#Zb#oAgv0? z_r{^8Un*MWv#&8k>;7nOkSzE5|@s=JFzKzBc&yT7WtOG-d@Kcu_F%lE@C zG>CW!W&9V~1O`#6&ow?u9mg@@afCRIP{tp}iDk!eQrRw;T(%27wq4+k)_wNBT77H^ zJLDMM$NpOh_)3|o&W~1af49z0qw{>h%+SdO2{}Yyf-*iZP1lfi)IGZ^Y)CWos+V4^ zv{#=nz%?50suFN?KBc?2sk?tzcT4ksikQi+;D(QVN4*S;r9rn-I>B-+)zPOI<^oz@O`qtj*pR?1s74m+|9jz8ZlNU4vG{LQq zdOx@o5~3;sQ5Auxia=CFAgUq|RS}4)2t-u`qACJW6@jRVKvYE_sv;0o5s0b?L{$W$ zDgsdzfvAc=R7D`FA`n#(h^h!gRRp3c0#Ox#sER;TMIfpo5LFR~st80?1fnVeQ5Aux zia=CFAgUq|RS}4)2t-u`qACJW6@jRVKvYE_sv;0o5s0b?L{&uOdtWd!_{#39L(f+> z#232A_r6Ry^$DFIoJ79&6;2ucMITJ^P8tMi5Sbci@`9#-CeQ$mXaGV35E_8c0E7l0 zGytIi2n|4J073&08i3FMga#lq0HFa04M1oBLIV&QfY1Ph1|T#5p#caDKxhC$0}vX3 z&;W!6AT$7>0SFC1XaGV35E_8c0E7l0GytIi2n|4J073&08i3FMga#lq0HFa04L~ie z&gZSS8=k&q2J?fr`)YJFZASv&zdqn}7Ho9>qC6mw4Mu_RYE5SCn)1b`a^0EmBK+l3_|34KjNf2J-m1p_j!0(S&q9rW@#lN z5kw?{SY70MKe4)g(lKX--1+TBk?;MS`e<7C5tQ+JZa!V-YW`r=D!7_2^1TJAkDdxY zf--(o3+cMLnKNHq=h803rCo?iyAYRlAujDgT-t@Wv|DWhk0f zh2+~sQ|{W)8$ClkLFfrWPY`;7&=Z87AoK*GCkQ=3=m|nk5PE{p6NH{1^aP$7c>Plfu3+gPY`;7&=Z87AoK*GCkQ=3=m|nk5PE{p z6NH{1^aPKrMk5Ho9zO3tnQU05G~Z6KqHGteEZYUEB#X6@ z#dao()$EW}x>u@F3AkeQhdSR?o!{O%zlP59C3CyJGl|a-$$-fB)~1}ghfWYqBHvq= za_Sa3K{$zg?@yerRPnB}oN^1va$CJ&4@;vd2tKCvf+M*tL~aX_+d|~F5V0FVg&|KsaH( zni)DlIEj2qpp+gabE(q(zgnuN_SdK3{({(FBHyc#a+(x=8A3Qg89#k$rUo$~90bH5 zpo||xEe27lbL85o<2W%qju6KY%J}10he4dAv$^_Nu7ld0vV3m`-fm?#3waH)+#7nk zp}k!<^v;>ZKsHRdDfDQB`i zLLE@1Pb|M*`71hh>6mXvH&M<^Zq-CAw@D(NdsHGT_w+=0H`r3v#5QiC{kQK#MK@c` zwr+)*?Of&kt*PW*l}j}@RBu*yv-PTmTcW16Yf{siI_^*VwY)?l*J~$hc5zi2ShK4; zx1lw=x%e*D?C$#QYE8E5vAZ=*-S29exhwXtW`B3HJ*6^nqC3#;>`t8GzEg9mJ3^a2 z&5hW>njF_!{};A}pNQR>pP(>#v^v+SM2^8`Q=+^r6>QnYmWsA)EB)Q5UEHSF_U(4o zk4oytG5SBSmlRt+wzqy%RzF5+TD5aC)m-l8*0-jEJ6e-0*X3_#&6RG(w$^lX&Gf31 zYf;&nE^bLxYr6S~Dz>4jwp7!`Uek=tclp}K1Ko7Zr$MffMp{tZle)U~v4$-*ZK-8T zZCmQtvV;1)Rswh+{Xpx=u})oceApeK=8;1GGxh4x&Fr6gTCIuwa%tijcY&J0MOtk~ zt@f?_hq$UbHqRDmwfZ)qoos2K)w^oF=L*;R>%-Sjjt|IjXnMy+a$GOR=hHhjwlB+p znno{nPV8)5*~OM!wQ@3KEmh=t%~eT5EfW+|?|?dGU?$E{T}(v8-%uI@h4wBFV=Rr9W5eIV%?e-SQ|(Y@&L{9g*>duS+i z^`-D$`eU}I98Z(usPv9|$+4{*N2hnpmSb`l{pCN#4Nx=I4b|=&SGX5n)$XfQxHCVO z%lOTFaVEbp|L&LLKmAtRTO-2{_v@CYR{Lux$<=Z-hR@u3HJ>Nr$X}0$T%pEhWU9Lw)x zj(?op~$4+uwpWg9!IrcV3 zuUss>=LzzBOrEg_&lBZ&iyqA%kMKN6o@*_0^|E5FdfeLbF+Xv#-mPq>NnXVb-aW-8 z`>D2^rcaY+PeI<_$&2 z6b?|vRPCRh0B&;Tce;k9_q=K`cb>*o-F8Ke4LD)Te=JKHr8n!uIww4P;|yEQ)GCcE zr}OGX?B}!0`)pfU*k;bxX6oB!TH1>%^rC^iXl0+CW6Qa=oM+4VwzRgTjV%}0a-l63 z*>bThm)LTtEp2VNOnn$?=OtdFn7eFyYa7ShS@uQj?Ht=oTHel9yWE!cT2*H~-tIBi zU90RF4kLN`^Vf-U2V40HTXJo=(iUd}$*yGUH@AUYrL7)b!CoC3Ztbr(bG1B2R5Z`y zB0R5==L~rsAK`heb*G~(*V%HtEuE}udD<%{S=Tz-izBqFPPP|a?8WgK^lA3O*B=<^7|)+r(Ts9j6O6xA9))-rJVDZ0Tc5Ut9Xw(%+T= zw%l#YJ+|Cy%YC-oZ%e){18o_kKU2(Ak^=Rim@6a?*uHpB{g`d#5AV{51N4wQw=4wx zZ^MZ3u>G}1w2dRJnBZL&bF0<1i}E> zq&$bnGdIHXDS5sp&nqK5pO)uLdAbPCXKeg~<$a-5O1!I!xYvf*TF+{&YF5tht}WtP z&)Hf-ZF%077i@Xa4&UN^Z3CSwcD|&|jncg;I zQX^}-r%yrL9&+j#qlB?#smNBT#k{6*PEmHVtDB~A+!-qjM|WzNy<%yLrdy>?ud7cB zgFY=*pL)j%!&9;K=?$9`BW!uombcWui8_vbVs4SzzA-nqv9~Qg2Rd`!{ zEN>+=Z$Ql5r1ox`-S6n#*($8G|Njr>U7H^7*)qzO(YB1y;8cq9 z3Swa$Ypi`RP9LbYnP!;N^zlRg>M+w)BeuN0GG2qo;Hwbt+m8OgmJij>idOmY9*y{l zn;_4DR`v28i}3u&wmZ?5Nw!S3*fQ0YX|_zaWri)EXp7@iM{8Ob#ENjf`Im9^ zZg6-(zi>7VLFuOR78KV$ zSD(sR?aX^N=90be6JOXK`_h)LZ28)jZ*2M2mhWttZOa^6=GrpPmhWx(!ImGjr8ZVh z^InXYKR?OydU?JS;rX*Xm#cTfB0T5Ib8l^Pc!cKy%Y^31@ny?|7Fx#_*|OM{CECO+ zE6RDVTHvu7n)h17rj}}r3i5nC!gHBz{})@9+w!X|zuEG;Eh}tUY0D~GR@?H2Eo*F9 zYs)%Y{tgnYN5wi@70KSJAhV53MSWOB1*LKJ;kNe5N`iVh zK7A*xv}9{nlG&b>if}>4%1_#S@t(z5?Ms~~-qScMwb_oVCR5!^Sa|Yk=&hPEwPb3` z)REairmjpqGp@06&PjH}cGO$-^;T=utESppJJ~A>~TkT9s4b*;S zznIuSYc|Apq>3$HL>%x&@^37&v&=56$yw-^TU4X+c4f_#`kPTbd$EbmDusO)G0<#WAF(HOd2=FM_r`UGT<1o(?t^QE^5!}(!nFymhs*W* z2-kgay-Kb>M7ZvU>!Wi0F~YSeuG8iEQ-o_XT+3H5*PkO?_s6w`T<1r)9)Rl|a$OMN zdLXWE%5`Cc>p{3KlwPvIy71 zaa|zSUm{$Oz_npTb6poLY`=^vRy+fTH2jM$EbF>D(uY0HxgNK+SWc<<`iw> zDit_a+a^xctG;^mhrK#Y4yT*Rc0X8%u^W+oRHRuGZZ%nEPu`wnrEK%p9c@;vHsi78 zzA8wYD#UWEU(^6!Yh?mP18NBCbS|LgG|q)ORN5&oUz-x>e2 zRFG>J;on96KK>7?oVQDa{|)BqM#*zGJN7rSVo&{{J>ssnwMDD^S&7g7?9DQ_$o!Y> z6w2>Z&FSoTQA&tb2GpUV61aCfsQB#@cXRA+Ca+1{QJ&f_URdOF1u70TwWprns=s<0 z>n^qw$BJ#sl<72!7iNvcRoJrnr3O?rsJ7KmwQc`+VHR0zo$5xXngyLYP@Os;zDc2> zk#1!w(tVD9c`m*2jTl>8`mi#u9njn9W>L-%ddR&e?t5xZ92(!`LiI*>W^Lx)ljJQ~ z$y-qV?wwixPT9`8gTWl4!5kK$;X5_>UNXI9?qbcsTJwmw8>05ecu-G0Djw8RkB+-0 zs-rgFbT|E5*Ymf^oan>AZdS*S*&<;Vy{NI}6SXg$8l+AgyG6p@nw{#WdC*^GfXv-8 z_sHBUbDzxpGWlkT{hW3nohatB`$2R<_jr3J#JS1aJ8{#HSj-WjKu7EW`q|d*wDeAi z2n!G5dcyAJdTNC0LzzGN|7r9O)6d&A`qLtY^a!r)H2TvcTp!h@ACq}p<_Vc6WuB6G zTILycoBVh9@oeBGS@TWfBUvW?*%sVo;GvlKiJHR z>itVH!(@ibyllpOuPvW#8RIMVD!9wgYZWoHSGD$Q_%~BI^V|sk*X92P{+G$Wb%g&2 zTy+DZ*CxXCOA+vQ{R>Q%jJZ?DE_+v8>4m-#^ELzxL?iZ)R{DzfK9 zZFrK*WSNg;rpQc{nIRtsA{(IqinOq-;aLv}H_mo|S`i z&iGK6gr~dWx>ZgE4y02>IWs&+V>lT1mO2%BIAY%%BL742Pu@!8_R@Nf#9a@yk6IN% z)riMZ)rhbPahR><4wcvA7MBj!mX2UcoP<4*T%(s=L^)EQ9mQvpeN#ib3W6YPVK9ctf%9yrP^mQEIU6&NAy^k<7AGPIl)Y^U$;DwP89QX%aiED zQA*s0LTGP6mzw68#AZLoGz0iqyO;j{YS==IYZ`5nX}9!FZMqlvGKFz z-$JIPOe>jl%p@=0zZkLlx$-{`|NE4Z4vX+VA6KsP507wdtxdO)xj^PZnTupDmbt`C zw!2l=7T<{2(51Nkq&vGuM7XxqrZ1CeCv&+>dzlVqtiGu<_MM1rTp|BlnJZ)F)_X6~F^0{87lT2rsE@oUit?`~kUf*6NGs4jkeYycx{>#Le z2-h3+hi;O&S>_g*|C-5m%PQI*9v88Cp8Ri>xs8>$Tr)mmrLOYthX0r<_D9~2@b51F z+wos6{|_Skd&s}1%pEd!%Jh=yEpwNd`G z>*{}DS93cmVVG&BIBI9b-9>6YD^a!Ahcu}F$vkYvwNifYxk>F2z4fTfV=|A+JR$R> z%u_N?o3UA>FKx>l_fz{tnC_&@9Jgkg^9-G8k?d3}wO@wmPH}Z=uy)rFnP+95lNo9z z+fh6EDq_AqFaH;0UX*!>H7T`x7qR9r`45+QnU$!w%!yd(6`Tey8Pde86oqg8COe{lOJu$y=AYm-3$80`KgEnl($*s4z2QY#Gigge{iJCyJmtq z#C-d!@9C{kX0qL#`kq-3v7OQKA0snXW}M7;nfGNrkoiz%f|=z1_*fdT$&che5r0mj zmqqwb!j;R$zeKoB#+CEt3#oI?F-)^pUZL!CU&*;~X1UC-W~}05PuB6uM|9#h`Tvgp7-a?9MEI}3l@d$E2-lUkk_&7b;krt_SS|C1 z%o>@sGV5gilv!^k+nuR>UpZoHdQ3czfwx~Zw&PV>p{;7+!_}ATRdsu%hpXGKwrH#5 zLp36{s)u-cdI+|she+EGTWHNcw9jfrtf_|;C!bN=P5!kb{Piqfdv?7UJ@z#DSkn{L z?>bp-klG!x+<9v2X1O71>qTr)j|1g#pVn8S;Bk{gkR2n|)MFX#&zjLg40&WB4=42W zm_K{Sp&1@|ca6Hy$abq9d6#^A-Mf0Pal{|cW8Qe6o8ND{_MJL)?9{=(wq1U1(|#4N zyM)*I_viPketG*2ZT+jew7>k4&RyDeY2VrY18R1ndd_@%`dgxH&fWH$!Nff|1FW%I za&ul*TQ`<7Ty2$jPJgvk<2n7*R*UEKReNVVr;plR@tnJ?_4`%iuXVgCzqfx`)Bghy COO?a` diff --git a/src/parsetab/tabs.dbm.dir b/src/parsetab/tabs.dbm.dir index f55990c88..881659c7d 100644 --- a/src/parsetab/tabs.dbm.dir +++ b/src/parsetab/tabs.dbm.dir @@ -1,4 +1,4 @@ 'zxbpp', (0, 71563) -'asmparse', (71680, 233956) -'zxnext_asmparse', (305664, 259034) -'zxbparser', (564736, 641214) +'asmparse', (71680, 234798) +'zxnext_asmparse', (306688, 259879) +'zxbparser', (566784, 641214) diff --git a/src/zxbasm/asmparse.py b/src/zxbasm/asmparse.py index be4bc0284..55f26c082 100755 --- a/src/zxbasm/asmparse.py +++ b/src/zxbasm/asmparse.py @@ -925,7 +925,9 @@ def p_expr_div_expr(p): def p_expr_lprp(p): - """pexpr : LP expr RP""" + """pexpr : LP expr RP + | LP pexpr RP + """ p[0] = p[2] 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 From 5beb0ef35226ab7021142c101943494582b364c3 Mon Sep 17 00:00:00 2001 From: "Oleg S. Kostenko" Date: Fri, 8 May 2026 20:36:31 +0300 Subject: [PATCH 118/169] Corrections to Play documentation --- src/lib/arch/zx48k/stdlib/play.bas | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/arch/zx48k/stdlib/play.bas b/src/lib/arch/zx48k/stdlib/play.bas index 61da99e1a..f37d47ca6 100644 --- a/src/lib/arch/zx48k/stdlib/play.bas +++ b/src/lib/arch/zx48k/stdlib/play.bas @@ -26,6 +26,7 @@ ' - # - 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 @@ -36,7 +37,8 @@ ' - () - repetition ' - !! - comments ' - H - stop -' - M, Y, Z - MIDI control (also you can't now pass more than 3 parameters to Play). +' - 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. From 0a73d1dc811c42bfd30725cd8e0f5f06d4cd9dcf Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 8 May 2026 23:53:53 +0200 Subject: [PATCH 119/169] update build tools for Python 3.14 --- docs/requirements.txt | 2 +- pyproject.toml | 4 ++++ src/api/constants.py | 6 +++--- src/arch/z80/optimizer/basicblock.py | 2 +- src/zxbc/zxbparser.py | 6 +++--- src/zxbpp/base_pplex.py | 4 ++-- src/zxbpp/zxbpp.py | 4 ++-- 7 files changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 240737565..dff0e1aeb 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -27,6 +27,6 @@ pyyaml_env_tag==0.1 regex==2024.11.6 requests==2.33.0 six==1.16.0 -urllib3==2.6.3 +urllib3==2.7.* watchdog==6.0.0 mkdocs-material diff --git a/pyproject.toml b/pyproject.toml index fa47935b7..73189d425 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -118,6 +118,8 @@ disable_error_code = [ exclude = [ 'src/ply/.*\.py$', 'scratch/*', + '.venv/*', + 'venv/*', ] [[tool.mypy.overrides]] @@ -130,6 +132,8 @@ follow_imports = "skip" line-length = 120 target-version = "py311" exclude = [ + ".venv/", + "venv/", "src/ply" # PLY, external 3rd party tool ] diff --git a/src/api/constants.py b/src/api/constants.py index 6f1981b11..d02632c0b 100644 --- a/src/api/constants.py +++ b/src/api/constants.py @@ -175,7 +175,7 @@ def to_type(typename: str) -> TYPE | None: @enum.unique -class SCOPE(str, enum.Enum): +class SCOPE(StrEnum): """Enum scopes""" global_ = "global" @@ -193,7 +193,7 @@ def to_string(scope: SCOPE) -> str: @enum.unique -class CONVENTION(str, enum.Enum): +class CONVENTION(StrEnum): unknown = "unknown" fastcall = "__fastcall__" stdcall = "__stdcall__" @@ -209,7 +209,7 @@ def to_string(convention: CONVENTION): @enum.unique -class LoopType(str, enum.Enum): +class LoopType(StrEnum): DO = "DO" FOR = "FOR" WHILE = "WHILE" diff --git a/src/arch/z80/optimizer/basicblock.py b/src/arch/z80/optimizer/basicblock.py index d796c55c3..5c7480c14 100644 --- a/src/arch/z80/optimizer/basicblock.py +++ b/src/arch/z80/optimizer/basicblock.py @@ -446,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/zxbc/zxbparser.py b/src/zxbc/zxbparser.py index ab63938ac..0beeb0ebb 100755 --- a/src/zxbc/zxbparser.py +++ b/src/zxbc/zxbparser.py @@ -3511,7 +3511,7 @@ def val(s): 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_) + p[0] = make_builtin(p.lineno(1), "VAL", p[2], val, type_=Type.float_) def p_code(p): @@ -3531,7 +3531,7 @@ def asc(x): 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) + p[0] = make_builtin(p.lineno(1), "CODE", p[2], asc, type_=Type.ubyte) def p_sgn(p): @@ -3545,7 +3545,7 @@ def p_sgn(p): 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_) + p[0] = make_builtin(p.lineno(1), "SGN", p[2], sgn, type_=Type.byte_) # ---------------------------------------- diff --git a/src/zxbpp/base_pplex.py b/src/zxbpp/base_pplex.py index 20fb5b9e1..40f2ab5f4 100644 --- a/src/zxbpp/base_pplex.py +++ b/src/zxbpp/base_pplex.py @@ -9,7 +9,7 @@ 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 @@ -26,7 +26,7 @@ @unique -class ReservedDirectives(str, Enum): +class ReservedDirectives(StrEnum): INCLUDE = "INCLUDE" ONCE = "ONCE" DEFINE = "DEFINE" diff --git a/src/zxbpp/zxbpp.py b/src/zxbpp/zxbpp.py index cbf58cbac..53c7f6449 100755 --- a/src/zxbpp/zxbpp.py +++ b/src/zxbpp/zxbpp.py @@ -12,7 +12,7 @@ import re import sys from dataclasses import dataclass -from enum import Enum, unique +from enum import StrEnum, unique from typing import Any, Final, NamedTuple from src.api import config, global_, utils @@ -28,7 +28,7 @@ @unique -class PreprocMode(str, Enum): +class PreprocMode(StrEnum): BASIC = "BASIC" ASM = "ASM" From 7d71fa01ae254857dfdc9afacf18b7b328fa70b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 21:54:17 +0000 Subject: [PATCH 120/169] build(deps): bump idna from 3.10 to 3.15 Bumps [idna](https://github.com/kjd/idna) from 3.10 to 3.15. - [Release notes](https://github.com/kjd/idna/releases) - [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.md) - [Commits](https://github.com/kjd/idna/compare/v3.10...v3.15) --- updated-dependencies: - dependency-name: idna dependency-version: '3.15' dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index dff0e1aeb..d2b9df5b7 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -6,7 +6,7 @@ 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.8.1 MarkupSafe==3.0.2 From 2d219845f8211e6dadda28829fee0f2479a858b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 21:52:37 +0000 Subject: [PATCH 121/169] build(deps): bump pymdown-extensions from 10.16.1 to 10.21.3 Bumps [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) from 10.16.1 to 10.21.3. - [Release notes](https://github.com/facelessuser/pymdown-extensions/releases) - [Commits](https://github.com/facelessuser/pymdown-extensions/compare/10.16.1...10.21.3) --- updated-dependencies: - dependency-name: pymdown-extensions dependency-version: 10.21.3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index d2b9df5b7..b9675618b 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -20,7 +20,7 @@ paginate==0.5.7 pathspec==0.12.1 platformdirs==4.3.6 Pygments==2.20.0 -pymdown-extensions==10.16.1 +pymdown-extensions==10.21.3 python-dateutil==2.9.0.post0 PyYAML==6.0.2 pyyaml_env_tag==0.1 From d371d8373f1f4124706ab8ecdf7c33de511a8a31 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 9 May 2026 10:18:18 +0200 Subject: [PATCH 122/169] build: put dependencies back It won't compile mkdocs correctly if updated --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index d2b9df5b7..02ef8eea7 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -19,7 +19,7 @@ packaging==24.2 paginate==0.5.7 pathspec==0.12.1 platformdirs==4.3.6 -Pygments==2.20.0 +Pygments==2.18.0 pymdown-extensions==10.16.1 python-dateutil==2.9.0.post0 PyYAML==6.0.2 From ba4c221eff3ac9fcd92e361fae5a4c0dd11d7e7b Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Thu, 21 May 2026 00:09:37 +0200 Subject: [PATCH 123/169] doc: add BIN --- docs/acs.md | 2 +- docs/asn.md | 4 ++-- docs/bin.md | 27 +++++++++++++++++++++++++++ docs/identifier.md | 1 + 4 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 docs/bin.md 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/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/identifier.md b/docs/identifier.md index 9f9a3294d..644efe25f 100644 --- a/docs/identifier.md +++ b/docs/identifier.md @@ -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)** From 7c562c7db064a277a13e43df2e82f1ecfd5b1a4b Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Thu, 21 May 2026 00:13:40 +0200 Subject: [PATCH 124/169] doc: add TAB --- docs/identifier.md | 1 + docs/print.md | 4 +++- docs/tab.md | 28 ++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 docs/tab.md diff --git a/docs/identifier.md b/docs/identifier.md index 644efe25f..2fa82fb4c 100644 --- a/docs/identifier.md +++ b/docs/identifier.md @@ -101,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)** 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/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) From 97bcd2925fe8707d8ca1be985f55897e7297002c Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 17 May 2026 09:30:59 +0200 Subject: [PATCH 125/169] Improve typing --- src/zxbasm/memory.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/zxbasm/memory.py b/src/zxbasm/memory.py index 55e795b9c..603418896 100644 --- a/src/zxbasm/memory.py +++ b/src/zxbasm/memory.py @@ -160,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: @@ -209,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. From 25a74ee16bfcc50b75e3f8b810ffa3a833d3f952 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Thu, 21 May 2026 23:42:31 +0200 Subject: [PATCH 126/169] refact: rename src.ast module to src.ast_ --- src/api/optimize.py | 2 +- src/arch/z80/visitor/translator_inst_visitor.py | 2 +- src/arch/z80/visitor/translator_visitor.py | 2 +- src/{ast => ast_}/__init__.py | 0 src/{ast => ast_}/ast.py | 0 src/{ast => ast_}/exceptions.py | 0 src/{ast => ast_}/tree.py | 0 src/{ast => ast_}/visitor.py | 0 src/symbols/symbol_.py | 2 +- src/zxbasm/expr.py | 4 ++-- 10 files changed, 6 insertions(+), 6 deletions(-) rename src/{ast => ast_}/__init__.py (100%) rename src/{ast => ast_}/ast.py (100%) rename src/{ast => ast_}/exceptions.py (100%) rename src/{ast => ast_}/tree.py (100%) rename src/{ast => ast_}/visitor.py (100%) diff --git a/src/api/optimize.py b/src/api/optimize.py index ed786e08f..c888d8f2f 100644 --- a/src/api/optimize.py +++ b/src/api/optimize.py @@ -18,7 +18,7 @@ from src.api.config import OPTIONS from src.api.constants import CLASS, CONVENTION, SCOPE, TYPE 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 diff --git a/src/arch/z80/visitor/translator_inst_visitor.py b/src/arch/z80/visitor/translator_inst_visitor.py index 393139c27..ea89f2541 100644 --- a/src/arch/z80/visitor/translator_inst_visitor.py +++ b/src/arch/z80/visitor/translator_inst_visitor.py @@ -10,7 +10,7 @@ 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 diff --git a/src/arch/z80/visitor/translator_visitor.py b/src/arch/z80/visitor/translator_visitor.py index 6cb8790f6..e0c7cd5a2 100644 --- a/src/arch/z80/visitor/translator_visitor.py +++ b/src/arch/z80/visitor/translator_visitor.py @@ -17,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 diff --git a/src/ast/__init__.py b/src/ast_/__init__.py similarity index 100% rename from src/ast/__init__.py rename to src/ast_/__init__.py diff --git a/src/ast/ast.py b/src/ast_/ast.py similarity index 100% rename from src/ast/ast.py rename to src/ast_/ast.py diff --git a/src/ast/exceptions.py b/src/ast_/exceptions.py similarity index 100% rename from src/ast/exceptions.py rename to src/ast_/exceptions.py diff --git a/src/ast/tree.py b/src/ast_/tree.py similarity index 100% rename from src/ast/tree.py rename to src/ast_/tree.py diff --git a/src/ast/visitor.py b/src/ast_/visitor.py similarity index 100% rename from src/ast/visitor.py rename to src/ast_/visitor.py diff --git a/src/symbols/symbol_.py b/src/symbols/symbol_.py index f495b4977..41c3c2df4 100644 --- a/src/symbols/symbol_.py +++ b/src/symbols/symbol_.py @@ -10,7 +10,7 @@ from collections import Counter import src.api.global_ -from src.ast import Ast, Tree +from src.ast_ import Ast, Tree class Symbol(Ast): diff --git a/src/zxbasm/expr.py b/src/zxbasm/expr.py index 4c9444acc..536dc2b4d 100644 --- a/src/zxbasm/expr.py +++ b/src/zxbasm/expr.py @@ -6,8 +6,8 @@ # -------------------------------------------------------------------- from src.api.errmsg import error -from src.ast import Ast -from src.ast.exceptions import NotAnAstError +from src.ast_ import Ast +from src.ast_.exceptions import NotAnAstError from src.zxbasm.label import Label From c4794a7a570ce7c5bc41038132d544c0f80bb41d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 21:51:02 +0000 Subject: [PATCH 127/169] build(deps): bump pymdown-extensions from 10.16.1 to 10.21.3 in /docs Bumps [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) from 10.16.1 to 10.21.3. - [Release notes](https://github.com/facelessuser/pymdown-extensions/releases) - [Commits](https://github.com/facelessuser/pymdown-extensions/compare/10.16.1...10.21.3) --- updated-dependencies: - dependency-name: pymdown-extensions dependency-version: 10.21.3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 02ef8eea7..685fa14f4 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -20,7 +20,7 @@ paginate==0.5.7 pathspec==0.12.1 platformdirs==4.3.6 Pygments==2.18.0 -pymdown-extensions==10.16.1 +pymdown-extensions==10.21.3 python-dateutil==2.9.0.post0 PyYAML==6.0.2 pyyaml_env_tag==0.1 From 19c2909193d832a31475d40cf6ce17f7285d0992 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Tue, 26 May 2026 00:15:37 +0200 Subject: [PATCH 128/169] fix: crash un undeclared substring assign --- pyproject.toml | 2 +- src/api/errmsg.py | 7 + src/api/symboltable/symboltable.py | 11 +- src/zxbc/zxbparser.py | 3 +- .../arch/zx48k/letsubstr_undecl.asm | 446 ++++++++++++++++++ .../arch/zx48k/letsubstr_undecl.bas | 2 + tests/functional/cmdline/test_warning.txt | 3 + 7 files changed, 466 insertions(+), 8 deletions(-) create mode 100644 tests/functional/arch/zx48k/letsubstr_undecl.asm create mode 100644 tests/functional/arch/zx48k/letsubstr_undecl.bas diff --git a/pyproject.toml b/pyproject.toml index 73189d425..f52bc577d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,7 @@ pytest-xdist = "*" setuptools = ">=70.1.1,<79.0.0" [build-system] -requires = ["poetry-core>=2.0.0"] +requires = ["poetry-core>=2.4.1"] build-backend = "poetry.core.masonry.api" [tool.poe.tasks] diff --git a/src/api/errmsg.py b/src/api/errmsg.py index ec143223d..4a87a0cd2 100644 --- a/src/api/errmsg.py +++ b/src/api/errmsg.py @@ -19,6 +19,7 @@ "register_warning", "warning", "warning_not_used", + "warning_uninitalized_string_var", ) @@ -127,6 +128,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/symboltable/symboltable.py b/src/api/symboltable/symboltable.py index 70e66ba54..da01eec75 100644 --- a/src/api/symboltable/symboltable.py +++ b/src/api/symboltable/symboltable.py @@ -132,7 +132,7 @@ def declare(self, id_: str, lineno: int, entry: symbols.ID | symbols.TYPE) -> No 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 @@ -204,7 +204,7 @@ def check_is_undeclared( ) 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. @@ -342,7 +342,7 @@ def access_id( 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. @@ -442,10 +442,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: diff --git a/src/zxbc/zxbparser.py b/src/zxbc/zxbparser.py index 0beeb0ebb..c213eb5a1 100755 --- a/src/zxbc/zxbparser.py +++ b/src/zxbc/zxbparser.py @@ -1265,7 +1265,8 @@ def p_substr_assignment_no_let(p): return if entry.class_ == CLASS.unknown: - entry.class_ = CLASS.var + errmsg.warning_uninitalized_string_var(p.lineno(1), entry.name) + entry.to_var() if p[6].type_ != Type.string: errmsg.syntax_error_expected_string(p.lineno(5), p[6].type_) 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/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' From 90a6638b49b42eca639d65dbd388f61207c16116 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 30 May 2026 23:24:45 +0200 Subject: [PATCH 129/169] tests: add test for outfmt --- tests/outfmt/__init__.py | 0 tests/outfmt/test_binary.py | 27 +++++++++++++ tests/outfmt/test_sna.py | 57 +++++++++++++++++++++++++++ tests/outfmt/test_tap.py | 63 ++++++++++++++++++++++++++++++ tests/outfmt/test_tzx.py | 77 +++++++++++++++++++++++++++++++++++++ tests/outfmt/test_z80.py | 57 +++++++++++++++++++++++++++ 6 files changed, 281 insertions(+) create mode 100644 tests/outfmt/__init__.py create mode 100644 tests/outfmt/test_binary.py create mode 100644 tests/outfmt/test_sna.py create mode 100644 tests/outfmt/test_tap.py create mode 100644 tests/outfmt/test_tzx.py create mode 100644 tests/outfmt/test_z80.py diff --git a/tests/outfmt/__init__.py b/tests/outfmt/__init__.py new file mode 100644 index 000000000..e69de29bb 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() From 5d07d63c032cf9cfe09bebe3b23aeada9b8fb1dc Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 30 May 2026 23:38:25 +0200 Subject: [PATCH 130/169] typing: improve typing --- src/outfmt/binary.py | 16 ++++++++-------- src/outfmt/codeemitter.py | 12 ++++++------ src/outfmt/gensnapshot.py | 13 ++++++------- src/outfmt/sna.py | 28 ++++++++++++++-------------- src/outfmt/tzx.py | 27 ++++++++++++++------------- src/outfmt/z80.py | 14 +++++++------- 6 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/outfmt/binary.py b/src/outfmt/binary.py index 9c4a64f4f..fdd127d83 100644 --- a/src/outfmt/binary.py +++ b/src/outfmt/binary.py @@ -13,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 42f537fee..3e1d304e6 100644 --- a/src/outfmt/codeemitter.py +++ b/src/outfmt/codeemitter.py @@ -16,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 cf6e2b712..12999e68a 100644 --- a/src/outfmt/gensnapshot.py +++ b/src/outfmt/gensnapshot.py @@ -28,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. @@ -69,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 9d07ccbc2..1c26c1544 100644 --- a/src/outfmt/sna.py +++ b/src/outfmt/sna.py @@ -14,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: @@ -90,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/tzx.py b/src/outfmt/tzx.py index fa679923b..4be8c046f 100755 --- a/src/outfmt/tzx.py +++ b/src/outfmt/tzx.py @@ -32,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]) @@ -44,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 @@ -64,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) @@ -122,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 34ae1ba96..2cb6c7db5 100644 --- a/src/outfmt/z80.py +++ b/src/outfmt/z80.py @@ -168,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""" From b4ea0fd9b3b6ee934359409cfc95515c538828e0 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 31 May 2026 00:42:08 +0200 Subject: [PATCH 131/169] build: upgrade to Python 3.14 --- .github/workflows/run-tests.yml | 6 +++--- .pre-commit-config.yaml | 2 +- poetry.lock | 6 +++--- pyproject.toml | 4 ++-- src/api/config.py | 4 ++-- src/api/optimize.py | 2 +- src/api/symboltable/scope.py | 3 +-- src/api/utils.py | 6 +++--- src/arch/z80/optimizer/helpers.py | 8 ++------ src/arch/z80/peephole/engine.py | 4 ++-- src/arch/z80/peephole/evaluator.py | 4 ++-- src/ast_/ast.py | 4 ++-- src/ast_/visitor.py | 20 +++++++++----------- src/zxbpp/prepro/id_.py | 2 +- src/zxbpp/prepro/macrocall.py | 7 +++---- src/zxbpp/prepro/operators.py | 4 ++-- tests/functional/cmdline/test_cmdline.txt | 6 +++--- tests/functional/test.py | 2 +- 18 files changed, 43 insertions(+), 51 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 657fe4481..7a583cc15 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -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 @@ -34,7 +34,7 @@ jobs: 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/.pre-commit-config.yaml b/.pre-commit-config.yaml index 42cff9ce8..d69ca1233 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.9.4 + rev: v0.15.12 hooks: # Run the linter. - id: ruff diff --git a/poetry.lock b/poetry.lock index f8fb948fc..ca40be381 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.4.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. [[package]] name = "bump2version" @@ -1098,5 +1098,5 @@ watchmedo = ["PyYAML (>=3.10)"] [metadata] lock-version = "2.1" -python-versions = ">=3.11" -content-hash = "d4834e154177290ee17b2c82c065ff8a39079065d02e18af40fe4bea4c629fb2" +python-versions = ">=3.12" +content-hash = "aa734f52ed314d0af56c786d5eae67b0964d6d199359f642a10418b5cafd1866" diff --git a/pyproject.toml b/pyproject.toml index f52bc577d..387408eb5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ license-files = [ "LICENSE.txt" ] readme = "README.md" -requires-python = ">=3.11" +requires-python = ">=3.12" keywords = ["compiler", "zxspectrum", "BASIC", "z80"] classifiers = [ "Development Status :: 5 - Production/Stable", @@ -130,7 +130,7 @@ follow_imports = "skip" [tool.ruff] line-length = 120 -target-version = "py311" +target-version = "py314" exclude = [ ".venv/", "venv/", diff --git a/src/api/config.py b/src/api/config.py index 9923394e9..7321fed46 100644 --- a/src/api/config.py +++ b/src/api/config.py @@ -115,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) @@ -157,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/optimize.py b/src/api/optimize.py index c888d8f2f..41284cb6c 100644 --- a/src/api/optimize.py +++ b/src/api/optimize.py @@ -65,7 +65,7 @@ def filter_inorder( node, filter_func: Callable[[Any], bool], child_selector: Callable[[Ast], bool] = lambda x: True, - ) -> Generator[Ast, None, None]: + ) -> Generator[Ast]: """Visit the tree inorder, but only those that return true for filter_func and visiting children which return true for child_selector. """ diff --git a/src/api/symboltable/scope.py b/src/api/symboltable/scope.py index 9e938c120..621d98903 100644 --- a/src/api/symboltable/scope.py +++ b/src/api/symboltable/scope.py @@ -6,7 +6,6 @@ # -------------------------------------------------------------------- from collections import OrderedDict -from typing import Optional from src.api.config import OPTIONS from src.symbols.id_ import SymbolID @@ -37,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/utils.py b/src/api/utils.py index cf6173ebc..ec44f24b1 100644 --- a/src/api/utils.py +++ b/src/api/utils.py @@ -36,13 +36,13 @@ 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_)) @@ -175,7 +175,7 @@ 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): diff --git a/src/arch/z80/optimizer/helpers.py b/src/arch/z80/optimizer/helpers.py index 9c041d2c2..ececec619 100644 --- a/src/arch/z80/optimizer/helpers.py +++ b/src/arch/z80/optimizer/helpers.py @@ -6,7 +6,7 @@ # -------------------------------------------------------------------- from collections.abc import Iterable, Mapping -from typing import Any, Final, TypeVar, cast +from typing import Any, Final, cast from . import patterns @@ -48,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( [ @@ -493,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/peephole/engine.py b/src/arch/z80/peephole/engine.py index d76ff8e4c..484c6a2b5 100644 --- a/src/arch/z80/peephole/engine.py +++ b/src/arch/z80/peephole/engine.py @@ -77,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) @@ -95,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 0182c5580..c01ebf02f 100644 --- a/src/arch/z80/peephole/evaluator.py +++ b/src/arch/z80/peephole/evaluator.py @@ -238,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_) @@ -248,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/ast_/ast.py b/src/ast_/ast.py index 4c1094afd..69da2bb68 100644 --- a/src/ast_/ast.py +++ b/src/ast_/ast.py @@ -28,14 +28,14 @@ def token(self): class NodeVisitor(GenericNodeVisitor[Ast]): def _visit(self, node: Ast): - meth: Callable[[Ast], Generator[Ast | Any, Any, None]] = getattr( + 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, None]: + def generic_visit(self, node: Ast) -> Generator[Ast | Any, Any]: for i, child in enumerate(node.children): node.children[i] = yield self.visit(child) diff --git a/src/ast_/visitor.py b/src/ast_/visitor.py index 106adbf56..e6aea3ffd 100644 --- a/src/ast_/visitor.py +++ b/src/ast_/visitor.py @@ -3,21 +3,19 @@ from abc import abstractmethod from collections.abc import Generator from types import GeneratorType -from typing import Final, Generic, NamedTuple, TypeVar +from typing import Final, NamedTuple __all__: Final[tuple[str, ...]] = ("GenericNodeVisitor",) -_T = TypeVar("_T") +class ToVisit[T](NamedTuple): + obj: T -class ToVisit(NamedTuple, Generic[_T]): - obj: _T - -class GenericNodeVisitor(Generic[_T]): - def visit(self, node: _T | None) -> _T | Generator[_T | None, None, None] | None: - stack: list[_T | GeneratorType] = [ToVisit[_T](node) if node is not None else None] - last_result: _T | None = None +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: @@ -36,7 +34,7 @@ def visit(self, node: _T | None) -> _T | Generator[_T | None, None, None] | None return last_result @abstractmethod - def _visit(self, node: _T): ... + def _visit(self, node: T): ... @abstractmethod - def generic_visit(self, node: _T) -> Generator[_T | None, None, None]: ... + def generic_visit(self, node: T) -> Generator[T | None]: ... diff --git a/src/zxbpp/prepro/id_.py b/src/zxbpp/prepro/id_.py index 3c397b749..322b40e2f 100644 --- a/src/zxbpp/prepro/id_.py +++ b/src/zxbpp/prepro/id_.py @@ -53,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 17a302573..880dda9d6 100644 --- a/src/zxbpp/prepro/macrocall.py +++ b/src/zxbpp/prepro/macrocall.py @@ -7,7 +7,6 @@ import copy import re -from typing import Union from src.api.debug import __DEBUG__ from src.zxbpp import prepro @@ -26,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. """ @@ -44,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_() @@ -102,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 727b13c94..2589b5fd9 100644 --- a/src/zxbpp/prepro/operators.py +++ b/src/zxbpp/prepro/operators.py @@ -22,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() @@ -41,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/tests/functional/cmdline/test_cmdline.txt b/tests/functional/cmdline/test_cmdline.txt index 3933a993d..3fcf536b1 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] diff --git a/tests/functional/test.py b/tests/functional/test.py index 91a330149..930f29041 100755 --- a/tests/functional/test.py +++ b/tests/functional/test.py @@ -100,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 From 9b3f41c9d54cb7502397bad1daa9df43246102e3 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 31 May 2026 01:09:08 +0200 Subject: [PATCH 132/169] feat: check Python 3.12+ is used --- src/api/python_version_check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python_version_check.py b/src/api/python_version_check.py index 348707bc4..78f31577f 100644 --- a/src/api/python_version_check.py +++ b/src/api/python_version_check.py @@ -8,7 +8,7 @@ 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(): From c39ebf5b5105e19919825d7e959f308da1b86bf8 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Tue, 30 Jun 2026 16:07:03 +0200 Subject: [PATCH 133/169] Add zx81sd architecture for ZX81 + SD81 Booster hardware New target architecture that compiles ZX BASIC programs for the ZX81 with SD81 Booster expansion (512KB RAM, SD card, AY sound, FPGA video). Key design decisions: - Inherits zx48k runtime; only overrides files specific to SD81 hardware - Two-stage boot: stage 1 at $6000 (external BASIC loader), stage 2 at $0100 - Flat binary ORG $0000 with RST vector table ($0000-$00FF) and 8KB page layout - Spectrum HiRes mode: framebuffer at $C000 (block 6), attrs at $D800 - Sysvars mapped to $8000+ (blocks 4-5) instead of Spectrum ROM addresses - SCREEN_ADDR / SCREEN_ATTR_ADDR as RAM pointers (not EQU constants) to match the indirect addressing used by the inherited zx48k runtime - CHARS pointer initialised to font_base - 256 (Spectrum convention) - Bold/italic FLAGS2 check uses direct LD A,(FLAGS2) without PUSH/POP AF to avoid destroying the Z flag from BIT before CALL NZ New files: src/arch/zx81sd/ backend (main.py, generic.py) src/lib/arch/zx81sd/runtime/ sysvars, bootstrap, charset (INCBIN specfont.bin), print, border, pause, vsync, paging, vectors, ... Co-Authored-By: Claude Sonnet 4.6 --- src/arch/__init__.py | 1 + src/arch/zx81sd/__init__.py | 34 ++ src/arch/zx81sd/backend/__init__.py | 29 + src/arch/zx81sd/backend/generic.py | 29 + src/arch/zx81sd/backend/main.py | 202 +++++++ src/lib/arch/zx81sd/runtime/arch_config.asm | 13 + src/lib/arch/zx81sd/runtime/bootstrap.asm | 80 +++ src/lib/arch/zx81sd/runtime/border.asm | 16 + src/lib/arch/zx81sd/runtime/break.asm | 62 +++ src/lib/arch/zx81sd/runtime/charset.asm | 11 + src/lib/arch/zx81sd/runtime/error.asm | 40 ++ src/lib/arch/zx81sd/runtime/fp_calc.asm | 34 ++ src/lib/arch/zx81sd/runtime/paging.asm | 54 ++ src/lib/arch/zx81sd/runtime/pause.asm | 29 + src/lib/arch/zx81sd/runtime/pixel_addr.asm | 71 +++ src/lib/arch/zx81sd/runtime/po_gr_1.asm | 88 +++ src/lib/arch/zx81sd/runtime/print.asm | 586 ++++++++++++++++++++ src/lib/arch/zx81sd/runtime/random.asm | 102 ++++ src/lib/arch/zx81sd/runtime/specfont.bin | Bin 0 -> 768 bytes src/lib/arch/zx81sd/runtime/sysvars.asm | 57 ++ src/lib/arch/zx81sd/runtime/vectors.asm | 66 +++ src/lib/arch/zx81sd/runtime/vsync.asm | 49 ++ src/zxbpp/zxbpp.py | 8 + 23 files changed, 1661 insertions(+) create mode 100644 src/arch/zx81sd/__init__.py create mode 100644 src/arch/zx81sd/backend/__init__.py create mode 100644 src/arch/zx81sd/backend/generic.py create mode 100644 src/arch/zx81sd/backend/main.py create mode 100644 src/lib/arch/zx81sd/runtime/arch_config.asm create mode 100644 src/lib/arch/zx81sd/runtime/bootstrap.asm create mode 100644 src/lib/arch/zx81sd/runtime/border.asm create mode 100644 src/lib/arch/zx81sd/runtime/break.asm create mode 100644 src/lib/arch/zx81sd/runtime/charset.asm create mode 100644 src/lib/arch/zx81sd/runtime/error.asm create mode 100644 src/lib/arch/zx81sd/runtime/fp_calc.asm create mode 100644 src/lib/arch/zx81sd/runtime/paging.asm create mode 100644 src/lib/arch/zx81sd/runtime/pause.asm create mode 100644 src/lib/arch/zx81sd/runtime/pixel_addr.asm create mode 100644 src/lib/arch/zx81sd/runtime/po_gr_1.asm create mode 100644 src/lib/arch/zx81sd/runtime/print.asm create mode 100644 src/lib/arch/zx81sd/runtime/random.asm create mode 100644 src/lib/arch/zx81sd/runtime/specfont.bin create mode 100644 src/lib/arch/zx81sd/runtime/sysvars.asm create mode 100644 src/lib/arch/zx81sd/runtime/vectors.asm create mode 100644 src/lib/arch/zx81sd/runtime/vsync.asm diff --git a/src/arch/__init__.py b/src/arch/__init__.py index fe5ba2cf3..241f40820 100755 --- a/src/arch/__init__.py +++ b/src/arch/__init__.py @@ -13,6 +13,7 @@ __all__ = ( "zx48k", "zxnext", + "zx81sd", ) AVAILABLE_ARCHITECTURES = __all__ 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..196556ddc --- /dev/null +++ b/src/arch/zx81sd/backend/generic.py @@ -0,0 +1,29 @@ +# -------------------------------------------------------------------- +# SPDX-License-Identifier: AGPL-3.0-or-later +# ZX81 + SD81 Booster — handler del opcode END +# -------------------------------------------------------------------- + +from src.arch.interface.quad import Quad +from src.arch.z80.backend import Bits16, common + + +def _end(ins: Quad): + """Secuencia de fin de programa para ZX81 + SD81 Booster. + + No hay ROM ni BASIC al que volver: detiene la CPU de forma segura. + Si END aparece varias veces en el programa (salidas anticipadas), + los casos posteriores generan un JP al primer bloque END emitido. + """ + 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..940279407 --- /dev/null +++ b/src/arch/zx81sd/backend/main.py @@ -0,0 +1,202 @@ +# -------------------------------------------------------------------- +# 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 + +# --------------------------------------------------------------------------- +# Constantes de arquitectura ZX81 + SD81 Booster +# --------------------------------------------------------------------------- + +# Mapa de memoria (flat, ORG $0000): +# $0000-$00FF vectors.asm — vectores RST + relleno hasta $0100 +# $0100-$0FFF stage 2 bootstrap (prólogo) + rutinas de sistema +# $1000-$7FFF runtime ZX BASIC + código del usuario (28 KB) +# $8000-$80FF sysvars del runtime +# $8100-$BFFF heap + datos del usuario (~15.75 KB) +# $C000-$D7FF bitmap pantalla Spectrum (bloque 6, página dedicada) +# $D800-$DAFF atributos pantalla +# $E000-$FFFF bloque 7 — banking de datos (mapas, sprites...) + +_ORG = 0x0000 # el binario comienza en $0000 (vectors.asm lo rellena hasta $0100) +_STAGE2_ENTRY = 0x0100 # punto de entrada del stage 2 bootstrap + +_HEAP_ADDR = 0x8100 # heap en zona de datos ($8100-$BFFF) +_HEAP_SIZE = 0x3EFF # ~15.75 KB + +# Páginas SD81 asignadas a cada bloque (las carga el BASIC antes del salto) +# Página 8 → bloque 0 ($0000-$1FFF) ← el stage 1 solo mapea este +# Página 9 → bloque 1 ($2000-$3FFF) ┐ +# Página 10 → bloque 2 ($4000-$5FFF) │ el stage 2 (aquí) mapea estos +# Página 11 → bloque 3 ($6000-$7FFF) │ +# Página 12 → bloque 4 ($8000-$9FFF) │ (datos, no ejecutable sin MC45) +# Página 13 → bloque 5 ($A000-$BFFF) ┘ + +_PAGE_MAP = [ + (1, 9), # bloque 1 → página 9 + (2, 10), # bloque 2 → página 10 + (3, 11), # bloque 3 → página 11 + (4, 12), # bloque 4 → página 12 + (5, 13), # bloque 5 → página 13 +] + +_SD81_PAGE_PORT = 0xE7 # puerto mapeador de memoria (modo full: OUT (C), A) +_STACK_TOP = 0x7FFF # pila al tope de la zona ejecutable + + +def _map_block(block: int, page: int) -> list[str]: + """Emite OUT (C), A para mapear una página a un bloque (modo full, 64 pág.).""" + 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) + OPTIONS(Action.ADD_IF_NOT_DEFINED, name="heap_size", type=int, + default=_HEAP_SIZE, ignore_none=True) + OPTIONS(Action.ADD_IF_NOT_DEFINED, name="heap_address", type=int, + default=_HEAP_ADDR, ignore_none=False) + 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]: + """ + Prólogo del programa para ZX81 + SD81 Booster. + + Estructura del binario generado (ORG $0000): + $0000-$00FF vectors.asm (vectores RST, incluido desde sysvars.asm) + $0100 START_LABEL (entrada del stage 2 bootstrap) + - mapeo de bloques 1-5 a sus páginas definitivas + - SP = $7FFF + - CALL (SD81_INIT_SYSVARS, etc.) + - JP __MAIN_LABEL__ + + El stage 1 bootstrap (externo, en el cargador BASIC a $6000) ya ha: + - Configurado HFILE=$C000 y activado modo Spectrum (POKE 2045, 172) + - Desactivado el IO mapeado en memoria (POKE 2056) + - Desactivado interrupciones (DI) + - Mapeado bloque 0 → página 8 (JP $0100 ya ejecuta en RAM limpia) + """ + # -- Definiciones del heap ------------------------------------------ + 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}" + ) + + # -- Tabla de vectores RST ($0000-$00FF) ---------------------------- + # Debe ser lo primero en el binario. Cada RST ocupa 8 bytes. + # Usamos org absoluto para cada entrada: el ensamblador rellena los + # huecos con ceros, lo que es correcto para una zona no ejecutable. + output = ["org $0000"] + output.append("jp $0100") # $0000: reset / RST 0 → stage 2 + output.append("org $0008") + output.append("di") # $0008: RST $08 (error handler Spectrum) + output.append("halt") + output.append("org $0010") + output.append("di") # $0010-$0037: RSTs no usados + 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("di") # $0028: RST $28 FP calc — nunca con __ZXB_NO_FLOAT + output.append("halt") + output.append("org $0030") + output.append("di") + output.append("halt") + output.append("org $0038") + output.append("di") # $0038: RST $38 IM1 — DI permanente, nunca llega + output.append("halt") + output.append("org $0066") + output.append("retn") # $0066: NMI desactivada, pero el vector debe existir + + # -- Stage 2 bootstrap en $0100 ------------------------------------ + output.append(f"org {_STAGE2_ENTRY}") + output.append(f"{common.START_LABEL}:") + + if OPTIONS.headerless: + output.extend(heap_init) + return output + + # Mapeo de bloques 1-5 a sus páginas definitivas. + # El bloque 0 ya fue mapeado a la página 8 por el stage 1 externo. + for block, page in _PAGE_MAP: + output.extend(_map_block(block, page)) + + # Pila al tope de la zona ejecutable + output.append(f"ld sp, {_STACK_TOP:#06x}") + + # Llamadas a rutinas de inicialización registradas con #init + # (SD81_INIT_SYSVARS y cualquier otra del runtime incluido) + output.extend(f"call {label}" for label in sorted(common.INITS)) + + # Salto al programa del usuario + 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/lib/arch/zx81sd/runtime/arch_config.asm b/src/lib/arch/zx81sd/runtime/arch_config.asm new file mode 100644 index 000000000..21a847240 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/arch_config.asm @@ -0,0 +1,13 @@ +; Configuración de arquitectura ZX81 + SD81 Booster +; Este fichero se incluye antes que cualquier otro del runtime. +; +; Activa la implementación de scroll por software (sin ROM Spectrum) +; y desactiva las funcionalidades que dependen de la ROM Spectrum. + +; Usar scroll por buffer propio (en vez de CALL $0DFEh 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) +; Comentar esta línea cuando se integre fp_calc.asm +#define __ZXB_NO_FLOAT diff --git a/src/lib/arch/zx81sd/runtime/bootstrap.asm b/src/lib/arch/zx81sd/runtime/bootstrap.asm new file mode 100644 index 000000000..5b629d041 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/bootstrap.asm @@ -0,0 +1,80 @@ +; 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 + +#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: primer carácter definible por el usuario (CHR$(144) en Spectrum) + ; = font base + (144-32)*8 = font + 896. Con la convención CHARS-256: + ; UDG = CHARS + 144*8 = __ZX81SD_CHARSET - 256 + 1152 = __ZX81SD_CHARSET + 896 + ld hl, __ZX81SD_CHARSET + 896 + ld (UDG), hl + + ; 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 ; $801D ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $801F ← $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 + ld hl, $F838 ; ATTR_T=$38, MASK_T=$F8 + ld (ATTR_T), hl + + ; 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 + + 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..464812eda --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/border.asm @@ -0,0 +1,16 @@ +; 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. +; +; Entrada: A = color de borde (bits 2-0, igual que en Spectrum) +; Sustituye a: BORDER EQU $229Bh (ROM Spectrum) + + push namespace core + +SD81_ULA_PORT EQU $FB ; Puerto ULA emulado en modo HiRes Spectrum + +BORDER: + 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..20a0cad25 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/charset.asm @@ -0,0 +1,11 @@ +; 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" + + 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..4bd801895 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/fp_calc.asm @@ -0,0 +1,34 @@ +; FP_CALC — Calculador de coma flotante +; Sustituye a RST $28h (ROM Spectrum, dirección $0E9Bh) +; +; ESTADO: STUB — Primera versión sin soporte float. +; La arquitectura zx81sd compila con __ZXB_NO_FLOAT activado (arch_config.asm), +; por lo que este fichero no se incluye en compilaciones normales. +; +; TODO: Integrar el calculador FP del Spectrum reubicado en $0000-$0FFF +; o una reimplementación libre compatible con el protocolo de bytecodes. +; Cuando esté listo: +; 1. Eliminar #define __ZXB_NO_FLOAT de arch_config.asm +; 2. Sustituir FP_CALC_ENTRY por la implementación real +; 3. Verificar que todos los RST $28h del runtime se convierten +; en CALL FP_CALC_ENTRY (gestionado por el backend) +; +; Protocolo RST $28h del Spectrum: +; - Los bytecodes de operación siguen inmediatamente al CALL en memoria +; - El calculador lee el flujo de bytecodes desde (SP) tras el CALL +; - El stack de coma flotante (5 bytes/número) es independiente del stack Z80 +; - El registro E apunta al primer bytecode +; +; Referencias: +; ROM Spectrum disassembly: https://skoolkid.github.io/rom/ +; FP calculator entry: $0E9Bh — routine CALCULATE + + push namespace core + +; Punto de entrada del calculador FP (para cuando se integre la versión real) +FP_CALC_ENTRY: + ; STUB: no hacer nada — el compilador no debería llegar aquí + ; con __ZXB_NO_FLOAT activo + ret + + 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..1e3c065c4 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/pixel_addr.asm @@ -0,0 +1,71 @@ +; PIXEL_ADDR — Calcula la dirección del byte de pantalla para coordenadas (Y, X) +; Sustituye a PIXEL_ADDR EQU $22ACh (ROM Spectrum) +; +; Convención (igual que la ROM Spectrum, para no modificar plot.asm): +; Entrada: A = 191 - Y_real, C = X (0-255) +; Salida: HL = offset dentro del bitmap (sin la base de pantalla) +; A = máscara de bit ($80 >> (X AND 7)) +; Destruye: B, D +; +; Organización bitmap Spectrum: +; bits [12:11] = tercio vertical (V AND $C0) >> 6 +; bits [10: 8] = línea en tercio (V AND $07) +; bits [ 7: 5] = fila de carácter (V AND $38) >> 3 +; bits [ 4: 0] = columna de byte X >> 3 + + push namespace core + +PIXEL_ADDR: + PROC + LOCAL BIT_LOOP, DONE_BITS + + ld d, a ; D = V = 191-Y + + ; -- Byte alto del offset: tercio (bits 12-11) + línea en tercio (bits 10-8) -- + ld a, d + and $C0 ; A = tercio * 64 + srl a + srl a + srl a ; A = tercio * 8 → bits [5:3] de H + ld h, a + ld a, d + and $07 ; A = línea en tercio (0-7) → bits [2:0] de H + or h + ld h, a ; H = (tercio<<3) | línea_en_tercio + + ; -- Byte bajo del offset: fila de carácter (bits 7-5) + columna de byte (bits 4-0) -- + ld a, d + and $38 ; A = fila_de_char * 8 + rrca + rrca + rrca ; A = fila_de_char (0-7) + add a, a + add a, a + add a, a + add a, a + add a, a ; A = fila_de_char * 32 + 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 + + ; -- Máscara de bit: $80 >> (X AND 7) -- + ld a, c + and $07 ; A = X AND 7 + ld b, a + ld a, $80 + or a + jr z, DONE_BITS ; si X AND 7 = 0, no rotar +BIT_LOOP: + rrca + djnz BIT_LOOP +DONE_BITS: + ret ; HL = offset en bitmap, A = máscara bit + + 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..6f78aaf99 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/po_gr_1.asm @@ -0,0 +1,88 @@ +; PO_GR_1 — Genera el patrón de bits para caracteres gráficos 128-143 +; Sustituye a PO_GR_1 EQU $0B38h (ROM Spectrum) +; +; Los caracteres gráficos Spectrum (CHR$ 128 a CHR$ 143) son bloques +; de pixels 2×2 en cuadrantes. El nibble bajo del código determina +; qué cuadrantes están encendidos (bit 0 = TL, 1 = TR, 2 = BL, 3 = BR). +; +; Entrada: B = código de carácter (128-143), solo se usan bits 3-0 +; Salida: MEM0 (5 bytes) = patrón de 8 bytes del carácter generado +; HL = MEM0 (para que plot lo use directamente) +; Destruye: A, B, C, D, E +; +; Patrón generado: 4 líneas top + 4 líneas bottom +; top_byte = $FF si bit 0 (TL) || $F0 si bit 1 (TR) || combinación +; bot_byte = igual pero mirando bits 2 (BL) y 3 (BR) +; +; Codificación exacta: +; bit 0 = cuadrante superior izquierdo → pixels $F0 en filas 0-3 +; bit 1 = cuadrante superior derecho → pixels $0F en filas 0-3 +; bit 2 = cuadrante inferior izquierdo → pixels $F0 en filas 4-7 +; bit 3 = cuadrante inferior derecho → pixels $0F en filas 4-7 + +#include once + + push namespace core + +PO_GR_1: + PROC + + ld a, b ; A = código gráfico (128-143) + and $0F ; quedarnos solo con los 4 bits de cuadrante + + ; Calcular byte superior (filas 0-3) + ld c, $00 + bit 0, a ; TL activo? + jr z, NO_TL + ld c, $F0 +NO_TL: + bit 1, a ; TR activo? + jr z, NO_TR + ld b, c + or $0F + ld c, a + ld a, b +NO_TR: + ; C = byte superior + + ; Calcular byte inferior (filas 4-7) + ld d, a ; guardar A (bits cuadrante) + ld e, $00 + bit 2, d ; BL activo? + jr z, NO_BL + ld e, $F0 +NO_BL: + bit 3, d ; BR activo? + jr z, NO_BR + ld a, e + or $0F + ld e, a +NO_BR: + ; E = byte inferior + + ; Rellenar MEM0 con 8 bytes: 4x C, 4x E + ld hl, MEM0 + ld a, c + ld (hl), a + inc hl + ld (hl), a + inc hl + ld (hl), a + inc hl + ld (hl), a + inc hl + ld a, e + ld (hl), a + inc hl + ld (hl), a + inc hl + ld (hl), a + inc hl + ld (hl), a + + ld hl, MEM0 ; devolver puntero al patrón + 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..97c589c7a --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/print.asm @@ -0,0 +1,586 @@ +; 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 + +# 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 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/specfont.bin b/src/lib/arch/zx81sd/runtime/specfont.bin new file mode 100644 index 0000000000000000000000000000000000000000..458e0a57ae820285073ef9a6294e81634c1b61be GIT binary patch literal 768 zcmXX^v1-FG5Iu}3qJ*F!gNFqcOr=5t8{<*xM4lGaYaB7i2fq=rw z^=^ge5FMhu8n~Y`k*2|0RXgI-?r^*$)FO0%NLr;lF)k`a-8FGPL1)UNG8oqM(;^$C(uoYxAzPl_>-#0sL&so=gdAsQ7wb@&-4W! zIcK|n<-O6s!?5PRsw(qS_Sbzu9bn_>=UJ(*m*ZiFh<=7+w`_WPJV1{*c`2ug`aj$& z{1QI*Q*E(7rf2*ByOZH?gYzrv%@Bx}u1*F0 zLP(npqO3y*Ypf4t)}<>0_q6cM%xAgoFyBy0;mWt(cff;D!(fQpZG=C7y_ZCVB4hNCdbp+^88QFJ`L{Md*TDbb R)h}%R6**l0?S8*$`U4XsW{3a) literal 0 HcmV?d00001 diff --git a/src/lib/arch/zx81sd/runtime/sysvars.asm b/src/lib/arch/zx81sd/runtime/sysvars.asm new file mode 100644 index 000000000..df1e90b3b --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/sysvars.asm @@ -0,0 +1,57 @@ +; ----------------------------------------------------------------------- +; 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.) +ATTR_T EQU SYSVAR_BASE + $0F ; DW — atributo temporal + máscara +P_FLAG EQU SYSVAR_BASE + $11 ; DB — flags de impresión (OVER/INVERSE perm.) +MEM0 EQU SYSVAR_BASE + $12 ; 5B — buffer temporal para rutinas gráficas +TV_FLAG EQU SYSVAR_BASE + $17 ; DB — flags de control de salida a pantalla +ERR_NR EQU SYSVAR_BASE + $18 ; DB — código de error (-1 = sin error) +FRAMES EQU SYSVAR_BASE + $19 ; DW — contador de frames VSYNC (software) +RANDOM_SEED_LOW EQU SYSVAR_BASE + $1B ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1D ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $1F ; DW — puntero a atributos (init: $D800) + +; Tamaño total del bloque de sysvars: $21 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/vectors.asm b/src/lib/arch/zx81sd/runtime/vectors.asm new file mode 100644 index 000000000..64a3df145 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/vectors.asm @@ -0,0 +1,66 @@ +; VECTORS — Tabla de vectores RST del Z80 para ZX81 + SD81 Booster +; +; Ocupa $0000-$00FF (primer bloque de la zona de sistema). +; Reemplaza la ROM del ZX81 (bloque 0 remapeado a página 8 por el stage 1). +; +; Tras el remapeo de bloque 0, el stage 1 salta a $0100 (stage 2 bootstrap). +; El vector RST 0 redirige allí también, por si algo fuerza un reset software. + + push namespace core + + org $0000 + +; $0000 — RST 0 / Reset software: salta al stage 2 + jp $0100 + + defs $0008 - $, $00 + +; $0008 — RST $08 (handler de errores Spectrum): redirige a __ERROR + jp __ERROR + + defs $0010 - $, $00 + +; $0010 — RST $10 (PRINT A en Spectrum): no usado, cuelga de forma segura + di + halt + + defs $0018 - $, $00 + +; $0018 — RST $18: no usado + di + halt + + defs $0020 - $, $00 + +; $0020 — RST $20: no usado + di + halt + + defs $0028 - $, $00 + +; $0028 — RST $28 (FP calculator Spectrum): stub, no debe llegarse aquí +; con __ZXB_NO_FLOAT activo el compilador no genera RST $28 + di + halt + + defs $0030 - $, $00 + +; $0030 — RST $30: no usado + di + halt + + defs $0038 - $, $00 + +; $0038 — RST $38 (IM1 interrupt): DI permanente, nunca debería llegar + di + halt + + defs $0066 - $, $00 + +; $0066 — NMI handler: NMI desactivadas por el cargador BASIC (modo FAST), +; pero el vector debe existir por si acaso + retn + + defs $0100 - $, $00 ; relleno hasta inicio del stage 2 en $0100 + + 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..424282b4a --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/vsync.asm @@ -0,0 +1,49 @@ +; ----------------------------------------------------------------------- +; VSYNC — Sincronización con el refresco de pantalla +; SD81 Booster: puerto $A7h, bit 0 = estado VSYNC +; bit 0 = 0 → pantalla pintándose (blanking activo) +; bit 0 = 1 → blanking vertical completado, inicio de nuevo frame +; +; 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 $A7 ; Puerto de datos MCU del SD81 Booster + +; VSYNC_WAIT — Espera al inicio del siguiente frame +; Destruye: A +; No modifica ningún otro registro. +VSYNC_WAIT: + PROC + LOCAL WAIT_LOW + LOCAL WAIT_HIGH + + ; Esperar a que VSYNC baje (por si estamos en medio de un pulso) +WAIT_LOW: + in a, (SD81_DATA_PORT) + rrca ; bit 0 → carry + jr c, WAIT_LOW ; si carry=1, todavía en blanking anterior + + ; Esperar el flanco de subida (inicio real del blanking) +WAIT_HIGH: + in a, (SD81_DATA_PORT) + rrca + jr nc, WAIT_HIGH ; si carry=0, aún no ha llegado el VSYNC + + 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/zxbpp/zxbpp.py b/src/zxbpp/zxbpp.py index 53c7f6449..2a81d8024 100755 --- a/src/zxbpp/zxbpp.py +++ b/src/zxbpp/zxbpp.py @@ -167,6 +167,14 @@ def set_include_path(): 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, []) From 9b9861f8e41eb866dfe05dd99e45f569b0ea49c6 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Wed, 1 Jul 2026 18:19:04 +0200 Subject: [PATCH 134/169] zx81sd: fix graphics/VSYNC, add native keyboard driver and beeper - sysvars.asm/bootstrap.asm: add missing MASK_P sysvar between ATTR_P and ATTR_T so COPY_ATTR no longer corrupts INK/PAPER attributes. - pixel_addr.asm: rework to return bit position directly (matches the interface plot.asm/draw.asm expect) instead of a rotated bitmask. - vsync.asm: fix port ($AF) and semantics (bits 1-6 = pulse counter since last read, auto-resets on IN) per SD81 Booster hardware. - arch_config.asm: pull in pixel_addr.asm/po_gr_1.asm so ROM-call substitutes are available project-wide. - Add native ZX81 keyboard driver (io/keyboard/keyscan.asm) since no ROM is mapped at runtime: scans the $FEFE matrix directly and decodes to ASCII (not the ZX81 ROM's own charset/token codes, since this runtime prints via the Spectrum/ASCII charset). Wire it into INKEY$ (io/keyboard/inkey.asm) and a new stdlib/input.bas that replaces the Spectrum-ROM-dependent LAST_K/BEEPER polling. - beep.asm: key click using the SD81 Booster's emulated Spectrum ULA port ($FBh, bits 4-3), sharing a shadow byte with border.asm so border color and beeper bits don't clobber each other. Co-Authored-By: Claude --- src/lib/arch/zx81sd/runtime/arch_config.asm | 28 +- src/lib/arch/zx81sd/runtime/beep.asm | 41 +++ src/lib/arch/zx81sd/runtime/bootstrap.asm | 8 +- src/lib/arch/zx81sd/runtime/border.asm | 14 +- src/lib/arch/zx81sd/runtime/draw.asm | 335 ++++++++++++++++++ .../arch/zx81sd/runtime/io/keyboard/inkey.asm | 53 +++ .../zx81sd/runtime/io/keyboard/keyscan.asm | 175 +++++++++ src/lib/arch/zx81sd/runtime/pixel_addr.asm | 77 ++-- src/lib/arch/zx81sd/runtime/plot.asm | 90 +++++ src/lib/arch/zx81sd/runtime/sysvars.asm | 24 +- src/lib/arch/zx81sd/runtime/vsync.asm | 27 +- src/lib/arch/zx81sd/stdlib/input.bas | 137 +++++++ 12 files changed, 929 insertions(+), 80 deletions(-) create mode 100644 src/lib/arch/zx81sd/runtime/beep.asm create mode 100644 src/lib/arch/zx81sd/runtime/draw.asm create mode 100644 src/lib/arch/zx81sd/runtime/io/keyboard/inkey.asm create mode 100644 src/lib/arch/zx81sd/runtime/io/keyboard/keyscan.asm create mode 100644 src/lib/arch/zx81sd/runtime/plot.asm create mode 100644 src/lib/arch/zx81sd/stdlib/input.bas diff --git a/src/lib/arch/zx81sd/runtime/arch_config.asm b/src/lib/arch/zx81sd/runtime/arch_config.asm index 21a847240..eac1425cf 100644 --- a/src/lib/arch/zx81sd/runtime/arch_config.asm +++ b/src/lib/arch/zx81sd/runtime/arch_config.asm @@ -1,13 +1,29 @@ -; Configuración de arquitectura ZX81 + SD81 Booster -; Este fichero se incluye antes que cualquier otro del runtime. +; arch_config.asm — Constantes de arquitectura ZX81 + SD81 Booster ; -; Activa la implementación de scroll por software (sin ROM Spectrum) -; y desactiva las funcionalidades que dependen de la ROM Spectrum. +; Sobreescribe zx48k/runtime/arch_config.asm para redirigir las +; llamadas ROM del runtime hacia implementaciones propias en RAM. -; Usar scroll por buffer propio (en vez de CALL $0DFEh ROM Spectrum) +; --------------------------------------------------------------------------- +; 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) -; Comentar esta línea cuando se integre fp_calc.asm +; --------------------------------------------------------------------------- #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/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 index 5b629d041..a4d5452b9 100644 --- a/src/lib/arch/zx81sd/runtime/bootstrap.asm +++ b/src/lib/arch/zx81sd/runtime/bootstrap.asm @@ -42,11 +42,11 @@ SD81_INIT_SYSVARS: ; 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 ; $801D ← $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 ld (DFCC), hl ; cursor bitmap al inicio de pantalla ld hl, $D800 - ld (SCREEN_ATTR_ADDR), hl ; $801F ← $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 ld (DFCCL), hl ; cursor attrs al inicio de atributos ; COORDS: último punto PLOT = (0,0) @@ -58,7 +58,9 @@ SD81_INIT_SYSVARS: ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum ld a, $38 ld (ATTR_P), a - ld hl, $F838 ; ATTR_T=$38, MASK_T=$F8 + 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 ; Flags a cero diff --git a/src/lib/arch/zx81sd/runtime/border.asm b/src/lib/arch/zx81sd/runtime/border.asm index 464812eda..cb7f3ce6e 100644 --- a/src/lib/arch/zx81sd/runtime/border.asm +++ b/src/lib/arch/zx81sd/runtime/border.asm @@ -1,15 +1,25 @@ ; 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) - push namespace core +#include once -SD81_ULA_PORT EQU $FB ; Puerto ULA emulado en modo HiRes Spectrum + 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 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/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..b51b36803 --- /dev/null +++ b/src/lib/arch/zx81sd/runtime/io/keyboard/keyscan.asm @@ -0,0 +1,175 @@ +; --------------------------------------------------------------------------- +; 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. +; +; 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 la tabla de decodificacion de abajo +; traduce 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 + +; --------------------------------------------------------------------------- +; __ZX81SD_KEYSCAN +; +; Escanea las 8 filas de la matriz y decodifica la primera tecla +; encontrada (si hay varias pulsadas a la vez, solo se detecta una). +; +; Devuelve: +; A = codigo ASCII de la tecla pulsada, o 0 si no hay ninguna pulsada, +; o si la tecla no tiene equivalente ASCII (ver nota arriba), o si +; solo esta pulsada la tecla SHIFT sola. +; Flag Z activo si A = 0 +; +; Registros modificados: AF, BC, DE, HL +; --------------------------------------------------------------------------- +__ZX81SD_KEYSCAN: + PROC + 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 LOOKUP + LOCAL NO_KEY + LOCAL UNSHIFT_TABLE + LOCAL SHIFT_TABLE + + ; D = indice de fila actual (0-7), E = flag SHIFT pulsado (0/1) + ld d, 0 + ld e, 0 + ld b, $FE ; B = mitad alta del puerto ($FEFE, $FDFE, ... $7FFE) + +ROW_LOOP: + ld c, $FE + in a, (c) ; lee la fila; bits 0-4 = columnas (0 = pulsada) + and $1F + cp $1F + jr z, NEXT_ROW ; ninguna tecla pulsada en esta fila + + ld l, a ; L = mapa de bits de columnas pulsadas + + ld a, d + or a + jr nz, FIND_COL + + ; Fila 0: la columna 0 es la tecla SHIFT (no genera caracter propio) + bit 0, l + jr nz, FIND_COL ; SHIFT no pulsado, comprobar columnas normalmente + ld e, 1 ; SHIFT pulsado + set 0, l ; descartar ese bit para no confundirlo con datos + ld a, l + cp $1F + jr z, NEXT_ROW ; en esta fila solo estaba pulsado SHIFT + +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) + + ld hl, UNSHIFT_TABLE + bit 0, e + jr z, LOOKUP + ld hl, SHIFT_TABLE + +LOOKUP: + ld c, a + ld b, 0 + add hl, bc + ld a, (hl) + or a ; fija flag Z segun corresponda + ret + +NEXT_ROW: + rlc b + inc d + ld a, d + cp 8 + jr nz, ROW_LOOP + +NO_KEY: + xor a + ret + + ; 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. +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' + +SHIFT_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, AND, 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, ',', '>', '<', '*' ; £, ',', >, <, * + + ENDP + + pop namespace diff --git a/src/lib/arch/zx81sd/runtime/pixel_addr.asm b/src/lib/arch/zx81sd/runtime/pixel_addr.asm index 1e3c065c4..524a0c129 100644 --- a/src/lib/arch/zx81sd/runtime/pixel_addr.asm +++ b/src/lib/arch/zx81sd/runtime/pixel_addr.asm @@ -1,70 +1,65 @@ -; PIXEL_ADDR — Calcula la dirección del byte de pantalla para coordenadas (Y, X) +; PIXEL_ADDR — Calcula la dirección (offset) del byte de pantalla para (B=Y, C=X) ; Sustituye a PIXEL_ADDR EQU $22ACh (ROM Spectrum) ; -; Convención (igual que la ROM Spectrum, para no modificar plot.asm): -; Entrada: A = 191 - Y_real, C = X (0-255) -; Salida: HL = offset dentro del bitmap (sin la base de pantalla) -; A = máscara de bit ($80 >> (X AND 7)) +; 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, D ; -; Organización bitmap Spectrum: -; bits [12:11] = tercio vertical (V AND $C0) >> 6 -; bits [10: 8] = línea en tercio (V AND $07) -; bits [ 7: 5] = fila de carácter (V AND $38) >> 3 -; bits [ 4: 0] = columna de byte X >> 3 +; 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 - LOCAL BIT_LOOP, DONE_BITS - ld d, a ; D = V = 191-Y + sub b ; A = 191 - Y (convierte coord Spectrum a offset desde arriba) + ld d, a ; D = V = 191-Y - ; -- Byte alto del offset: tercio (bits 12-11) + línea en tercio (bits 10-8) -- - ld a, d - and $C0 ; A = tercio * 64 - srl a - srl a - srl a ; A = tercio * 8 → bits [5:3] de H + ; -- 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, d - and $07 ; A = línea en tercio (0-7) → bits [2:0] de H + and $07 ; A = línea en tercio (0-7) or h - ld h, a ; H = (tercio<<3) | línea_en_tercio + ld h, a ; H = (tercio<<3) | línea_en_tercio - ; -- Byte bajo del offset: fila de carácter (bits 7-5) + columna de byte (bits 4-0) -- + ; -- L: fila de carácter (bits 7-5) + columna de byte (bits 4-0) -- ld a, d - and $38 ; A = fila_de_char * 8 + and $38 ; A = fila_de_char * 8 rrca rrca - rrca ; A = fila_de_char (0-7) - add a, a - add a, a - add a, a - add a, a - add a, a ; A = fila_de_char * 32 + rrca ; A = fila_de_char (0-7) + rlca + rlca + rlca + rlca + rlca ; A = fila_de_char * 32 ld b, a ld a, c rrca rrca rrca - and $1F ; A = X / 8 (columna de byte, 0-31) + and $1F ; A = X / 8 (columna de byte, 0-31) add a, b - ld l, a ; L = fila_de_char*32 + col_byte + ld l, a ; L = fila_de_char*32 + col_byte - ; -- Máscara de bit: $80 >> (X AND 7) -- + ; -- Retornar A = X AND 7 (posición del bit dentro del byte) -- ld a, c - and $07 ; A = X AND 7 - ld b, a - ld a, $80 - or a - jr z, DONE_BITS ; si X AND 7 = 0, no rotar -BIT_LOOP: - rrca - djnz BIT_LOOP -DONE_BITS: - ret ; HL = offset en bitmap, A = máscara bit + and $07 ; A = X AND 7 + + ret ENDP 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/sysvars.asm b/src/lib/arch/zx81sd/runtime/sysvars.asm index df1e90b3b..404b35eb7 100644 --- a/src/lib/arch/zx81sd/runtime/sysvars.asm +++ b/src/lib/arch/zx81sd/runtime/sysvars.asm @@ -36,17 +36,19 @@ DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitma 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.) -ATTR_T EQU SYSVAR_BASE + $0F ; DW — atributo temporal + máscara -P_FLAG EQU SYSVAR_BASE + $11 ; DB — flags de impresión (OVER/INVERSE perm.) -MEM0 EQU SYSVAR_BASE + $12 ; 5B — buffer temporal para rutinas gráficas -TV_FLAG EQU SYSVAR_BASE + $17 ; DB — flags de control de salida a pantalla -ERR_NR EQU SYSVAR_BASE + $18 ; DB — código de error (-1 = sin error) -FRAMES EQU SYSVAR_BASE + $19 ; DW — contador de frames VSYNC (software) -RANDOM_SEED_LOW EQU SYSVAR_BASE + $1B ; DW — semilla RNG (16 bits bajos) -SCREEN_ADDR EQU SYSVAR_BASE + $1D ; DW — puntero al framebuffer (init: $C000) -SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $1F ; DW — puntero a atributos (init: $D800) - -; Tamaño total del bloque de sysvars: $21 bytes +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) + +; Tamaño total del bloque de sysvars: $22 bytes ; --- Constantes de pantalla --------------------------------------------- diff --git a/src/lib/arch/zx81sd/runtime/vsync.asm b/src/lib/arch/zx81sd/runtime/vsync.asm index 424282b4a..7cb85a3e9 100644 --- a/src/lib/arch/zx81sd/runtime/vsync.asm +++ b/src/lib/arch/zx81sd/runtime/vsync.asm @@ -1,8 +1,9 @@ ; ----------------------------------------------------------------------- ; VSYNC — Sincronización con el refresco de pantalla -; SD81 Booster: puerto $A7h, bit 0 = estado VSYNC -; bit 0 = 0 → pantalla pintándose (blanking activo) -; bit 0 = 1 → blanking vertical completado, inicio de nuevo frame +; 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. @@ -12,27 +13,19 @@ push namespace core -SD81_DATA_PORT EQU $A7 ; Puerto de datos MCU del SD81 Booster +SD81_DATA_PORT EQU $AF ; Puerto de datos MCU del SD81 Booster -; VSYNC_WAIT — Espera al inicio del siguiente frame +; 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_LOW - LOCAL WAIT_HIGH + LOCAL WAIT_PULSE - ; Esperar a que VSYNC baje (por si estamos en medio de un pulso) -WAIT_LOW: +WAIT_PULSE: in a, (SD81_DATA_PORT) - rrca ; bit 0 → carry - jr c, WAIT_LOW ; si carry=1, todavía en blanking anterior - - ; Esperar el flanco de subida (inicio real del blanking) -WAIT_HIGH: - in a, (SD81_DATA_PORT) - rrca - jr nc, WAIT_HIGH ; si carry=0, aún no ha llegado el VSYNC + and $7E ; bits 6-1 = contador de pulsos + jr z, WAIT_PULSE ; si 0, ningún pulso desde la última lectura ret ENDP diff --git a/src/lib/arch/zx81sd/stdlib/input.bas b/src/lib/arch/zx81sd/stdlib/input.bas new file mode 100644 index 000000000..ec8bd5c71 --- /dev/null +++ b/src/lib/arch/zx81sd/stdlib/input.bas @@ -0,0 +1,137 @@ +' ---------------------------------------------------------------- +' 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 input(MaxLen AS UINTEGER) AS STRING + DIM result$ AS STRING + DIM i as UINTEGER + DIM LastK as UByte + + result$ = "" + + DO + PRIVATEInputShowCursor() + + REM Espera a que se pulse una tecla y a que se suelte (debounce) + LastK = PRIVATEInputWaitKey() + + 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 + + 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 From 1872536b530fd5285f6e5d0a7ab0112c9a39b9e6 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Wed, 1 Jul 2026 18:20:13 +0200 Subject: [PATCH 135/169] Ignore zx81sd test build artifacts (compila.bat, TESTSD81*.P/.BIN) Co-Authored-By: Claude --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 8385908be..1ca58c0ee 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,8 @@ build/ venv/ .pypi-token .coverage.* + +# zx81sd test build artifacts (compila.bat + split_sd81.py output) +/compila.bat +/TESTSD81*.P +/TESTSD81*.BIN From 9c2d154bc9faa2f68e1a057055b6a0c7a0d80563 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:41:28 +0200 Subject: [PATCH 136/169] zx81sd: port Spectrum ROM FP calculator (RST 28h), FLOAT support and DRAW arc mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port the Spectrum ROM floating-point calculator to the zx81sd architecture as native runtime code (no Spectrum ROM available): - fp_calc.asm: CALCULATE engine (RST 28h + literal bytecode), FP stack sysvars, arithmetic, comparison, trig/log/exp/sqrt (series-based, as in the ROM), and STK-TO-A/STK-TO-BC/CD-PRMS1 for arc drawing. - stackf.asm: native __FPSTACK_PUSH/__FPSTACK_POP over FP_STKEND. - fp_tostr.asm / printf.asm / str.asm: PRINT and STR$ of FLOAT values (sign + integer part + up to 5 decimals, trailing zeros trimmed). - val.asm: VAL for FLOAT expressions. - draw3.asm: DRAW x,y,angle (arc mode) override, replacing fixed ROM addresses with the routines ported in fp_calc.asm. Includes optional trace instrumentation behind -D DRAW3_DEBUG. - pixel_addr.asm: fix register contract — PIXEL_ADDR now preserves DE like ROM PIXEL-ADD ($22AC). It used D as scratch, which corrupted the Bresenham Y coordinate saved in D' by draw.asm, breaking every non-horizontal DRAW line (found while debugging arc mode). - sysvars.asm / bootstrap.asm: FP calculator sysvars (STKBOT/STKEND/ BREG/MEM + stack and MEM area) and their initialization. - backend main.py: include fp_calc.asm unconditionally in the prologue (RST 28h vector must always exist). Verified on hardware/EightyOne against a real Spectrum reference: PLOT 100,100: DRAW 20,0,3.14159: DRAW 10,10,1.5708: CIRCLE 60,30,20 renders identically on both. Co-Authored-By: Claude Fable 5 --- src/arch/zx81sd/backend/main.py | 3 +- src/lib/arch/zx81sd/runtime/bootstrap.asm | 15 + src/lib/arch/zx81sd/runtime/draw3.asm | 465 +++++ src/lib/arch/zx81sd/runtime/fp_calc.asm | 2016 +++++++++++++++++++- src/lib/arch/zx81sd/runtime/fp_tostr.asm | 298 +++ src/lib/arch/zx81sd/runtime/pixel_addr.asm | 18 +- src/lib/arch/zx81sd/runtime/printf.asm | 37 + src/lib/arch/zx81sd/runtime/stackf.asm | 97 + src/lib/arch/zx81sd/runtime/str.asm | 54 + src/lib/arch/zx81sd/runtime/sysvars.asm | 20 +- src/lib/arch/zx81sd/runtime/val.asm | 210 ++ 11 files changed, 3192 insertions(+), 41 deletions(-) create mode 100644 src/lib/arch/zx81sd/runtime/draw3.asm create mode 100644 src/lib/arch/zx81sd/runtime/fp_tostr.asm create mode 100644 src/lib/arch/zx81sd/runtime/printf.asm create mode 100644 src/lib/arch/zx81sd/runtime/stackf.asm create mode 100644 src/lib/arch/zx81sd/runtime/str.asm create mode 100644 src/lib/arch/zx81sd/runtime/val.asm diff --git a/src/arch/zx81sd/backend/main.py b/src/arch/zx81sd/backend/main.py index 940279407..df868e78d 100644 --- a/src/arch/zx81sd/backend/main.py +++ b/src/arch/zx81sd/backend/main.py @@ -155,8 +155,7 @@ def emit_prologue() -> list[str]: output.append("di") output.append("halt") output.append("org $0028") - output.append("di") # $0028: RST $28 FP calc — nunca con __ZXB_NO_FLOAT - output.append("halt") + output.append("jp .core.FP_CALC_ENTRY") # $0028: RST $28 -> calculador FP propio output.append("org $0030") output.append("di") output.append("halt") diff --git a/src/lib/arch/zx81sd/runtime/bootstrap.asm b/src/lib/arch/zx81sd/runtime/bootstrap.asm index a4d5452b9..e6b6b3cd7 100644 --- a/src/lib/arch/zx81sd/runtime/bootstrap.asm +++ b/src/lib/arch/zx81sd/runtime/bootstrap.asm @@ -12,6 +12,13 @@ #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 @@ -75,6 +82,14 @@ SD81_INIT_SYSVARS: 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 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/fp_calc.asm b/src/lib/arch/zx81sd/runtime/fp_calc.asm index 4bd801895..adc53f1c7 100644 --- a/src/lib/arch/zx81sd/runtime/fp_calc.asm +++ b/src/lib/arch/zx81sd/runtime/fp_calc.asm @@ -1,34 +1,1996 @@ -; FP_CALC — Calculador de coma flotante -; Sustituye a RST $28h (ROM Spectrum, dirección $0E9Bh) -; -; ESTADO: STUB — Primera versión sin soporte float. -; La arquitectura zx81sd compila con __ZXB_NO_FLOAT activado (arch_config.asm), -; por lo que este fichero no se incluye en compilaciones normales. -; -; TODO: Integrar el calculador FP del Spectrum reubicado en $0000-$0FFF -; o una reimplementación libre compatible con el protocolo de bytecodes. -; Cuando esté listo: -; 1. Eliminar #define __ZXB_NO_FLOAT de arch_config.asm -; 2. Sustituir FP_CALC_ENTRY por la implementación real -; 3. Verificar que todos los RST $28h del runtime se convierten -; en CALL FP_CALC_ENTRY (gestionado por el backend) -; -; Protocolo RST $28h del Spectrum: -; - Los bytecodes de operación siguen inmediatamente al CALL en memoria -; - El calculador lee el flujo de bytecodes desde (SP) tras el CALL -; - El stack de coma flotante (5 bytes/número) es independiente del stack Z80 -; - El registro E apunta al primer bytecode -; -; Referencias: -; ROM Spectrum disassembly: https://skoolkid.github.io/rom/ -; FP calculator entry: $0E9Bh — routine CALCULATE +; =========================================================================== +; 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 del calculador FP (para cuando se integre la versión real) +; --------------------------------------------------------------------------- +; Punto de entrada — sustituye a RST $28h +; --------------------------------------------------------------------------- FP_CALC_ENTRY: - ; STUB: no hacer nada — el compilador no debería llegar aquí - ; con __ZXB_NO_FLOAT activo + 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/pixel_addr.asm b/src/lib/arch/zx81sd/runtime/pixel_addr.asm index 524a0c129..93a0156f8 100644 --- a/src/lib/arch/zx81sd/runtime/pixel_addr.asm +++ b/src/lib/arch/zx81sd/runtime/pixel_addr.asm @@ -5,7 +5,9 @@ ; 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, D +; 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) @@ -22,7 +24,7 @@ PIXEL_ADDR: PROC sub b ; A = 191 - Y (convierte coord Spectrum a offset desde arriba) - ld d, a ; D = V = 191-Y + 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 @@ -30,22 +32,16 @@ PIXEL_ADDR: rrca rrca ; A = tercio * 8 ld h, a - ld a, d + 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, d + ld a, b and $38 ; A = fila_de_char * 8 - rrca - rrca - rrca ; A = fila_de_char (0-7) - rlca - rlca - rlca rlca - rlca ; A = fila_de_char * 32 + rlca ; A = fila_de_char * 32 (tras AND $38 no hay acarreo al rotar) ld b, a ld a, c rrca 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/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 index 404b35eb7..ec0a4c01a 100644 --- a/src/lib/arch/zx81sd/runtime/sysvars.asm +++ b/src/lib/arch/zx81sd/runtime/sysvars.asm @@ -48,7 +48,25 @@ 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) -; Tamaño total del bloque de sysvars: $22 bytes +; --- 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) + +; Tamaño total del bloque de sysvars: $83 bytes ; --- Constantes de pantalla --------------------------------------------- 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 From 7ae4d10a2b8747c930fd70312b599a1d8991fcb1 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Thu, 2 Jul 2026 16:47:41 +0200 Subject: [PATCH 137/169] zx81sd: BEEP nativo y PLAY sobre los AY ZonX del SD81 Booster BEEP (sin ROM Spectrum): - io/sound/beeper.asm: bucle BEEPER de la ROM ($03B5) portado al puerto ULA $FB del SD81 (bits como el $FE del Spectrum), con el borde tomado de la copia sombra y sin EI final (el runtime corre con las interrupciones deshabilitadas). La entrada __BEEPER del compilador corrige el periodo precalculado por el frontend (que usa la constante del Spectrum, 437500 = 3.5MHz/8) a los 3.25 MHz del ZX81: HL' = HL*13/14 - 2 (406250/437500 = 13/14 exacto). - io/sound/beep.asm: comando BEEP de la ROM ($03F8) portado sobre el calculador FP propio: misma matematica (incluida la parte fraccionaria del semitono), tabla de semitonos de la ROM, y constante 406250 (= 3.25MHz/8). FIND-INT1/2 sustituidos por __FPSTACK_POP+__FTOU32REG. PLAY (chip A del ZonX, 3 canales): - stdlib/play.bas: copia de zx48k/stdlib/play.bas con 4 cambios documentados en cabecera: puertos ZonX (latch $CF, dato $0F), tabla de divisores regenerada para reloj AY de 1.625 MHz (26MHz/16 en la FPGA), CpuCyclesPerSecond = 3250000 para el tempo, y sin EI final. Verificado en EightyOne con analisis FFT de la salida de audio: beeper y AY al unisono en 440 Hz (LA4), escala y acorde de 3 canales correctos, duraciones exactas. Co-Authored-By: Claude Fable 5 --- src/lib/arch/zx81sd/runtime/io/sound/beep.asm | 208 +++++++ .../arch/zx81sd/runtime/io/sound/beeper.asm | 142 +++++ src/lib/arch/zx81sd/stdlib/play.bas | 510 ++++++++++++++++++ 3 files changed, 860 insertions(+) create mode 100644 src/lib/arch/zx81sd/runtime/io/sound/beep.asm create mode 100644 src/lib/arch/zx81sd/runtime/io/sound/beeper.asm create mode 100644 src/lib/arch/zx81sd/stdlib/play.bas 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/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) From bc6f618d3fa32c24ae69ca73ae235335b5728640 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Thu, 2 Jul 2026 21:16:26 +0200 Subject: [PATCH 138/169] zx81sd: libreria de comandos del MCU del SD81 Booster (mcu.bas, joy.bas) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mcu.bas — primitivas del protocolo del MCU (puertos $A7 datos / $AF reloj) con las transferencias en ensamblador (McuSend/McuRecv/ McuSendBlock/McuRecvBlock: camino critico de LOAD/SAVE/F_READ/F_WRITE) y wrappers para todos los comandos del manual: - Sistema: NOP, VERSION, GETBYTE, SETBYTE - Ficheros: PWD, CD, DEL, MKDIR, RMDIR, MOVE, COPY, LOAD, SAVE, TYPE, DIR, FREE (texto y binario), OPENDIR/GETROWLEN/GETROW - Handles (32 bits): F_OPEN (ASCII), F_OPEN_ZX81, F_SEEK, F_READ, F_WRITE, F_CLOSE — verificado contra el firmware (COMMANDS.cpp): el MCU asigna el handle y lo devuelve - Hardware: MC45, 128C/64C, FULLPAGING/HALFPAGING, RAM48, y el mapeador de memoria Map/MapGet (puerto $E7, valido en ambos modos de paginacion con una sola escritura) - Voz: SAY, BINARY_SAY - AY del MCU (chip 2): AY_SET_REG/AY_GET_REG/AY_PLAY (con conversion de octavas mayuscula<->video inverso como el interprete del MCU) - VGM y PEG completos; RTC y BAT - Extensiones no-MCU del BASIC del SD81: HexPoke (*HEX), StrInv/StrBold (*INV/*BOLD); *LDIR/*LDDR cubiertos por MemMove de la stdlib estandar Conversion automatica ASCII<->codigos ZX81 en los comandos de texto. joy.bas (comando 21) reescrito como capa fina sobre mcu.bas. Verificado en EightyOne(SD81): version, get/setbyte, pwd, save+load con verificacion byte a byte, del, free, rtc, bat, dir, AY2 por registros, AyPlay, joystick, mapeador y HexPoke/MemMove/StrInv. Co-Authored-By: Claude Fable 5 --- src/lib/arch/zx81sd/stdlib/joy.bas | 77 +++ src/lib/arch/zx81sd/stdlib/mcu.bas | 897 +++++++++++++++++++++++++++++ 2 files changed, 974 insertions(+) create mode 100644 src/lib/arch/zx81sd/stdlib/joy.bas create mode 100644 src/lib/arch/zx81sd/stdlib/mcu.bas 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..2c5d41408 --- /dev/null +++ b/src/lib/arch/zx81sd/stdlib/mcu.bas @@ -0,0 +1,897 @@ +' ---------------------------------------------------------------------- +' 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() 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 line + line = "" + else + line = line + chr(_McuFromZx(c)) + end if + loop + if len(line) > 0 then + print line + 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() +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() +end function + +' Cmd 14: imprime el espacio total/libre de la SD como texto. +function McuFreeTxtPrint() as ubyte + McuSend(14) + return _McuStreamPrint() +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) From 75c8f7f7c3f9a118b0ac1b898ad7b60f01ad50b7 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Thu, 2 Jul 2026 23:38:36 +0200 Subject: [PATCH 139/169] zx81sd: statements LOAD/SAVE/VERIFY ... CODE contra la SD del SD81 Sustituyen a los load.asm/save.asm de zx48k (rutinas de cinta de la ROM Spectrum, inutilizables sin ROM): ahora LOAD/SAVE/VERIFY ... CODE operan sobre la tarjeta SD mediante los comandos 9/10 del MCU. - io/sd81_mcu.asm: primitivas del protocolo del MCU para el runtime (send/recv con espera de reloj, bloques, RECV_VERIFY que compara sin escribir, RECV_SINK para descartar sobrante manteniendo el protocolo sincronizado, conversion ASCII->ZX81 de nombres). - load.asm: LOAD carga el fichero completo (longitud 0) o hasta N bytes (descarta el exceso; fichero mas corto = error). VERIFY compara sin escribir. Errores blandos como en cinta: ERR_NR = 26 (Tape loading) o 14 (nombre invalido) y retorna. - save.asm: SAVE guarda el bloque; longitud 0 = InvalidArg. Interfaz del compilador identica a zx48k (incluida la liberacion del string del nombre con MEM_FREE). Verificado en EightyOne(SD81): ciclo SAVE+LOAD con verificacion byte a byte, VERIFY correcto y con byte corrupto (ERR 26), y carga de fichero inexistente (ERR 26). Co-Authored-By: Claude Fable 5 --- src/lib/arch/zx81sd/runtime/io/sd81_mcu.asm | 274 ++++++++++++++++++++ src/lib/arch/zx81sd/runtime/load.asm | 160 ++++++++++++ src/lib/arch/zx81sd/runtime/save.asm | 100 +++++++ 3 files changed, 534 insertions(+) create mode 100644 src/lib/arch/zx81sd/runtime/io/sd81_mcu.asm create mode 100644 src/lib/arch/zx81sd/runtime/load.asm create mode 100644 src/lib/arch/zx81sd/runtime/save.asm 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/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/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 From 9903c8662bc336a9b1498e89b81c9e9f605cf3b4 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Fri, 3 Jul 2026 13:14:39 +0200 Subject: [PATCH 140/169] zx81sd: area de UDGs dedicada (los POKE USR CHR$ corrompian el runtime) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La sysvar UDG apuntaba a __ZX81SD_CHARSET+896 asumiendo una fuente completa de 256 caracteres, pero specfont.bin solo cubre CHR$(32)- CHR$(127) (768 bytes): el puntero caia 128 bytes mas alla del final de la fuente, sobre el codigo del runtime que tocara despues en el enlazado. Cualquier programa que definiera UDGs (POKE USR CHR$(144+n)) machacaba ese codigo — p.ej. comecoquitos.bas corrompia CALC_TEST_ROOM de fp_calc y colgaba el sistema al primer uso del calculador. Fix: charset.asm reserva un area de 21 UDGs (CHR$(144)-CHR$(164), 168 bytes) tras la fuente, y SD81_INIT_SYSVARS apunta UDG a ella y la inicializa con copias de las letras A-U, igual que la ROM del Spectrum. Co-Authored-By: Claude Fable 5 --- src/lib/arch/zx81sd/runtime/bootstrap.asm | 17 +++++++++++++---- src/lib/arch/zx81sd/runtime/charset.asm | 9 +++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/lib/arch/zx81sd/runtime/bootstrap.asm b/src/lib/arch/zx81sd/runtime/bootstrap.asm index e6b6b3cd7..a9ddd84f9 100644 --- a/src/lib/arch/zx81sd/runtime/bootstrap.asm +++ b/src/lib/arch/zx81sd/runtime/bootstrap.asm @@ -33,12 +33,21 @@ SD81_INIT_SYSVARS: ld hl, __ZX81SD_CHARSET - 256 ld (CHARS), hl - ; UDG: primer carácter definible por el usuario (CHR$(144) en Spectrum) - ; = font base + (144-32)*8 = font + 896. Con la convención CHARS-256: - ; UDG = CHARS + 144*8 = __ZX81SD_CHARSET - 256 + 1152 = __ZX81SD_CHARSET + 896 - ld hl, __ZX81SD_CHARSET + 896 + ; 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 diff --git a/src/lib/arch/zx81sd/runtime/charset.asm b/src/lib/arch/zx81sd/runtime/charset.asm index 20a0cad25..9cc665859 100644 --- a/src/lib/arch/zx81sd/runtime/charset.asm +++ b/src/lib/arch/zx81sd/runtime/charset.asm @@ -8,4 +8,13 @@ __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 From 9a24059e77bc10de2e81efaba74ab7d890f612f7 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Fri, 3 Jul 2026 13:45:44 +0200 Subject: [PATCH 141/169] zx81sd: INKEY$ en minusculas (semantica Spectrum) y fix de PO_GR_1 - keyscan.asm: las letras se decodifican ahora en minuscula, igual que el INKEY$ del Spectrum en modo L (el por defecto). Los programas de la epoca comparan INKEY$ con "o","p","q","a","y","n"... y con mayusculas no respondian (p.ej. examples/comecoquitos.bas). - po_gr_1.asm: reescrito con el algoritmo literal de PO-GR-2 ($0B3E, ROM Spectrum). La version anterior tenia un OR aplicado al registro equivocado (machacaba los bits de cuadrante y dejaba vacia la mitad inferior de CHR$(143), entre otros) y el mapeo izquierda/derecha de los cuadrantes invertido respecto a la ROM (bit0 = superior DERECHO). Verificado con comecoquitos.bas completo (jugable, identico al .tap de Spectrum) y un test de los 16 graficos CHR$(128)-CHR$(143). Co-Authored-By: Claude Fable 5 --- .../zx81sd/runtime/io/keyboard/keyscan.asm | 354 +++++++++--------- src/lib/arch/zx81sd/runtime/po_gr_1.asm | 99 ++--- 2 files changed, 213 insertions(+), 240 deletions(-) diff --git a/src/lib/arch/zx81sd/runtime/io/keyboard/keyscan.asm b/src/lib/arch/zx81sd/runtime/io/keyboard/keyscan.asm index b51b36803..2c5809320 100644 --- a/src/lib/arch/zx81sd/runtime/io/keyboard/keyscan.asm +++ b/src/lib/arch/zx81sd/runtime/io/keyboard/keyscan.asm @@ -1,175 +1,179 @@ -; --------------------------------------------------------------------------- -; 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. -; -; 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 la tabla de decodificacion de abajo -; traduce 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 - -; --------------------------------------------------------------------------- -; __ZX81SD_KEYSCAN -; -; Escanea las 8 filas de la matriz y decodifica la primera tecla -; encontrada (si hay varias pulsadas a la vez, solo se detecta una). -; -; Devuelve: -; A = codigo ASCII de la tecla pulsada, o 0 si no hay ninguna pulsada, -; o si la tecla no tiene equivalente ASCII (ver nota arriba), o si -; solo esta pulsada la tecla SHIFT sola. -; Flag Z activo si A = 0 -; -; Registros modificados: AF, BC, DE, HL -; --------------------------------------------------------------------------- -__ZX81SD_KEYSCAN: - PROC - 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 LOOKUP - LOCAL NO_KEY - LOCAL UNSHIFT_TABLE - LOCAL SHIFT_TABLE - - ; D = indice de fila actual (0-7), E = flag SHIFT pulsado (0/1) - ld d, 0 - ld e, 0 - ld b, $FE ; B = mitad alta del puerto ($FEFE, $FDFE, ... $7FFE) - -ROW_LOOP: - ld c, $FE - in a, (c) ; lee la fila; bits 0-4 = columnas (0 = pulsada) - and $1F - cp $1F - jr z, NEXT_ROW ; ninguna tecla pulsada en esta fila - - ld l, a ; L = mapa de bits de columnas pulsadas - - ld a, d - or a - jr nz, FIND_COL - - ; Fila 0: la columna 0 es la tecla SHIFT (no genera caracter propio) - bit 0, l - jr nz, FIND_COL ; SHIFT no pulsado, comprobar columnas normalmente - ld e, 1 ; SHIFT pulsado - set 0, l ; descartar ese bit para no confundirlo con datos - ld a, l - cp $1F - jr z, NEXT_ROW ; en esta fila solo estaba pulsado SHIFT - -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) - - ld hl, UNSHIFT_TABLE - bit 0, e - jr z, LOOKUP - ld hl, SHIFT_TABLE - -LOOKUP: - ld c, a - ld b, 0 - add hl, bc - ld a, (hl) - or a ; fija flag Z segun corresponda - ret - -NEXT_ROW: - rlc b - inc d - ld a, d - cp 8 - jr nz, ROW_LOOP - -NO_KEY: - xor a - ret - - ; 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. -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' - -SHIFT_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, AND, 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, ',', '>', '<', '*' ; £, ',', >, <, * - - ENDP - - pop namespace +; --------------------------------------------------------------------------- +; 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. +; +; 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 la tabla de decodificacion de abajo +; traduce 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 + +; --------------------------------------------------------------------------- +; __ZX81SD_KEYSCAN +; +; Escanea las 8 filas de la matriz y decodifica la primera tecla +; encontrada (si hay varias pulsadas a la vez, solo se detecta una). +; +; Devuelve: +; A = codigo ASCII de la tecla pulsada, o 0 si no hay ninguna pulsada, +; o si la tecla no tiene equivalente ASCII (ver nota arriba), o si +; solo esta pulsada la tecla SHIFT sola. +; Flag Z activo si A = 0 +; +; Registros modificados: AF, BC, DE, HL +; --------------------------------------------------------------------------- +__ZX81SD_KEYSCAN: + PROC + 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 LOOKUP + LOCAL NO_KEY + LOCAL UNSHIFT_TABLE + LOCAL SHIFT_TABLE + + ; D = indice de fila actual (0-7), E = flag SHIFT pulsado (0/1) + ld d, 0 + ld e, 0 + ld b, $FE ; B = mitad alta del puerto ($FEFE, $FDFE, ... $7FFE) + +ROW_LOOP: + ld c, $FE + in a, (c) ; lee la fila; bits 0-4 = columnas (0 = pulsada) + and $1F + cp $1F + jr z, NEXT_ROW ; ninguna tecla pulsada en esta fila + + ld l, a ; L = mapa de bits de columnas pulsadas + + ld a, d + or a + jr nz, FIND_COL + + ; Fila 0: la columna 0 es la tecla SHIFT (no genera caracter propio) + bit 0, l + jr nz, FIND_COL ; SHIFT no pulsado, comprobar columnas normalmente + ld e, 1 ; SHIFT pulsado + set 0, l ; descartar ese bit para no confundirlo con datos + ld a, l + cp $1F + jr z, NEXT_ROW ; en esta fila solo estaba pulsado SHIFT + +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) + + ld hl, UNSHIFT_TABLE + bit 0, e + jr z, LOOKUP + ld hl, SHIFT_TABLE + +LOOKUP: + ld c, a + ld b, 0 + add hl, bc + ld a, (hl) + or a ; fija flag Z segun corresponda + ret + +NEXT_ROW: + rlc b + inc d + ld a, d + cp 8 + jr nz, ROW_LOOP + +NO_KEY: + xor a + ret + + ; 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. + ; Las letras se decodifican en MINUSCULA, igual que el INKEY$ del + ; Spectrum en modo L (el por defecto): los programas de la epoca + ; comparan INKEY$ con "o","p","q","a","y","n"... y con mayusculas + ; no responderian (p.ej. examples/comecoquitos.bas). +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' + +SHIFT_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, AND, 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, ',', '>', '<', '*' ; £, ',', >, <, * + + 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 index 6f78aaf99..c1435cdd5 100644 --- a/src/lib/arch/zx81sd/runtime/po_gr_1.asm +++ b/src/lib/arch/zx81sd/runtime/po_gr_1.asm @@ -1,24 +1,20 @@ ; PO_GR_1 — Genera el patrón de bits para caracteres gráficos 128-143 -; Sustituye a PO_GR_1 EQU $0B38h (ROM Spectrum) +; 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 pixels 2×2 en cuadrantes. El nibble bajo del código determina -; qué cuadrantes están encendidos (bit 0 = TL, 1 = TR, 2 = BL, 3 = BR). +; 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): ; -; Entrada: B = código de carácter (128-143), solo se usan bits 3-0 -; Salida: MEM0 (5 bytes) = patrón de 8 bytes del carácter generado -; HL = MEM0 (para que plot lo use directamente) -; Destruye: A, B, C, D, E +; 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 ; -; Patrón generado: 4 líneas top + 4 líneas bottom -; top_byte = $FF si bit 0 (TL) || $F0 si bit 1 (TR) || combinación -; bot_byte = igual pero mirando bits 2 (BL) y 3 (BR) +; (CHR$(129) = ▝, CHR$(130) = ▘, CHR$(143) = bloque macizo.) ; -; Codificación exacta: -; bit 0 = cuadrante superior izquierdo → pixels $F0 en filas 0-3 -; bit 1 = cuadrante superior derecho → pixels $0F en filas 0-3 -; bit 2 = cuadrante inferior izquierdo → pixels $F0 en filas 4-7 -; bit 3 = cuadrante inferior derecho → pixels $0F en filas 4-7 +; 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 @@ -26,61 +22,34 @@ PO_GR_1: PROC + LOCAL PO_GR_HALF, PO_GR_FILL - ld a, b ; A = código gráfico (128-143) - and $0F ; quedarnos solo con los 4 bits de cuadrante - - ; Calcular byte superior (filas 0-3) - ld c, $00 - bit 0, a ; TL activo? - jr z, NO_TL - ld c, $F0 -NO_TL: - bit 1, a ; TR activo? - jr z, NO_TR - ld b, c - or $0F - ld c, a ld a, b -NO_TR: - ; C = byte superior - - ; Calcular byte inferior (filas 4-7) - ld d, a ; guardar A (bits cuadrante) - ld e, $00 - bit 2, d ; BL activo? - jr z, NO_BL - ld e, $F0 -NO_BL: - bit 3, d ; BR activo? - jr z, NO_BR - ld a, e - or $0F - ld e, a -NO_BR: - ; E = byte inferior - - ; Rellenar MEM0 con 8 bytes: 4x C, 4x E + and $0F + ld b, a ; B = bits de cuadrante ld hl, MEM0 - ld a, c - ld (hl), a - inc hl - ld (hl), a - inc hl - ld (hl), a - inc hl - ld (hl), a - inc hl - ld a, e - ld (hl), a - inc hl - ld (hl), a - inc hl + 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 - ld (hl), a - - ld hl, MEM0 ; devolver puntero al patrón + dec c + jr nz, PO_GR_FILL ret ENDP From 5aa86b2ff26c9f1496fa7a17ecc12023db972b06 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 09:05:19 +0200 Subject: [PATCH 142/169] zx81sd: teclado con mayusculas/CAPS LOCK, fix scroll/heap, MSFS sobre bloque 7 (WIP) - backend/main.py: heap_size/heap_address se asignaban con ADD_IF_NOT_DEFINED, pero Z80Backend.init() ya los definia antes -> el heap se quedaba inline en vez de en $8100. Fix: asignacion directa tras super().init(). - keyscan.asm: rediseno completo. Pulsacion directa = minuscula, SHIFT+letra = mayuscula (antes solo devolvia minuscula), SHIFT+"2" = CAPS LOCK persistente, "." como tecla normal con los simbolos clasicos del ZX81 accesibles como tecla muerta desde INPUT() (ver input.bas). - input.bas: composicion de simbolos ZX81 (tecla muerta "."+tecla) y doble punto = punto literal sin combo. - print.asm: __SCROLL_SCR ya no cae en la ROM del Spectrum ($0DFE) - la implementacion por buffer es ahora la unica rama. - stdlib/scroll.bas: nuevo override zx81sd (llamaba a PIXEL-ADD $22AC de la ROM, sustituido por PIXEL_ADDR propio). - stdlib/cb/maskedsprites.bas: nuevo override que porta MSFS al mapeador de memoria (bloque 7) en vez de BANKM/$7FFD del Spectrum. Sigue en proceso, aun no funciona del todo bien. - mcu.bas: _McuStreamPrint admite controlar el salto de linea final (para listados tipo DIR sin salto extra). - fzx_fonts/: fuentes FZX de ejemplo para la libreria de fuentes. - examples/sd81/filesystem.bas: demo de McuCd/McuDirPrint/McuLoad. Co-Authored-By: Claude Sonnet 5 --- examples/sd81/filesystem.bas | 11 + src/arch/zx81sd/backend/main.py | 15 +- .../zx81sd/runtime/io/keyboard/keyscan.asm | 576 ++++--- src/lib/arch/zx81sd/runtime/print.asm | 10 +- .../arch/zx81sd/stdlib/cb/maskedsprites.bas | 1369 +++++++++++++++++ .../arch/zx81sd/stdlib/fzx_fonts/academy.fzx | Bin 0 -> 1254 bytes .../zx81sd/stdlib/fzx_fonts/belegost1.fzx | Bin 0 -> 1310 bytes .../zx81sd/stdlib/fzx_fonts/belegost2.fzx | Bin 0 -> 1358 bytes .../arch/zx81sd/stdlib/fzx_fonts/bigbold.fzx | Bin 0 -> 1450 bytes .../arch/zx81sd/stdlib/fzx_fonts/cobra.fzx | Bin 0 -> 1332 bytes .../arch/zx81sd/stdlib/fzx_fonts/crash.fzx | Bin 0 -> 1208 bytes .../arch/zx81sd/stdlib/fzx_fonts/d_o_c.fzx | Bin 0 -> 1169 bytes .../arch/zx81sd/stdlib/fzx_fonts/eclipse.fzx | Bin 0 -> 1163 bytes .../arch/zx81sd/stdlib/fzx_fonts/extra.fzx | Bin 0 -> 1507 bytes .../arch/zx81sd/stdlib/fzx_fonts/hijack.fzx | Bin 0 -> 1270 bytes .../arch/zx81sd/stdlib/fzx_fonts/italika.fzx | Bin 0 -> 1234 bytes .../arch/zx81sd/stdlib/fzx_fonts/just6.fzx | Bin 0 -> 1241 bytes .../zx81sd/stdlib/fzx_fonts/locomotion.fzx | Bin 0 -> 1270 bytes .../arch/zx81sd/stdlib/fzx_fonts/midnight.fzx | Bin 0 -> 1236 bytes .../zx81sd/stdlib/fzx_fonts/moonalert.fzx | Bin 0 -> 1167 bytes .../arch/zx81sd/stdlib/fzx_fonts/nether.fzx | Bin 0 -> 1222 bytes .../arch/zx81sd/stdlib/fzx_fonts/neverend.fzx | Bin 0 -> 1266 bytes .../arch/zx81sd/stdlib/fzx_fonts/roman.fzx | Bin 0 -> 1197 bytes .../arch/zx81sd/stdlib/fzx_fonts/script.fzx | Bin 0 -> 1230 bytes .../arch/zx81sd/stdlib/fzx_fonts/script2.fzx | Bin 0 -> 1295 bytes .../arch/zx81sd/stdlib/fzx_fonts/standard.fzx | Bin 0 -> 1255 bytes .../arch/zx81sd/stdlib/fzx_fonts/tomahawk.fzx | Bin 0 -> 1235 bytes .../zx81sd/stdlib/fzx_fonts/ultrabold.fzx | Bin 0 -> 1462 bytes .../zx81sd/stdlib/fzx_fonts/upcasebold.fzx | Bin 0 -> 1367 bytes .../arch/zx81sd/stdlib/fzx_fonts/wildvest.fzx | Bin 0 -> 1212 bytes .../arch/zx81sd/stdlib/fzx_fonts/winter.fzx | Bin 0 -> 1240 bytes src/lib/arch/zx81sd/stdlib/input.bas | 60 + src/lib/arch/zx81sd/stdlib/mcu.bas | 19 +- src/lib/arch/zx81sd/stdlib/scroll.bas | 1043 +++++++++++++ 34 files changed, 2910 insertions(+), 193 deletions(-) create mode 100644 examples/sd81/filesystem.bas create mode 100644 src/lib/arch/zx81sd/stdlib/cb/maskedsprites.bas create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/academy.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/belegost1.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/belegost2.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/bigbold.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/cobra.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/crash.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/d_o_c.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/eclipse.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/extra.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/hijack.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/italika.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/just6.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/locomotion.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/midnight.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/moonalert.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/nether.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/neverend.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/roman.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/script.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/script2.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/standard.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/tomahawk.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/ultrabold.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/upcasebold.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/wildvest.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/fzx_fonts/winter.fzx create mode 100644 src/lib/arch/zx81sd/stdlib/scroll.bas diff --git a/examples/sd81/filesystem.bas b/examples/sd81/filesystem.bas new file mode 100644 index 000000000..5adce9089 --- /dev/null +++ b/examples/sd81/filesystem.bas @@ -0,0 +1,11 @@ +#include +#include +dim version as ubyte +version = McuVersion() +print "SD81 OS version: ";version +print "Current dir: "; McuPwd() +if McuCd("/")=0 then print "Change to root dir" else print "error" +if McuCd("/test")=0 then print "Change to test dir" else print "error" +McuDirPrint("/test/*.*") +McuLoad("/DEMO.WAV",0) +input a$ \ No newline at end of file diff --git a/src/arch/zx81sd/backend/main.py b/src/arch/zx81sd/backend/main.py index df868e78d..c4e1378af 100644 --- a/src/arch/zx81sd/backend/main.py +++ b/src/arch/zx81sd/backend/main.py @@ -68,10 +68,17 @@ def init(self): super().init() OPTIONS(Action.ADD_IF_NOT_DEFINED, name="org", type=int, default=_ORG) - OPTIONS(Action.ADD_IF_NOT_DEFINED, name="heap_size", type=int, - default=_HEAP_SIZE, ignore_none=True) - OPTIONS(Action.ADD_IF_NOT_DEFINED, name="heap_address", type=int, - default=_HEAP_ADDR, ignore_none=False) + + # El backend Z80 generico (super().init(), justo arriba) ya registra + # "heap_size" (4768) y "heap_address" (None) con ADD_IF_NOT_DEFINED, + # asi que un ADD_IF_NOT_DEFINED aqui seria un no-op y el heap acababa + # reservado inline (DEFS) con 4768 bytes dentro de la zona ejecutable. + # Se asigna directamente para que el heap viva en la zona de datos + # $8100-$BFFF; los flags --heap-address/-H de la CLI se aplican + # despues y siguen pudiendo sobreescribir estos valores. + 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, diff --git a/src/lib/arch/zx81sd/runtime/io/keyboard/keyscan.asm b/src/lib/arch/zx81sd/runtime/io/keyboard/keyscan.asm index 2c5809320..8bf0a78cd 100644 --- a/src/lib/arch/zx81sd/runtime/io/keyboard/keyscan.asm +++ b/src/lib/arch/zx81sd/runtime/io/keyboard/keyscan.asm @@ -1,179 +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. -; -; 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 la tabla de decodificacion de abajo -; traduce 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 - -; --------------------------------------------------------------------------- -; __ZX81SD_KEYSCAN -; -; Escanea las 8 filas de la matriz y decodifica la primera tecla -; encontrada (si hay varias pulsadas a la vez, solo se detecta una). -; -; Devuelve: -; A = codigo ASCII de la tecla pulsada, o 0 si no hay ninguna pulsada, -; o si la tecla no tiene equivalente ASCII (ver nota arriba), o si -; solo esta pulsada la tecla SHIFT sola. -; Flag Z activo si A = 0 -; -; Registros modificados: AF, BC, DE, HL -; --------------------------------------------------------------------------- -__ZX81SD_KEYSCAN: - PROC - 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 LOOKUP - LOCAL NO_KEY - LOCAL UNSHIFT_TABLE - LOCAL SHIFT_TABLE - - ; D = indice de fila actual (0-7), E = flag SHIFT pulsado (0/1) - ld d, 0 - ld e, 0 - ld b, $FE ; B = mitad alta del puerto ($FEFE, $FDFE, ... $7FFE) - -ROW_LOOP: - ld c, $FE - in a, (c) ; lee la fila; bits 0-4 = columnas (0 = pulsada) - and $1F - cp $1F - jr z, NEXT_ROW ; ninguna tecla pulsada en esta fila - - ld l, a ; L = mapa de bits de columnas pulsadas - - ld a, d - or a - jr nz, FIND_COL - - ; Fila 0: la columna 0 es la tecla SHIFT (no genera caracter propio) - bit 0, l - jr nz, FIND_COL ; SHIFT no pulsado, comprobar columnas normalmente - ld e, 1 ; SHIFT pulsado - set 0, l ; descartar ese bit para no confundirlo con datos - ld a, l - cp $1F - jr z, NEXT_ROW ; en esta fila solo estaba pulsado SHIFT - -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) - - ld hl, UNSHIFT_TABLE - bit 0, e - jr z, LOOKUP - ld hl, SHIFT_TABLE - -LOOKUP: - ld c, a - ld b, 0 - add hl, bc - ld a, (hl) - or a ; fija flag Z segun corresponda - ret - -NEXT_ROW: - rlc b - inc d - ld a, d - cp 8 - jr nz, ROW_LOOP - -NO_KEY: - xor a - ret - - ; 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. - ; Las letras se decodifican en MINUSCULA, igual que el INKEY$ del - ; Spectrum en modo L (el por defecto): los programas de la epoca - ; comparan INKEY$ con "o","p","q","a","y","n"... y con mayusculas - ; no responderian (p.ej. examples/comecoquitos.bas). -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' - -SHIFT_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, AND, 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, ',', '>', '<', '*' ; £, ',', >, <, * - - ENDP - - pop namespace +; --------------------------------------------------------------------------- +; 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/print.asm b/src/lib/arch/zx81sd/runtime/print.asm index 97c589c7a..1bb7a0285 100644 --- a/src/lib/arch/zx81sd/runtime/print.asm +++ b/src/lib/arch/zx81sd/runtime/print.asm @@ -418,7 +418,12 @@ __ITALIC: #ifndef __ZXB_DISABLE_SCROLL LOCAL __SCROLL_SCR -# ifdef __ZXB_ENABLE_BUFFER_SCROLL +; 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 @@ -487,9 +492,6 @@ __SCROLL_SCR: ;; Scrolls screen and attrs 1 row up ld bc, 31 ldir ret -# else -__SCROLL_SCR EQU 0DFEh ; Use ROM SCROLL -# endif #endif 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/fzx_fonts/academy.fzx b/src/lib/arch/zx81sd/stdlib/fzx_fonts/academy.fzx new file mode 100644 index 0000000000000000000000000000000000000000..a2a7f27563ce43c4b22aeb85c0cea9dd8368b3cf GIT binary patch literal 1254 zcmYjPU1(fI6y9^D7eOz$y0>+p4{prPy3hwllGX(UsSczG+fgc1ZzjTIC_(IV9cD>5qF2N}>Hq=Q*7=tCcj^`f8`TvN}?UB&PI&77I@eczmO zQ&>3;@ElA%4;{K+gx#;eo>S0Wgj*J&wG6E{VgDJ}zY5Q+!jUuZ^a^BWLA(poYal)V z@eznmKzs_~a}ZyGC_sD-2OZq=4a}^AT8EkMKwJW`3E~G3KY_Rm;%5-Qg7_W8pCJB% z>8l_Zifd3@hu!N@w6J>Zj-Z&w z<)hen5Y!GCdI!~esNTochj_!s*t&>EFXEmrFe@-|n5|=S39}I8H-{zVTzu$JSOfGtO4dnQ@v@s-(Pd({r5Gf-$Rgqn6gP zYE-1G13Q-uXrCk>>Pr$SNxA+|p0%SO5caz=c++J{2&%r^ zqNI30J3d)l+1V%xx4k_cja+|N6veO~6lgunz8@Y4GfE&%LsqE*0Ypm(3iOsmv9Ymp zr3k}+LsTm|sJ3IW6+vH3)#SX41X7l=Cfkt5upiVHTrHXUMkijI-`+p$F_L`G| z#vOFp7ziDuKy=q@PAX4<=z%!ul9EcW%YB|UL(K_6IMgpC+Sqk{q7j|)Hl2@9uFlux v9JD9D4AI8w^qQ0INAq;@nuCaY&AK=wT=%R^Ii&1KGIpN+@kt;U2{8WvF+WK@ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f77eaf27b0e335d782825671f76b5e9029111b6a GIT binary patch literal 1310 zcmYLHU1%d!6wW<23?o#zn~BqTs4%1nJA?|eYbs$4%iYYlgxH5^EK5<5p+*^kVpj7I zHIzYvhNX)`DAny|VOp{!#)45q{HbfiimR^B?XGTHK?3?D2=QeJ7|=VD77sJ`=ey^g zbMAM(d(o8&La!jQh=Mknegh@mLem$}^hI=H35Dt?^e)m@k={V(ub^ixqw}jMyoO>M zX!3m&`vAp0MzPOO>>7%FgQmYju^T9M6P>w*&fG!ief0P~O8<(49-96gaepGNkGQ*t z8zK&Xy9dPkU|fK(1ouAxVg$tFAWngJ0>puR+L$XcgXh9inf7Rs-Q8Xm5kC4B86d254&_Y=GQ`$cG?&0^|!g`z2^!f%Y|M z--7l%Xg`AX6GU!;b_=whLB9j(eb9abtp}5TK;SQ!{2LzX!}whg{s9hQfy3Mw7LH+V z919bei(uh6<{retNz5g&Krwd~3y)!L79Tr@gHK_+fH?~bFJSIvER->~h@pzP3m7|? zTfxF*%&lSDw}72-oV~rwFvC81;0~05ynUU>GfT1|&(%uQmMLYPl*-;OdpkoOutPS; z-7dBJ-7e1);-A>^{L*rB+aj{*GGDhSP3gfT9VTb0e6Tu`afP{o2oirEwin}=4vTusoid%%Zv?P88(&sfj%3lZFV|DrU6=B-rTaw!E!6A zSM+>cDp%HXw{yD-jifT?IL^AB>{T`jg=}u2QDI2KLm*|fHWfwbP$_C|*4uir@ZRh% zPwXz#&6Z*87)GbOln5unmQ!@2FHKqcN2QixDVF+lBAY$PX0t!2^EdR{dS*pR6xP?< z%Vnz>UpV=^t0qfrwx2s)ZpPdBc;sMWDl=SpH9S~cCWIKy=Xx^VjOLroxl{RiD3pst zrEEJMj~B8DeM6E`QZv1s+s$pyyOrlwgM39!Z}t2h*=+K-CxqvFj8EM?IHO~>sPWrI?)ODQ!vj%yOf9JxBK9bhZlmIJ02&2=f; zunVr^p`JAl>h&gz%C0d&O>fn+Z5GBU65{SKkD_f7R)QB^W-c9r4KA(KS}lXcb6p=} zDf_H%eU-8{Qq_xmWL@?!rX<5nr7vAwyLu^|BCH&*>WRQOV literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..08c1cde105bdb55319d5624450508ec20c2e9e93 GIT binary patch literal 1358 zcmYLHTZkM*6z#e-1ri!vnYn8#B7wyv9}4A?j{UKSf=Ez5@)HpeEF>Qh8|fIqPUx&5 z3Yw4L0|EgFuAtd~21G#-gp?2mB#o{TmnIp_C}JWEu@#ht6oaR_XSJBA$L;$#=iD2L zlZB86#LX*Wup$4}vd>dwvv4zleq3M0`~&Oa%Wa_#eU71qZ~6cnk4O!~^16iSMBP z0u8ni-%Y$k^Y;=j6F)%w5b-0#yNGuaKSBHy@gACghWI(+{lq!-4^jLQ^1DdDZWnelzK{v8)c6r59BSkNwrm~MXBzRYDua@s{3T`L8%^=yRqYUq&g#epGft&RA;67N~-fxeJ8D7fwfgt zrPHbZ>B41+YxMoAA3m=4UKrevq{+c=&P@**t1~mUc5E`;ZtXKolj$@`Q>~rT+8AS< zvo?&hvAHWplhWl|%^3E&Zd_P2_>412Lf0gDQ&rAI2!P2od4m)F&V{!pnK6jZbG&i7 zA}n$M1yW;z0O{-;Di}TIT;SYc+>AYpEX%nG5IDIovaUd2>F`HZ{7}G^)*>MuSis?I zT?0^eS`XNcP|JVxE_IDU03P{OHCZ`oz+!|XemQq!cx}3|F&viVWHK6+CE|Uk&+i&| zwC~Ej(7q8@fM@}tUMau4xA$+4JlDhFaARY74f%4dJFCMD%0`gZIe^8&ZgdpZwNHT^ zdifi=Q0xI>=L*1Zh4o^3(m>MWVc1#W3}H;YZpUM5J6kKQcP_y!@FlE`qM&8%X=6H? z|7q75rUazBfZW$;1#KD65{BV=a}`;-QSuU+y?INwBMYh>(YCq1aNA(#k_=pvmX=PU z7Rlx|#z5o-1DICJD_1UEzI0_}InqH#D1t`Tw=zv;mI|3REgg(Bf&O0pDhoPMstoY` zK)})gt}@VS&?;CuG-=}1hpMRcAJE8i@vVWeqa2|P?a-J_1+{1Gsjogd)4omaTY}^$ pgOw(;$!UrBlx-e=*)}&*%>(fL)q3YlLA|pL_Rbw7Tzob}{R_4_&zt}N literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3f5ce655675833082db9620d2428882d9ac001aa GIT binary patch literal 1450 zcmYjPU5MON6rOv|G@_)Xf|U`rP_+{UQEsO@cYqCH<@h@A@|G4z2|)2 zcdjc=4n_M_(K{}79~Xl+ME5PxeNP;CU-antQ1niV{zqbNS-iU}7S4(jXNC8PP!~l1 z3!yFv^^H*93biKGNT{EL`c0^@I9!M$e~SJ8h(o2=|DWg!=(j=L0cty_yFtx>x)0QY zpdJA=3ws}h{^QVp64X;Lcox(Ppk4xX6x7S0=0Uvz{UN9mpx%VRJD`?e@Bye(&^rU& zbI|(~`k%p>3vl2f=r2Kk1^R2ymqC9A`UlWgL0^OCeuTMSVD5L&*Wvj;K>rQ;U(h!| z3)EXs-;Q%zQQwJr2kLuJ??ins>MqVcgnRbj>;U&FeBd$ko&BIfmW> zIQk&PBZc65?g_zDMU8dOxFc9lgKMxq;nwd&}0gcURll z+4gp~orl}GjaHs#Sr&%I@Ds|a`nalQt4gM6){!y@({w(|7OJdT2!c4sV~#waha-;< zMs!3*2PH3(wv;8+bsX2_({XIf>e|}cuf`OGsL|~q&WzR!sSR)DwmGRq8z2EEyrgjll3~d zk&Om$Wo~WT(vJ-?qRxy~InAnw54mXL*2Cj)==gtv@!lLBrQCPgjupJM4{nkEwP3aM2eDyr5x8* zST)uNYeQ{9R%DF@n+Di@te5I@6?(Bfw6M&?l%rJ^O}LS41@rcC82Wya(6ynjMky`x zIUOgJ#zYa#I0&MMwYK=hmy7OOQ9N^MGFe%_a%G*2aXT$bo^F;!^>{;nnyt~TS!Ppb zYnC-;+EcWNtpJ;4c{D+bEj^2xc~EDTK->v!j?40~&ew=4ka`)k8egY~uNqZr1~Ns| su(Ca@V>eNfG}WkFN0Q3MQ9f7Ss+&kkKO~PS=rS~jF;!@?Q^eW-0dsaSV*mgE literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e56c75498f3765f812bbd3b0536e9161f8fce064 GIT binary patch literal 1332 zcmXw1UuYaf7~lD3?_4C}c(;4KLeU{cSPasDFC-^95` zYy<2P)b5AcgHW--IS93f!G07DxKMinoJnwIz&Qd=8|HD_q)NlK5>QiqnGq2=eO`2sC3QhJQim#KM@mR_T!H)#1RrEk&FMQXlF z&C9g(AuWGQOP|v6HjQ-{u}qSF5ajp^c)pptIq&s$IA2&_=K-JVo)9q?x>WF3a1|oP zO1#8{r>TguG_@?o@B#}Pauq{1c(|!*vzG5$jGs7{Cp@yOB($u$EBCBJMZ$tP=d2zgLPJV~kPU@1>vf|vHBt;*bR&XiMuvTeI83?-Z17WSpWKX3G3L7X)F_iO zlD^Em+0{5km`wT@GRviigphf$i-Ed7j}MG|e+3Ih1EFJZ$inC*;ZO!)Aoue!Z?_j0 z=jS_je_UFco12}zvriN8B?1rhc};ryEah-{y&KlMy{-JJQh`VKj*1rUuIn;(x6|p& zPmhm}d_1`RNbiib_EwRUSlt_F$z$Av;G8kb8Ympj2Rzb{|2Cw+rt^?RNwS=ZNMdY- zNgO9(UF9$vCK;Z1pbEuc3zZcSDq7SIg^^+!?}}Z-saQ~?zt?HJ_zZjO?BZw0Cscr; z{vV0KvN}bqwIvlm7>6tg6_P*?^;JFU`ajG0@$o)dCFqa*3AfGyo+Vr)Spz?eaoPpV zVoi}Ax+o1cTmFS>SFT;~TXk*}B&pD=kDRl0Pc=>yIiWEa@@CvK&P)9C{q5$Wi17L9{G#oX&06{~U%H z=JDn`?|r_b;H@eMZ-Y^VQ}04*0g{W5x&)~en0y~5KY+>0U|#|IDqOq@=dQrTRj}89 zUW4(EfZhbU0rWP|O`vyxegX6wn79l1Z{f^6$ZWyP7G!)#-G>wd`T%GH=np`5f&K)v z4fI!_T^Rcv#`b{z0rWA@zkv>bB2o$IQ%DaXJ&g1jq|YKfigXMqMS22{KZjFOXeV*% zc}%{5*G){FM(0JGID^j1IPnTPuj2S?=;U!?2B%)f@gh#RICc)R=g}#l^9DL^qBDoi zTj0UZyW_t3e7$rUs{#N;ZTx{SxyQM-=PEz~|mX%n^2QTiISyC`j; zb|0nhQETAAA5r@mrC(6%qSV9jeH0(#fj&woY6nH>X;C{YO1e05RFsa12Tq8RA!;d6 zIxTv92;SP-bbp#p5^jWvkkofnlBP33-?=gdalRmsMhU<-P62 z<)Gqr2r&$!Djm{|ZrdEu6{(xn6Vru(t}nR$OHvT(hTCVY%7Bd=F%84C%jO=d7HUiL zis^cS;8}veb-AQPLLua<@)^7&cOs3nL z&00;r(b`(|n!fMPGT&SMt!mr$fHCHF_^>*z>k_ij$>s_Vn+?X>@qO0b?wae1j4glm z)yEGStlVIW+vn58Y_`Q!imQvu_2z}yY;K=lSYUotV!Z)VRbAH^+upYAwBE{|r6>7m zef_&Vn>9*CzRhxtvq@P#vd0wN=(PXp3;}j{H>%7L5z--Zn|%AnwD&Em-_a{K%a&Cr z2K8n!=LexImmUSRav|6EL&M#-c-iG%P*p;nR0x8=6^8v70%xtRaRuJaPQa6f(em#4 z?rx>F6a*!nSt%5A^`>8Xv{Nf9&4Bk?U7AxuG88QtK~SGI^%gfFsNMc^I%pLhw(MRx ztyFq7-R+sgC&V0<&Wk5HFFU`H&fNI&lN*_|q6$%YqjHQr@jrp+tx-9~#xtVsM&;Ny z9*9aBYD9c_AbL2V{s)df^Q0g}jgjg^hdRzJwZo3@#f1NvN^M jTs4BDsIl7#H71f!V{=u0DZ-;BcAxmnFGT@Gjg9{omonFH literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e67f4edfcbcda20fbb98810e230af9f34e621f20 GIT binary patch literal 1169 zcmYjOal|E66rXc$vTIje?=7Xo?Q63{y!Xx{)@JX{JSmCMCQD+HX#a@W{@S#0S@D!z zx80Tf*y?VIm#9~)ZPgz`tV;Am`6!tsmK0Z({Y2?^XTD{RnK@_9{hiFHCD%YFlTj=2dA0WdH61?4|wnqt511&fz_8h zY*=09d%t5|vHF?SuROTI>M9TaWc4?zO}^zn9xA1_tKn@*?NSbwxe#=@S>my#4zVm- z`^{=p9Xe}+U51r)Ho@Rvt~8sbG$tB@pV&0pw6U)C%aw0!13+7OUAb6!qkUyU05G~% z>$Y;O_f_=3+sapn@z%g*nX_32HV|18vxqM!=h|du^yt~e7w%~7eMEr=19)I9BE+?8 zor~Sq)ozIlvJnnKY&+6gr%Y&qPetI|*jOy)-md_y0R;nI93TuUUEwGA+C|9S3g#=3n)!UbSd`j_^xsL3Y4+G8>IS0qwkGv$Y@1v(C4yLczbsu?fw}^&)#VbkRcKsg z1IkkGw1tH5xC%CyNa2FAWQz+}r33Ha!}-P#38Ei94tt3!an6R*vhG$#`MjrO&^-SR%wbazo9 W=h*cwsvV z3)K$v9zpdeifL3Fy`89bq4y;2dpph$F>CACj)%rLmr~0d zC$eT7r;5y(Xy8>4Pl-=cfjJ_1uE~tH$W$BETB(^3IQ{sMSF%)p zz_JI6ft{+wYSNaHrfd=$^?4$*s(EHpwmhCE-R^))^cf>_Q>Mv^(sq(4&&d-pySq*u z8xFhOxw$-V-O`>ktDUdp3-x=E_hq2x$$%C}+3k`_vQLFkEN5qrotjt5aos3ap-W!} z^)0HP-dxJ&1SScWEct{mGMdn5H!RjDkt8fiH_VDy*MqP~+;D9pcEge)B|^eC8b-mi zm}E;+b7{}2xh!Uk?y)hyGX2=0<;&}fsmZj)9_rdmldrQ0jQ#vSK&w=nu03QQ*3gCAE{t)2f0G|ZWuK}I`_y>T02GMzd ze+Q8Uz<+_rB@p=sd~gLMu7WgxI1E$Q!{|*gwF*Yoz|Cu6G!7$oKzt9xDTuegbRSIJ z2h%!CY=@}_U}^_U4MDscrXGRmEKENNQ+b$v9BwYc)#Grb16Mx}@1KCtmmzu$qG^cU zhv;L7PC)b(L}iG+gQy149PBy=(FGV@gyBmtd<7yPcU>oU-7JTrGFmI6+vV_`a(JVR zw#cYoM%!idpd22S(Wo5evZ|_-G8QV=^=7rIkB^UEb-3^7`U69;NG;>lW+&A#r1=AW!)l*z~EQ z#xwJciHVA5`Lm@-fvC}ps_Uj*nW*_TwQIGb3yv~xQOlyC0Css(tP5~4rQ?MhkJ0wS zWNBJ0vJfj0etPQU$v11PuZOIUbKf_N#){sCG3X8$-1o>`hAvn(Y&+A^4bHjawYIgk zWvtvT%kw5G3x2hh+{`aMt(heR{A!}heihr0_K`fyBh}2<%Li`b!xwvWA}=hVOmy> zA6I#fY26KOs^|TEblp0(&(Y+;TCF0^D5Q@X!7^3aQ#tm=+=6N5biHQe))UpNoNoNF zCy^vQJ%p$i{OTm3w{geU0wV)!-Ev*{N)$ywnj-8I22z(=-skQ2%<)vytXqtEv4l}u z*zYpa5s02PcD~NFwJ7BXmp2L+uWYNLm9>InU3;;*Xe;wY(O%fUhhJnNx1Jdg>_{jk zpp>eqx{RAe%ltdghviHIl!!*>HZ4+r5vmsIN1@!XV3RyNzMM|h6KT?*{+o0DprJV?h1b+6!k|S8j zc!m&0#8^OgR88J;n)9J{?8Jc`jgitxoA^_4a#_(NX@@Mx7)dVaOY@sLaVw<7j2+Dh JU5a2K{V#+nHT3`h literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e365183dde159f60a95127c0e1649b0e8e4e547d GIT binary patch literal 1270 zcmXYve`wrf7{}lDdE@2vJT%<0T+vcrvTNSp$~@@Qz>b>5n8mH&={BsgL8)UDk#W>A zGVSKe5lX0s9=AH@sM>A=e=)=|{}|{vor;3~p`ea2q4<{|Vqt%Hpj6+aA%x_8-sji% z^E@T=)*Qa=1g@UM!zZzw;L59*cQHSM%`>=q4p-mC4=m%I=kd%1d|(C7EMxl;<{#tX zPcXlN`Dd7aj`>%Ze~bC|nE!ySpYW4EMf}B zLEV4~A?od1X1sLv8(nyAN! z@d8m#5KUvuMIyZ3DWjKBRX!p5SH8-S-Eee!-JQDljb`m_*TUM=VlwOYM9p=D5bmUw zhV~k)&l-%ayKb-NiuGO(BR~qGNJ9+C#Hb$Z?Oyyc8n7GEy;QLP0t@#la(f9*pBQY(z~TNGEINZPm-~$Y<~naG^bDdS5$-On*X%7h~TpmP#`n#)c-(Dp49D<|99pO6F4` zsf^GZ(WVn~YDr0&WB%D~b0v-SWbxXja3N|`lP1=i?34zPi5^1-(F*zib&-!8RMJq^ zae?L&v2#OF$qhvXWyLc?N>rCZQo%(fAF1@ndemu-UV3Dm=rm_qplGwf0?lDdOGc0= z`IbmShKDHm`0es0P@U#0BeQ0Jbed0JhGeh`G~WZA=7e;bk6p91j26}J+gtz{lNky$ H--!PKD*{vr literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a252f264b6172fc19d799f83929fa87e62a1a044 GIT binary patch literal 1234 zcmXw%e`p(J7{~KGmtJCw(qha`h*`U&?~733U9O9#JJVUZOO^Ty>zsdxM*>Hwp;QX- z)N59yV8wzHm9=%kCWGyd{ZTy^3rd9!D$b%%C4#~!G4s942?y`J-}iam z=lgt~_jdT$0(ceVc^I6B;sO*Gfh+-8h9k?6I}5oFfULmq8dN`m$yIoH1;}|I7l2#> zas}vBAnQOs1NsG!4WKsACKMbP`x^3FaB>Us-vap#$PYkn0QnKf&p>_!`Ww(cfcypY zZ=nAI1*APlZbNzp(mtejBe@63y+{U-3?jJ?>HSEKB0YxWK_m|&eFSqSkvxjI$8q>6 zJof|^pGNH&B+sGtJkl3Xdl5+$%LdYE%+Da1L;4ElU&r!1YG+V;1Ivr3y@lnsQF{mT z@1ph|YVTwDLoA;|Z4Jxkaqt2TT*ARCNG>D2is?@=_Bp0EFm?^oU!k~#>1`Bu@X!qu zf5Jn*VC;8H|B2#1n7)N#kB~|Tv0fpT7Q~E@9uQ(wNDm3Ig3zM~Vo4CkgxF(3`bi=7 ztiYJAM{K(`em398kGIF8r73I3J5e}QlZw->QajD+yLJ7<0@H2V)*1hJhTS`F)|Gb`(s z^g5ul7%MCRq{BUxEreBf_jVzx-rlp^+()#+bXr7fW;6 zs?oIV15+6?G-ug6T^d%I#WfexiL;t{6Ph zFecoc^}Sqf^pth5q)jMn)?KVrhBFe|8__tWN~gtP+IGwDC@Er#Gi9vBOph11l_@e) zZNtyeSSDz|w*6l&)OXd<)r%KbM^!N%2`mcHkbWu@68{T{ zXe20?iih-q2;QMD1mojE;ED@f%seHiors3~c$KatF&={DN`Op^hcAM^kpPGg4M&}d yhX_;g@O|()0h2^D9Ccux^AV!q(0OPVN-7@CIL?y-Lea#r?zp>NTuX?C^ZyUjQLa1y literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..709689c254e80d7f3c17a4c910072de930010c69 GIT binary patch literal 1241 zcmYjPO{g425Z>AUjTCf_&eYqfG-071pEu|Z@|9;UjqIM_%iU{!2bdR@)gKeA@4!H z26->?b;#Ev--vt@@;>C7k#9x54f%HDJCW~3K7g}(kPjl?hbQkvK7{6e(i*tV0{kNd91%d^*z=XQT>educ-dO`cG7svHk}Y7WI{d+Edim7HV3k z8w$lmy{}NW6!ol7cNFzqg*s5EgM~U&)DITwk%H2g(0o4cn=wJqt`wsNhV^^36bmRh-@Xnnb@U0zCWtL>(bRTbS( z%4xrLgZDP3Fs-d@<(021t99E>S{X8tPFV#qPGVXzgoI4U`fz&3r3fFrAI8^u=bX3e zUab8(IyX2M$HjGWxA5KQ?}c|_Sf(M+{c?BNr(hY;{}_U}dA|ya%uBX5A;@LI6Ft&N z0tC-W_?V1R$mlOBrc(%^@xCFM(vksF39*xsweC_yC1VYXp#gk zcUDqaC#4Jtd%i8!EL0;!q!Kn2h^vjlKTe5uiDb@W-0tcSDjTIM!ACo#NoG2wO%s}p zq4}R7BR0~>EMoSMLbD-tQ4E<_L&7$xErtv)Ir7gYJSLfHDR>)!6L6AlDrxhdA%<3H zitnc74Q-@sXx)oXzs^9iOXcR4XZE7fm_#;P)hLW0=aPveCPPyb(wclnLt>H*{{apZ Bwub-! literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e22bc25de4d40d0145f8d0edaf5faef1914c9cf9 GIT binary patch literal 1270 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..4045e4b4b15af1f60945c2f8730a4726a9953f00 GIT binary patch literal 1222 zcmYjPU1%It6y9@2=Rt>1We67$SvTx$kTp!Q+Y%%M8YfCI;s`}Xd~g&kc^KAFq(nND zC8E;$gD9=7QnmU}P{dFLLqxrzNFG915W=;FWmqIEVYn=UVX4FFxigFSF>`;;{m#$* z&b9Dz4EPdE$8h8{96bZom*L2(z|XRV84!}t#H>%cp}yTJE=?*muB{{(&j zl^ei^z!ABL{1)V6$ZtnJj{FYfcOkDJ=g99tK81V+`BCKeA)m*}0@fF?avZA<F+P|Xt z8`>Q-|3G^m%|6Efj zh9HzgD3{dIQfJAD`zv#E17}OFHNq{&zZmVlaVikyK+}S9O9TYb@}11_9U2@mw+2F3 z%n3xOws-ba==39vl{92gmMy2&q?ImPwNNR`8j5sGscSQ+N#Tr$Tbly7YoFRlXnYhK@`%QF=YOwk#bAO{`V{5d;yv1MlOxg-chi zTw0i8PKoN=j4U>i-5(ih|1;#-YHTE1!H`jnjr!)5OLHqw_Ed2WhP2uE3pk|>Q* zT2Zp(Qdw;D6oNtvCn!3cP>e!5-x<9NvgC@?*r>%ihE~U7MW@2Gpj2p5W24@dy>hP4 Oe_bY@+>jTdJN_^Fdsoc> literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1b598e042d4a7db67f2c77a06699cd2c9c5f93b7 GIT binary patch literal 1266 zcmY*XU1%It6y9?t>@2#J723MWiW5l21d2|xyK{q%>2_1Mc?gKmOr-Wc6sqLg- zox(6Xn}%)GShc2*#kOf$3>6|u{h^?hY11?+O9k;EqUiWTR!~c*p!+b)dS^HD;9;11 z?|0Ar?m6H6qHxZDwquYqAytI*B&5oaei_oIVEgNkoPlHo$a&ax0Vd|*r8$^r&c#cR zS^)7~5EnswAH*vlehlJO5I+U+bJ%Ia&Lzk$!=7cxu7FsD^w&VX1@aw`AA$S|N6yC$+6@2PLBv+BYhU_x(Um^Pq@~g-?*!df>e@r<0y(EgcN0Ub>NKwcYuCe{X)H~&fWYX#tlwuCpGR_Yg)JMj3o#e+qNCewmCr% zG)Kx-=I1L}iR0=V=O;95I}+nEmhsBz)1JXO9H-Td^4g%_8I#haXC!*}dU46jxZ$#4 zT5h>)csDvCf^JM2bk``G6u0dVy~O`zC{u>3hwAiCIm_Xw5E2A^X7Mvi?;8@gWwXE3 zj0JsLzEJR_{^L{D^-9jmq9Q4XL)E%D2m2vGCS`vF$yHc(!zn(lylDmq(=D z!Sc$|d_LvpHJ14313iU?V>N>gnGDCpVpKXm98SC3oVdNa)-xG5VpOZ9qZq2{>h?{2 zQ`BGURoB)fopWhN`VLjyalDnXCWJKM13eznuPMHET ebj4aC`4w!udyJm*HH&PH1Qwv@e1x70kpBSj?A;0g literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..965e28676df2da143fe1355eefec17177208d0fa GIT binary patch literal 1197 zcmYk4QD_`R7{~AXwij1CbkRL~Xt4visf!})C3|vWC}W%piv(Hls6j*yED{89s})8$ zaY+vyG3Z%?O~B$doN!_(L4u@Ea32mi#D_i<>_Z<8DcA>vLwwkWh<>-(fXnXA?#Imk z`_Jz`CAioDd>3jR@H$Zc0K9Wh{}AdQLrp{N6R3R(vIp`!y!APp{tV7!zup(%eF@$c zcwd3{HF(?LeFOCYc;A6{1;h?K^F1u?LTeWme}MW`s9yt~03QM$0sjT~b>KIE-voXO zX2-y91HS|O58xB1{tY|@z7HIb7xCyl$nV1w4f^AH)gnDKW5Ch zMv_EHAsO|0{=QE)OXJi@*>GgB<#iUYmOd*Y5z$2hT~WYND$|R#lQc<8i-{=Q91KQb zJ7!c5HC58obSpZHqBu@j8cj?chS5apaAKMZrWxvxzUfh>8AS!s76p-zkYH&crMHMw z*R%?j+PHizQtrxNr1e?V@9*`yY9)?i8q!v)bwj4~w-?fVnom*XH1@^)oF%UdnI;}5jf*6PMazuQ$i{xYeEm43gwu^yUx zQmVl3!bzzfxqrJ8t=hJ|K|-`p$~hXO<5*fwg`D+~>pIIrEv0Nwv$YO^>8y>X;KsBe z%N!FNHfG^~hJPE9`s};D(kc;3%01Ule79sOLeh}?qR|kcNJO}ymRvhm3N_`lLPPrwvAmjj3%i&MIU%h!+;OE?wGMTqs$E%v^=#)SbK#{I8I;vhO}* zMpLrAWmBfPQ6aPBSUKK+6|&Bbl|!qr!hxX*JH$z)3|7g?&w`uNa77++zz*m+R(>$3 pPX;7ldB&N0f>Sz7n;IV=ajZO9I@lQ=7EF%^WlVjuLdnWQ{}1`Yn3Vtk literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..411de344323a56b773500b409133cd7c17dc6884 GIT binary patch literal 1230 zcmXw1U1%It6rOWtvwMXom$*q+iRIGFUu)Irl57`TaY%PIV+>&$w+d1r53%%N8^$O) zVhCeN(oxfPx~`Jc2oZuTYXzaDq!7Y_C6rJUL`6!KrGog-hbRamq~M+1@yu|~+;7hB zcTTtP`79*fg(I^t_#WiuVCXF579i(C_8eqCgzR}RFM@dqW-h_&7h&cCm;uN&7+3~* z734LL*Fmm>dXzUt{tr_HJNu3zIi7xr4pmWAZ0V z-@#-H`~JYxeeC@kQz51wU{Vwhq{Oa=#pI)6>M^l*znGH5eFw$lGh$*$Og$_18DdvS z?0Zq{Ixc!#1TP4zJ&V6ATnn|3-MmHF*b=i@*?-IMjL3#7E}qRo4)cTYOW14@$_&r( zBXQW|8sp;l5g2l%6W{T8Y?so|+a>(wc)O8hNFo!1`$cc$?h?ln1kVrz9+!tSSSW=2 zG(2~nP=UI&E1RCHTUOT1? zuw3h*lt&obAcO=h+g_$rlS=&5b=}_J9ZX$TUYV#qakg4*n3ZbKbB2fF`TC{tnv}`2 zCS&<_7ha2Hc>VTTK^PWRgfIBB!oLavWkS2&ZW<56jsfj|AWei$R*klqTt3B3+Wl%Y z9w!Id)oC=QocnZ2NRyaMz7U<#5+r6h${aD+P2&||9eOe>9=t|lLV4I|UuMqmJZ*l` oB%V8u+UBa8#v5R>n42_-=S|Y2%@echyZ}D=1QFT*Wqt?v57nr6wg3PC literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2ac2fb3b07de047065c9e5ef3dfd82188ff05e30 GIT binary patch literal 1295 zcmX|9U5Fz^5KdP+#|SxSHJuFz(nr!T-iuK#7(t|!%w9Yn?1!ubeQ+M`VEqBd^EgJl zlW{pU$e}SCbUh#B+DLhN$5|J+u`4W*Ylj~n0*rf-wFSN@GFF0 zCHx=4uK|876s`ka1b!pT3E&d=LEyIlza5Hq0>2yhJn;K~YnWSr;)5Uq;K!iy2-F^f z$`c@;g6E%v+6nMa0)Gbl)4-R&KLh+M_^$$g4JzkAz61Py;OF7+hu~iX|0D1}0sj*C zpMifF_$JJM3G-t({590J!2cHf@1VR52Y!U|4#=Nj?l<7O;9LQI6`a3-19e==_fzKv z%1hL_iSjaaZl?TJ>fAy3UDRpN!oAeFpE?WFd5Fq~sdJ3-N2wE1=OlGb(c*J--;1<( zmhS7(*hntcS`W3(@TWyI485sH$4sb|k{S+G(p^+a#X=!e348>qA_)^zG9l7b#aWuV zh>ye0N;2+*D#ZY`6L#Dx`pn^WLRQt?jIk`9y7*OB@wC$kGZUs%h$=q#MlRb)(_D&z ze=0Uvm0(#eGlwmKAvqCtl8v=E#GEMo#V|?MlH@`==$eMkL_s(4Ja2a!zk|Sj4_L(d zXh(OqA7P)(^^Iz{w%)92hJvmk_9MIyJ07=m80ywIUWpi+tVin47Rtf{YKlbZRy$a+ zRikKibz?YOomK54m>JVrGHTdRTX0~rrzTRjytLJxoi^pc#>ivL8#S8AhMTW1dQ4py zxRKb*rlyg2YPxo(n1ZpXu#}6e+T2kp!`AzYLhC?nw?tKAlZdg#stz{m){`E^YUsp{ z(P&J;q}5hArP5{ac0oM`XGH0tr+z2%+?5n9!QZeTT>lFfrfbl5GFRM0I% zhOuCHG-@`-M-Bz8elfq)>n%U-PDIdmwVt%npQ?dAU=TZ8iJu@Fci0-xDAurQQU#aEQ&3d+=*f2qUD z6#0KfTRXmHwW<`MmJ5+KrlCzneJ!;fG*TAI)ElkV*3{PO#ycwVbC2%#3u3y&j8efQ zw31EGP-NxQJ8=*f$4;I5a=i>`B&9UM2(P%h4Zm*4rDf8iQn9G`c=({_O@}4`9hboPxRH*|FL?-CT8WV4H z?bu`T@vZv*29S?8M6tgMR{slY Ctv7uD literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..67592d045054302f75c568b3c06ced27df9730cd GIT binary patch literal 1235 zcmYjPQD_`R7~cP1cOUE`r{2{?u%x-nL?p^!G_2*Q8gE;rh=Np3d~k@e7MzEx)MJVl zR76xPsAy_}B3G5qS-vPS@?0a~$2lIWHU5Dm6%>Dpu1Ne`?Hi2IUJ^=m;@EgE?2fhdF zPpJP5{2!Pe14o>8k==~^7UZ`fKaBiNWJi!qBIC&JL4F^y`;pBdJBIb+SbYrZCy|}P zr6-U*iQ!Yop26m6WY1yqd1NnO_#*O`vHl89okji{*3aX~*D+kg<{KEkjp4f(zK_ih zkbQ_JKE~!Jcwz~|&#?J9hO0QWhDR^q)Meyf;Pe%&eueJ0SiOdB53B3wY+&stbbrR$ z0Nr1)_8U5XVD&F_{>7SQJFb2EA^WCVZRZZ#z00mm+Roi}^)rj zfK`f-H6mIWQ`i@l==KMLLEJT$a+*=pjiS5}k-VreQc)DC(RksEKC>_`7MYpYMsz$W z@{`PHli3l^^N4aMQd&hj23&~7y`BC2tzIwL&9^B#Q2l;i1%`nxnA7#7V@_J_k$z25 z^RG$0QDR21Xf5d0=6J79KpD}Lk|)dJr6kWtW*pPMBqM!4GuK5%VYB9elB6VW`#M!a zsg+V`JB`zJKmawUosQ!6K~8p#0PSZ(sX9@#C#|6=%C`dF_tUgJl%}p51|&Gz-`d)u z{wjI{13`yaoZ>6k(4UfrSP1|o9J(Dj#W_w9f_@-QNPt!0@%mp2Ecy9p?` zTcY%lSPDs*S250$!Cu8OzMbpofJG&ZcCIcFT_*nUZrgQ dj9xp45fI^B<^B)-N78d{j literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f7fecd4955ee35d71231ef38fa6c99754c748d7c GIT binary patch literal 1462 zcmY*YU5MON6rOv|I2UvYOVxCgxDRWZ3;Q6|hw0S~16rE(#eFIj)Q16a1qme--Lw)? z*NTWxC975AF~6e*@3g{*eC3jGwj$I=6=e= zXUzMOiLaTs#KiYZ{KCYqOk87P$i$y)?+v#1FShR%d*K$__YV^-5FHRRAa;Q0f|vv0 zf>;2t8$=IWAH)+No&>QBVn2vyKny^vfCxbx0&y6;6vP`K-Ue|3#7PkEf%pjAv*4Zs z_jB;p;pBPfU4Y;l@Ge8}9e6)Oa234YVUGfD1Kcg}Zi4qWxFswh1{{OCFqp;QUJUNT z-~sH-W8h)1fWbo;Jc7X<3>I?!1ZoU)%|8erLP0qusf`-I;Img|_{0 zn?Kr)2!qyOG|+nGhcGM(bA>WlKc76dzMjZsec?Iz@~ir~4CPT>#1yV%qzocSmqDaL zS-t5(}_GhHo>w;bjB?|Ql)vA<5`wcSeBKX^HQl?^KW(1 ze^Ke%8D$Y`S&kzm>13&4%)@rfbwaxsQu=!K&BoYQige!RW8xvk

+?dKpkVLc0 zq#BfK7qhb2%j6M^ae_|jttqHtHgBvh)(qW=Q7NZNsg*UV9J(3rgcZMQ7LvZWMr-Of zeCj)d61incFUOS$rOZl3Now5ajb~+2ix>WCM3of&K`YgohCl@n@Muta~47CL@Aw*N8?=AU+CLPX<%A!Kx*b*E!6L7 zO)YDy(8hV9RbuIcvZ82p)vsdflu*u+okCxJow8-EqEm`3s=^DOY?zAurU6QgsBk(z zauoe`ocrnU%1>8@sc&^#lx*s1=@Hw^!s+GJMyHHN2Q*T8YI;XAjx>Zv$%j!aj lPWa691ooWPq_^g;I{^vQ`AN4rWu%&=jaL$=tQsy${{u8G1K|Jw literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..7ba37c2bf702129d4c719f6cf42fd87c15966977 GIT binary patch literal 1367 zcmYjPacCP=7{B+uUN|I<$5OL^LoR793HZkj>SmCDXL61Xh6b#|KOBVmhayF;Q1qN5&S(p^r%X z650D1iGNPwD!>cg#HKf+z@HHrH zfO!K-n_&J3rJun31%`iv(iWJ1fcY1ge}j1!N&%#RstQf*p-=Cj`|hK$`>C9wu^g2T z(7}ULF3?z!%13DINh%+sv1e)IIXd_vl_zNI6)HP4`35B?Y0RbaX&O62Ba3wKB8_}P zX`RX|l(wk+EscFgdhZ{Cijn(BK8daRY3$`PO?9Us)wuo|i0}8`5YGJC?m=XwXmYoQ0Fp-46 zT9H&_5` z&uDdjy6e?_uh9)xkBj^Djkay#Z3E*B@pjMS1JP*oXuED9-o?;eywNSTe!t#owPuU0 z+yC79rZtPzqBU&f@A?=g^0#j`Ch=VFZnWnLwpX8w+L%{}sjDIn)**PsqCVDau5E5! z9>;SXL#t-HAv{%QTc~?0u#wdBkQ(0`jZpuE1X|Z-xyicVbAks7`b&X+z^SnEl526^ zab35AE9Vwt79OmM>vOlF8)&{^SkOmK;VVRgu=8bgSMMEq(OnZq{(=%ClGbmB##t2*kXN}-S}H!4O2X9Yt;NE8-A{{a@8JP7~* literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3dc5c7880b3c4afacdca53b4b3ef3d580d468a2c GIT binary patch literal 1212 zcmYjPU1%It6y9?tnJzel5vDQ^i(Imqn|TN_`O|F(xGu9H4?*T3=%R}bF>Y5G!zR{} z(pXoaY0x!Iq!f!45|Gk|JVXc@ZG^EbV;^EDC6pl)9YS3eS;9UPx=0YuZ05m_d*|N! zaps=yeCJB|?E>Hh=wE=ri!gi%hCYMg%P_nI{a2v>bLjsP%&TCo!t5$cUxnFaFjs(I zgXGu1ZvtNf{vBwyfo}lc1il4)8(#St27ZFf9hkfWnO|V|SJ0}U-2=W0+I`^rzo4K(2%4j)rP0iwHiqUnCMPg!p*D$wujAmGsJ)HJcW~@o%$`N_JH4> z@pn`npy;FWCyIZe@;7!iG5G{L{=v?eAd152X9V$CL3vIP6GBI~(5VX@F9@fG1m%oy z>Se(t#N3sY@#AqCthLtCTdaCdW)l@=vB{-#>2kVEpoLJjSc_=-FgmncH!ibu+41%s z);uS$Lp1ZtU5W0ucEhD>IWP51RF0(Q7hdY1$nk)M2w3ShE&ry>W;%UrsX0Fv*hR;2>Xz^NR)PrM&xiYxVYhYW ziO8w;NmRp*1*DE_F*Y|@U^_*-*dT_`tiMmF(^ZZ|zgXd}Xa^_1C+bv`ovW9&@3ZjQ zqD~=xE{wCP)&2d+CExOwLMo+}{OwZRPch~)mTDJJ4j5BOyR|l&xv{=}BQvV9SX-kc zN9J1*q5qF)r_<%gyePub<*0kQ9BZr6<;Z1MHnZJ zs4VKV#Uc-NIeH%LJY8JPnAn@)N@^qOwr}2#q`d@)Raexx-Cb97>|hm0Ad^5 AssI20 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..adc7e36d29d1857404c728d10b78d68c365d2aaa GIT binary patch literal 1240 zcmYjOO^6&t6rT6G10z9bNh>2LlrwL2Jp@KcU_B_cP{JsPEt-Sm;N&2z2cZHYqr_0i z9_p}26P9G+4-OHuQPdSd4+^%3ID$DShsYR5xq;wju5~Ux zhFx!qVO58s2yWTMm-TMxy$xLeX>IX}3;zku+P2doaMP}*w9~2=Isw=yZsSnhK}U1o zMnhG3AG}|t3rJw;Exr)T;M@Z6s|TpLX+nwhKC%Zx@F|LV?CV)Ot79`F_#d0t@BO@4 zcc3rMTt-3(HSOD&LveX?A3Zv zflPBSLZB%*4K-2D1as>hlvrq4YK?0&tPwk)4jDk;fP$c~Y^@b0q`)f@)ojb0 zyL8TM^;9RZ?5Xt%s&%6JeP54o zy+|e75`nnqV(kf-;27psYHeehi?-!v(Mh(%jirU7Ct1`3w-|P_Si1t~G)1Y%E?T6j NW>o{klt@%!(={Eb!WRGl literal 0 HcmV?d00001 diff --git a/src/lib/arch/zx81sd/stdlib/input.bas b/src/lib/arch/zx81sd/stdlib/input.bas index ec8bd5c71..13320a11c 100644 --- a/src/lib/arch/zx81sd/stdlib/input.bas +++ b/src/lib/arch/zx81sd/stdlib/input.bas @@ -67,11 +67,30 @@ 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$ = "" @@ -83,6 +102,47 @@ FUNCTION input(MaxLen AS UINTEGER) AS STRING 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 diff --git a/src/lib/arch/zx81sd/stdlib/mcu.bas b/src/lib/arch/zx81sd/stdlib/mcu.bas index 2c5d41408..1a7df9966 100644 --- a/src/lib/arch/zx81sd/stdlib/mcu.bas +++ b/src/lib/arch/zx81sd/stdlib/mcu.bas @@ -287,7 +287,7 @@ end function ' Recibe un stream y lo imprime linea a linea (para listados largos: ' DIR, TYPE, FREE_TXT). Devuelve el status. -function _McuStreamPrint() as ubyte +function _McuStreamPrint(newLine as ubyte) as ubyte dim c as ubyte dim line as string line = "" @@ -298,14 +298,21 @@ function _McuStreamPrint() as ubyte exit do end if if c = $76 then - print line + ' 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 - print line + if newLine=1 then + print line + else + print line; + end if end if return McuRecv() end function @@ -424,20 +431,20 @@ end function function McuTypePrint(fname as string) as ubyte McuSend(11) _McuSendPascal(McuZxStr(fname)) - return _McuStreamPrint() + 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() + 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() + return _McuStreamPrint(1) end function ' Cmd 15: espacio total y libre en KB -> McuFreeTotalKb / McuFreeFreeKb. 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 From df96a23d5c04bd276b276bb2ac655ffc4a6b4449 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 09:27:27 +0200 Subject: [PATCH 143/169] zx81sd: documentacion del port y ejemplos adaptados MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Añade docs/zx81sd/ (README, USO, PRECAUCIONES, CAMBIOS_BASIC, MAP) con la guia de uso de la arquitectura zx81sd, las precauciones a tener en cuenta al escribir/portar software (sysvars, contratos de registros, teclado del ZX81, ausencia de interrupciones, namespaces ASM, eliminacion de codigo muerto, metodologia de simulacion) y el catalogo de cambios de fuente ya necesarios en ejemplos oficiales. Añade a examples/sd81/ los ejemplos adaptados/probados hasta ahora: snake_sd81.bas, flights_sd81.bas, maskedsprites_sd81.bas (MSFS sobre el mapeador de memoria, aun en proceso), pong.bas y block7test.bas. Co-Authored-By: Claude Sonnet 5 --- docs/zx81sd/CAMBIOS_BASIC.md | 304 ++++++++++ docs/zx81sd/MAP.md | 529 +++++++++++++++++ docs/zx81sd/PRECAUCIONES.md | 184 ++++++ docs/zx81sd/README.md | 79 +++ docs/zx81sd/USO.md | 88 +++ examples/sd81/block7test.bas | 63 +++ examples/sd81/flights_sd81.bas | 129 +++++ examples/sd81/maskedsprites_sd81.bas | 810 +++++++++++++++++++++++++++ examples/sd81/pong.bas | 237 ++++++++ examples/sd81/snake_sd81.bas | 165 ++++++ 10 files changed, 2588 insertions(+) create mode 100644 docs/zx81sd/CAMBIOS_BASIC.md create mode 100644 docs/zx81sd/MAP.md create mode 100644 docs/zx81sd/PRECAUCIONES.md create mode 100644 docs/zx81sd/README.md create mode 100644 docs/zx81sd/USO.md create mode 100644 examples/sd81/block7test.bas create mode 100644 examples/sd81/flights_sd81.bas create mode 100644 examples/sd81/maskedsprites_sd81.bas create mode 100644 examples/sd81/pong.bas create mode 100644 examples/sd81/snake_sd81.bas diff --git a/docs/zx81sd/CAMBIOS_BASIC.md b/docs/zx81sd/CAMBIOS_BASIC.md new file mode 100644 index 000000000..5228bfff4 --- /dev/null +++ b/docs/zx81sd/CAMBIOS_BASIC.md @@ -0,0 +1,304 @@ +# Cambios en fuentes BASIC para portar ejemplos oficiales a zx81sd + +Este fichero recopila, para cada ejemplo oficial de `examples/` que se ha +probado en zx81sd, si hizo falta tocar el fuente BASIC (nunca el original: +siempre una copia en `examples/sd81/`) o solo cambiar las flags de +compilación. + +Regla de fondo: el fuente oficial de zxbasic **nunca se modifica**. Cuando +un programa depende de una sysvar o dirección absoluta específica del +Spectrum, se hace una copia adaptada junto al original. + +--- + +## 1. `examples/english/comecoquitos.bas` — SIN cambios de fuente + +Compila tal cual. Solo necesita flags de línea de comandos: + +``` +python -m src.zxbc.zxbc comecoquitos.bas --arch zx81sd --string-base 1 --array-base 1 -o comecocos.bin +``` + +**Por qué**: el fuente usa indexación de strings 1-based (estilo Sinclair +BASIC clásico, `l$(f)`, slicing). Con la base 0 por defecto de zxbasic, +las comparaciones de colisión salen desplazadas una posición — no es un +bug del port, en zx48k pasa exactamente igual sin esas flags. + +--- + +## 2. `examples/english/snake_en.bas` → `examples/sd81/snake_sd81.bas` + +Un solo cambio, una línea: + +```diff +-73 POKE UINTEGER 23675, @udg(0, 0): REM Sets UDG variable to first element ++73 POKE UINTEGER $8002, @udg(0, 0): REM sysvar UDG de zx81sd (en Spectrum era 23675) +``` + +**Por qué**: `23675` ($5C7B) es la dirección absoluta del sysvar `UDG` en +el mapa de memoria del **Spectrum**. En zx81sd el mismo sysvar vive en +`$8002` (ver `SYSVAR_BASE+2` en `src/lib/arch/zx81sd/runtime/sysvars.asm`). +Sin el cambio, el POKE cae en RAM libre y los UDGs (cabeza y fruta de la +serpiente) nunca se activarían — el juego funcionaría pero pintaría +espacios en vez de los gráficos. + +**Patrón general**: cualquier ejemplo que haga `POKE`/`PEEK` a una +dirección de sysvar del Spectrum en vez de usar la función de más alto +nivel (aquí habría bastado con no usar UDG a pelo) necesita este tipo de +traducción de dirección. Ver también flights.bas más abajo. + +--- + +## 3. `examples/flights.bas` → `examples/sd81/flights_sd81.bas` + +Tres cambios de contenido (más limpieza cosmética de espacios finales sin +efecto funcional, no listada): + +### 3.1 Eliminar un POKE de sysvar del Spectrum sin equivalente + +```diff +-1 POKE 23658,8: BORDER 1: PAPER 1: INK 7: CLS ++1 BORDER 1: PAPER 1: INK 7: CLS +``` + +**Por qué**: `23658` ($5C6A) es `REPDEL` (retardo de repetición de tecla) +en el Spectrum — no existe ese sysvar en zx81sd, y el POKE escribiría +sobre una dirección de RAM arbitraria. Se elimina sin más: el efecto +(ajustar el auto-repeat del teclado) no tiene equivalente ni falta le +hace al juego. + +### 3.2 Traducir el sysvar COORDS a su dirección real en zx81sd + +```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 +``` +(y tres apariciones más idénticas en las líneas 2370, 2378 y 2445) + +**Por qué**: `23677`/`23678` ($5C7D/$5C7E) son el sysvar `COORDS` +(última coordenada de `PLOT`) del Spectrum. En zx81sd vive en `$8004`/ +`$8005` (`COORDS EQU SYSVAR_BASE+$04` en `sysvars.asm`). Sin la +traducción, los `PEEK` leen RAM del programa compilado en vez de la +coordenada real, y el `DRAW` de la línea del horizonte sale con un +desplazamiento aleatorio dependiente del contenido de esa RAM — este fue +el primer síntoma reportado ("pinta mal la línea del horizonte"). + +### 3.3 Comparaciones de tecla a minúsculas + +```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 +``` +(y las comparaciones de "Y"/"N" en las líneas 6150/6160) + +**Por qué (este cambio corresponde al esquema de teclado antiguo — ver +nota de "ya no es necesario" más abajo)**: en el momento de portar +`flights.bas`, `zx81sd/runtime/io/keyboard/keyscan.asm` (reescaneo a +mano del **teclado físico del ZX81**, 40 teclas, ya que el SD81 Booster +no tiene teclado Spectrum) solo devolvía minúscula siempre, sin importar +si se pulsaba `SHIFT` o no — `SHIFT+letra` no producía la mayúscula de +esa letra, sino uno de los símbolos clásicos del ZX81 (dos puntos, +comillas, `+`, `-`...). Con ese esquema no existía combinación física +que reprodujera "CAPS SHIFT+S" al estilo Spectrum, y la única +adaptación posible era comparar contra minúscula sin shift. Mismo +motivo, ya resuelto antes por otra vía en `comecoquitos.bas` (ahí el +propio fuente ya comparaba en minúscula, "o","p","q","a","y","n" — +coincidencia de que su autor tecleó así, no algo que tuviéramos que +tocar) y en `snake_sd81.bas` (compara "O"/"o" con `OR`, cubriendo ambos +casos sin necesidad de tocarlo). + +**Ya no es necesario (2026-07-04)**: `keyscan.asm` se rediseñó para que +la pulsación directa de una letra dé minúscula y `SHIFT+letra` dé la +MAYÚSCULA de esa letra (además de `SHIFT+"2"` como CAPS LOCK persistente +y los símbolos clásicos del ZX81 alcanzables con `"."+tecla` desde +`INPUT()`). Con el esquema actual, `INKEY$="S"` ya funcionaría de forma +natural pulsando `SHIFT+S`, exactamente como en un Spectrum — el cambio +de caso de `flights_sd81.bas` documentado arriba fue necesario en su +momento pero ya no lo sería si se portara hoy desde cero. Se deja +constancia aquí en vez de revertir la copia ya probada. Detalle completo +del rediseño en [MAP.md](MAP.md), sección "Esquema de teclado nuevo". + +--- + +## 4. `examples/english/4inarow.bas` — SIN cambios de fuente + +Compila con flags por defecto: + +``` +python -m src.zxbc.zxbc 4inarow.bas --arch zx81sd -o row4.bin +``` + +Sus arrays usan índices 1..8 que caben en base 0 por defecto sin +colisión de slicing (no hace slicing de strings), y sus comparaciones de +tecla ya usan minúsculas ("y"/"n" en los prompts). Ejercita el código de +arcos (`DRAW 8,0,PI`) y `CIRCLE` sin necesitar ningún parche. + +--- + +## 5. `examples/scroll.bas` — SIN cambios de fuente + +Compila con flags por defecto: + +``` +python -m src.zxbc.zxbc scroll.bas --arch zx81sd -o scroll.bin +``` + +El problema no estaba en el ejemplo sino en la librería: `scroll.bas` no +tenía versión zx81sd — se usaba tal cual la de zx48k, cuyas 8 subs +(`ScrollRight/Left/Up/Down` + variantes `*Aligned`) llaman a `$22AC` +(rutina *PIXEL-ADD* de la ROM del Spectrum, inexistente en zx81sd). Se +creó `src/lib/arch/zx81sd/stdlib/scroll.bas`, idéntica salvo sustituir +esas 8 llamadas por `call PIXEL_ADDR` (nuestra propia implementación, +mismo contrato de registros que la ROM — ver [MAP.md](MAP.md)). El +ejemplo en sí no necesitó ningún cambio, igual que 4inarow.bas. + +--- + +## 6. `examples/maskedsprites.bas` → `examples/sd81/maskedsprites_sd81.bas` + +**En proceso — aún no funciona del todo bien.** + +Un cambio de fondo: `WaitForNewFrame` (definida en el propio ejemplo, no +en la librería) reescrita para no depender de interrupciones: + +```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 ; garantiza al menos un frame esperado (sustituye 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 +``` + +**Por qué**: `23672` es el contador `FRAMES` de la ROM del Spectrum, +incrementado automáticamente por la interrupción IM1 de 50Hz. El +original hace un `HALT` inicial (espera a la interrupción) y luego un +bucle que confía en que la interrupción lo siga incrementando en segundo +plano mientras el bucle solo *comprueba* sin volver a esperar. En +zx81sd no hay interrupciones (DI permanente; el vector `$0038` es una +trampa `DI;HALT`, no un manejador real) — ese `HALT` no despertaría +nunca. Se sustituye por `VSYNC_TICK` (sondeo del contador de pulsos +VSYNC hardware del SD81 Booster por puerto, la misma rutina que ya usa +`PAUSE`), llamada explícitamente en cada vuelta que haga falta esperar. + +**Actualización — MSFS portado al mapeador de zx81sd (bloque 7)**: el +riesgo de arriba ya no aplica. Se creó +`src/lib/arch/zx81sd/stdlib/cb/maskedsprites.bas` (override completo de +la librería compartida, que sigue intacta): `SetBankPreservingINTs`/ +`GetBankPreservingRegs` reescritas en ASM a mano sobre el puerto `$E7` +(bloque 7, `$E000-$FFFF` — reservado en nuestro mapa de memoria para +"banking de datos, mapas, sprites"), y `MaskedSpritesFileSystemStart` +fija en `$E000` en vez de "lo que quede hasta `$FFFF`" (que asumía RAM +plana Spectrum). El resto de MSFS (cientos de líneas de álgebra de +bloques) es agnóstico de banco/dirección y se copió sin tocar una línea. +Detalle completo, incluido un bug real cazado en el proceso (registros +no preservados si se escriben en BASIC en vez de ASM), en [MAP.md](MAP.md). + +Pese a los fixes de arriba (confirmados con simulación y, en parte, en +emulador/hardware real), la librería en su estado actual **sigue sin +funcionar del todo bien** — trabajo en curso, no dar por cerrado este +ejemplo todavía. + +--- + +## 7. `examples/sd81/pong.bas` — transcripción clásica, un cambio ASM + +No es un ejemplo oficial de `examples/` sino una transcripción clásica +de Pong al estilo Sinclair BASIC (GOSUB/números de línea), añadida +directamente en `examples/sd81/`. Usa comparaciones de tecla ya en +minúscula ("4","q","3","a"), coincidentes con el esquema de teclado +actual sin necesidad de tocar nada — el único cambio fue sustituir la +sincronización de pantalla: + +```diff + ASM +- call VSYNC_TICK ; garantiza al menos un frame esperado (sustituye EI+HALT) ++ call .core.VSYNC_TICK ; garantiza al menos un frame esperado (sustituye EI+HALT) + ; halt ; Avoids screen flickering + END ASM +``` + +**Por qué**: `vsync.asm` envuelve `VSYNC_TICK` en +`push namespace core ... pop namespace`; al llamarlo desde un bloque +`ASM ... END ASM` que no está dentro de ese namespace hace falta el +prefijo completo `.core.VSYNC_TICK` — mismo patrón de error que en +`maskedsprites_sd81.bas` (ver [PRECAUCIONES.md](PRECAUCIONES.md), +sección 5). Sin el prefijo, el compilador da +`Undefined GLOBAL label '.VSYNC_TICK'`. + +--- + +## 8. `examples/sd81/block7test.bas` — sin equivalente oficial, prueba de mapeador + +Programa mínimo (no transcripción de ningún ejemplo) escrito para +confirmar, de forma aislada, que el mapeador de memoria del SD81 Booster +provee almacenamiento independiente por página: escribe patrones +distintos en las páginas 20 y 63 del bloque 7 (`$E000-$FFFF`) vía +`Map()` y verifica que no se pisan entre sí. Sirvió para descartar el +mapeador como causa durante la investigación del bug de MSFS (ver +[MAP.md](MAP.md)). Se conserva como ejemplo de referencia de uso directo +de `Map()`/`MapGet` sobre el bloque 7. + +--- + +## Resumen para el manual + +| Ejemplo | Copia en zx81sd | Cambios de fuente | Flags de compilación | +|---|---|---|---| +| comecoquitos.bas | no hace falta copia si no se toca el original | ninguno | `--string-base 1 --array-base 1` | +| snake_en.bas | `snake_sd81.bas` | 1 línea (dirección UDG) | ninguna especial | +| flights.bas | `flights_sd81.bas` | 3 tipos de cambio (POKE eliminado, 4× PEEK COORDS, 8× case de tecla) | ninguna especial | +| 4inarow.bas | no hace falta copia | ninguno | ninguna especial | +| scroll.bas | no hace falta copia | ninguno (el fix fue en la librería `stdlib/scroll.bas`) | ninguna especial | +| maskedsprites.bas | `maskedsprites_sd81.bas` | `WaitForNewFrame` reescrita (EI+HALT → VSYNC_TICK); librería `cb/maskedsprites.bas` portada al mapeador (bloque 7) — **en proceso, aún no funciona del todo bien** | ninguna especial | +| pong.bas (no oficial) | `pong.bas` en `examples/sd81/` | 1 línea ASM (namespace de VSYNC_TICK) | ninguna especial | +| block7test.bas (no oficial) | `block7test.bas` en `examples/sd81/` | n/a (escrito directamente para zx81sd) | ninguna especial | + +Patrón identificado para futuros ejemplos: + +1. Buscar `POKE`/`PEEK` a literales numéricos: casi siempre es una + sysvar del Spectrum que hay que traducir a su dirección zx81sd + equivalente (ver `src/lib/arch/zx81sd/runtime/sysvars.asm`). +2. Comparaciones de `INKEY$` contra mayúsculas de teclas de control + (`"S"`, `"Q"`, etc.): con el esquema de teclado **actual** + (pulsación directa = minúscula, `SHIFT+letra` = mayúscula) esto ya + funciona igual que en un Spectrum y **no hace falta tocar nada** — + el cambio a minúscula documentado arriba para `flights.bas` + corresponde a una versión anterior de `keyscan.asm` (ver nota "Ya no + es necesario" en la sección 3.3) y se mantiene aquí solo como + registro histórico de esa copia ya generada. +3. Código ASM inline que llame a rutinas envueltas en + `push namespace core` (como `VSYNC_TICK`) necesita el prefijo + `.core.` si el bloque `ASM` que lo invoca no está ya dentro de ese + namespace (ver caso de `pong.bas` arriba). diff --git a/docs/zx81sd/MAP.md b/docs/zx81sd/MAP.md new file mode 100644 index 000000000..a6eec0504 --- /dev/null +++ b/docs/zx81sd/MAP.md @@ -0,0 +1,529 @@ +# Mapa de tests de depuración (sesión DRAW3 / arco) + +Nota sobre rutas: esta bitácora se escribió originalmente en el +repositorio complementario de pruebas del port (donde los `.bas` citados +vivían todos en un directorio `tests_debug/`). Los ejemplos que se +consideraron suficientemente maduros para publicarse ya están copiados +en este repositorio, en [`examples/sd81/`](../../examples/sd81/) +(`flights_sd81.bas`, `snake_sd81.bas`, `maskedsprites_sd81.bas`, +`pong.bas`, `block7test.bas` — ver [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md)). +El resto de fuentes de depuración puntual mencionadas aquí (`diag1-6`, +`t_arc*`, `trig_test`, `heaptest`, `keytest`...) siguen solo en el +repositorio complementario, no en este. + +Compilación de cada uno (desde la raíz de este repositorio): +``` +python -m src.zxbc.zxbc examples\sd81\.bas --arch zx81sd -o .bin +python split_sd81.py .bin +``` +(`split_sd81.py` vive en el repositorio complementario — ver [USO.md](USO.md).) + +| Fuente | Prefijo SD81 | Qué prueba | Resultado obtenido | +|------------------------|--------------|---------------------------------------------------------------------|---------------------| +| `str_test.bas` | STRTEST | PRINT/STR$ de FLOAT (Fase 3) | OK: 7 / 7.5 / -3.25 / 0 / 123.5 / -0.5 | +| `trig_test.bas` | TRIGTST | SQR/SIN/COS/EXP/LN/ATN con argumentos triviales (Fase 4) | OK: 3 / 1.41421 / 0 / 1 / 2.71828 / 1 / 3.14159 | +| `trig2_test.bas` | TRIG2 | SIN/COS con argumentos NO triviales (1, 1.5708, 3.14159) | OK: 0.8147(4?) / 0.5403 / 0.99999 / -0.99999 / 0 | +| `draw_arc_test.bas` | DRAWARC | DRAW+DRAW+CIRCLE combinados (primer intento de arco) | Solo aparece una línea vertical, CIRCLE nunca se ve | +| `t_line.bas` | TLINE | `DRAW 20,0` (2 args, sin ángulo, sin FP) | OK: línea horizontal | +| `t_circle.bas` | TCIRC | `CIRCLE 60,30,20` sola | OK: círculo correcto | +| `t_arc.bas` | TARC | `DRAW 20,0,3.14159` (arco solo, offset horizontal, 180°) | BUG: línea vertical (no horizontal, no arco) | +| `t_arc2.bas` | TARC2 | `DRAW 30,10,1.5708` (offsets asimétricos, 90°) | BUG: línea vertical con leve inclinación a la derecha | +| `t_arc3.bas` | TARC3 | `PLOT 100,96` + `DRAW 20,0,3.14159` (centrado, con margen) | BUG: patrón de varias líneas en estrella/caóticas desde el centro; una se sale de pantalla hacia el área de atributos | +| `diag1.bas` | DIAG1 | ASM inline: llama a CD-PRMS1 (L247D) directamente con z=40, A=pi/2, e imprime mem-1/mem-3/mem-4/mem-0/nº de líneas | Pantalla en blanco — bug del propio test (`#include` dentro de `ASM` mete código ejecutable en el flujo lineal). Sustituido por diag2 | +| `diag2.bas` | DIAG2 | Igual que diag1 pero sin includes manuales: variables BASIC + PRINT normal | OK: valores exactos esperados → CD-PRMS1 numéricamente correcto | +| `diag4.bas` | DIAG4 | Con `-D DRAW3_DEBUG`: imprime solo N (nº de segmentos capturados en el hook de draw3.asm) | OK: 16 → el hook de captura funciona | +| `diag5.bas` | DIAG5 | diag4 + bucle de recorrido del buffer (2 páginas SD81) | No imprimía; descartado, sustituido por diag6 (1 página) | +| `diag6.bas` | DIAG6/DIAG7 | diag4 + bucle, 1 página. Su volcado de $152D + breakpoints de escritura en $8004 localizaron el bug | OK: 16/16. DIAG7 = idéntico recompilado tras el fix | +| `arcfix.bas` | ARCFIX | Test de referencia idéntico al Spectrum real: `PLOT 100,100` + 2 DRAW arco + CIRCLE | **OK tras el fix**: gancho + círculo, igual que la foto del Spectrum real | + +## Bug de DRAW3 (arco) — RESUELTO + +**Causa raíz** (nada que ver con el calculador FP, cuya matemática resultó +ser exacta): `src/lib/arch/zx81sd/runtime/pixel_addr.asm` destruía el +registro **D** (lo usaba como scratch para V=191−Y). Pero `draw.asm` +(heredado de zx48k) salva la coordenada Y del Bresenham en D' alrededor de +la llamada (`ld d,b / call PIXEL_ADDR / ld b,d`), porque PIXEL-ADD ($22AC) +de la ROM Spectrum preserva DE. Resultado: cada línea con componente +vertical arrancaba internamente con Y=191−y1, corrompiendo COORDS y el +trazado. Las líneas horizontales (t_line) y CIRCLE (que no pasa por ese +camino) salían bien, lo que despistó la investigación inicial. + +**Fix**: PIXEL_ADDR reescrito para usar B como scratch (ya se destruía, +igual que en la ROM) y preservar D y E. Verificado en hardware con ARCFIX +(idéntico al resultado del Spectrum real) y DIAG7. + +**Método de localización**: hook `#ifdef DRAW3_DEBUG` en draw3.asm (activado +con `-D DRAW3_DEBUG`) que captura por cada segmento: |Dy|,|Dx|,signos y +COORDS previas en un buffer (`DRAW3_DEBUG_BUF`); volcado de memoria del +buffer en el debugger de EightyOne + breakpoints de escritura en COORDS +($8004/$8005). El volcado demostró que la posición FP acumulada era +perfecta y que COORDS quedaba mal tras cada línea → el bug estaba en +__DRAW/PIXEL_ADDR, no en fp_calc.asm ni draw3.asm. + +## Ficheros fuente modificados/creados en esta sesión (motor FP + arco) + +- `src/lib/arch/zx81sd/runtime/fp_calc.asm` — Fases 1-5 (motor CALCULATE, + trig/log/exp/sqrt, y ahora STK-TO-A/STK-TO-BC/CD-PRMS1 para el arco). + También se le añadió `#include once ` (bug independiente: + se incluye siempre en todo binario zx81sd, así que debía bastarse a + sí mismo). +- `src/lib/arch/zx81sd/runtime/draw3.asm` — NUEVO. Override de + `zx48k/runtime/draw3.asm` que sustituye las llamadas a direcciones + ROM fijas por las rutinas portadas en `fp_calc.asm`. Incluye + instrumentación de traza tras `#ifdef DRAW3_DEBUG` (inactiva por + defecto; se activa compilando con `-D DRAW3_DEBUG`). +- `src/lib/arch/zx81sd/runtime/pixel_addr.asm` — FIX del bug del arco: + ahora preserva D y E (antes destruía D, rompiendo el Bresenham de + draw.asm en toda línea no horizontal). +- `src/lib/arch/zx81sd/runtime/fp_tostr.asm`, `printf.asm`, `str.asm` — + de la Fase 3 (PRINT/STR$ de FLOAT), sin cambios en esta sesión. + +## Sonido: BEEP y PLAY (chips AY ZonX del SD81) + +| Fuente | Prefijo SD81 | Qué prueba | Resultado | +|-----------------|--------------|----------------------------------------------------------|-----------| +| `beeptest.bas` | BEEPTS2 | BEEP variable (runtime FP) + BEEP constante (corrección de reloj 13/14 en __BEEPER) | OK: escala + DO/DO' + LA 440 | +| `playtest.bas` | PLAYTS2 | PLAY 3 canales + comparación AY/beeper | OK (¡ojo: notas en minúscula = octava abajo!) | +| `aycal.bas` | AYCAL | Emparejamiento AY vs beeper por semitonos | Sirvió para detectar el desfase | +| `aycal2.bas` | AYCAL2 | Duraciones cronometrables + pareja directa | FFT: beeper 434.5 (correcto, pacing emulador), AY 220 | +| `aycal3.bas` | AYCAL3 | Igual con notas en MAYÚSCULA | OK: unísono 440/440 | + +Lección de la investigación de la "octava fantasma": en el MML de PLAY +(semántica del BASIC 128K), las notas en MINÚSCULA suenan una octava por +debajo de la octava actual. Los tests iniciales usaban minúsculas y el AY +sonaba a 220 Hz *por diseño*. El emulador, la tabla de divisores +(1.625 MHz) y el beeper (3.25 MHz) eran correctos. Se verificó con FFT +sobre la salida de audio grabada. De regalo se corrigió un bug latente +real de EightyOne (el reloj del AY quedaba con el de la tarjeta del +diálogo de hardware en vez del ZonX forzado por el SD81: faltaba llamar a +Sound.InitDevices() tras forzar machine.aytype). + +## Librería MCU (SD81 Booster) — `zx81sd/stdlib/mcu.bas` + `joy.bas` + +| Fuente | Prefijo SD81 | Qué prueba | Resultado | +|----------------|--------------|------------------------------------------------------------|-----------| +| `joytest.bas` | JOYTS2 | `Joy("QAOPM")` (cmd 21) + validación local + eco INKEY$ | OK | +| `mcutest.bas` | MCUTST | VERSION, GET/SETBYTE, PWD, SAVE+LOAD+verificación, DEL, FREE, RTC, BAT, DIR, AY2 por registros, AyPlay | pendiente | +| `maptest.bas` | MAPTST | `Map(bloque,pagina)`/`MapGet` — mapeador $E7: firmas en 2 páginas conmutando el bloque 5 y verificación | OK | +| `exttest.bas` | EXTTST | Extensiones no-MCU: `HexPoke` (*HEX), `MemMove` (*LDIR/*LDDR, stdlib), `StrInv`/`StrBold` (*INV/*BOLD) | OK | +| `ftest.bas` | FTEST | Handles F_*: SAVE, F_OPEN_ZX81 (cmd 58), F_SEEK, F_READ con verificación, F_WRITE+relectura, F_CLOSE, DEL | OK | +| `lstest.bas` | LSTEST | Statements LOAD/SAVE/VERIFY ... CODE nativos → SD (override runtime load.asm/save.asm, cmd 9/10): SAVE+LOAD+verificación, VERIFY ok/corrupto (ERR 26), fichero inexistente (ERR 26) | OK | + +Arquitectura: `mcu.bas` contiene las primitivas del protocolo en ASM +(McuSend/McuRecv/McuSendBlock/McuRecvBlock — las *Block son el camino +crítico de LOAD/SAVE/F_READ/F_WRITE) y los wrappers de todos los +comandos del manual (sistema, ficheros, handles F_*, hardware, voz, +AY2/VGM/PEG, RTC/BAT). Conversión ASCII↔ZX81 automática en los comandos +de texto. `joy.bas` es una capa fina sobre `mcu.bas`. + +Notas de protocolo (extraídas de SD81Booster.cpp del emulador): +- Tras CADA operación en $A7 se espera el cambio del bit 7 de $AF. + NUNCA escribir en $AF (reset del MCU). +- Strings Z80→MCU: byte de longitud + datos (el MCU convierte ZX81→ASCII + salvo comandos "raw": JOY, BINARY_SAY, F_OPEN). +- Streams MCU→Z80 (PWD/DIR/TYPE/FREE_TXT): pedir cada carácter + escribiendo CMD_NEXTCH ($0D); fin = EOT ($6F); después llega el status. +- Respuestas de longitud fija (LOAD, FREE, RTC, BAT, F_READ): ráfaga + de bytes leyendo $A7 con espera de reloj entre cada uno. +- F_OPEN: confirmado en el firmware (COMMANDS.cpp) que el MCU asigna el + handle y lo devuelve (el manual estaba mal y se ha corregido). Añadido + F_OPEN_ZX81 (58) a la librería y al emulador. +- OPENDIR/GETROWLEN/GETROW (16-18) no están emulados en EightyOne: + probarlos solo en hardware real. + +## Test de integración: comecoquitos.bas (ejemplo oficial de zxbasic) + +| Fuente | Prefijo SD81 | Qué prueba | Resultado | +|---|---|---|---| +| `examples/english/comecoquitos.bas` | COMECO | Juego completo de 1985: FP, strings/slices, arrays, UDGs, bloques gráficos, color/FLASH/BRIGHT, INKEY$, BEEP, RND | OK — idéntico al .tap de Spectrum | +| `fpleak.bas` | FPLEAK | Detector de fugas de la pila FP por bloques (lee $8024 tras cada idiom) | Sirvió para acotar; la "fuga" era corrupción por UDGs | +| `blocktest.bas` | BLKTST | Los 16 gráficos de bloque CHR$(128)-143 | OK tras el fix de PO_GR_1 | + +Tres bugs del runtime cazados con este juego (commits 9903c866 y 9a24059e): +1. UDG apuntaba 128 bytes más allá del final de la fuente (96 chars, no + 256): los POKE USR CHR$ machacaban código del runtime → cuelgue en el + primer uso del calculador. Fix: área dedicada de 21 UDGs. +2. INKEY$ devolvía mayúsculas; el modo L del Spectrum (y los programas de + la época) usan minúsculas. Fix: tabla de keyscan en minúscula. +3. PO_GR_1 (bloques CHR$(128)-143) generaba patrones corruptos (OR al + registro equivocado + cuadrantes izq/der invertidos). Fix: algoritmo + literal de la ROM. + +RECETA para ejemplos clásicos transcritos de Sinclair BASIC: compilar con +`--string-base 1 --array-base 1` (indexación 1-based). Sin ello, las +colisiones por slicing de strings salen desplazadas una posición (no es +un bug del port: en zx48k pasa igual). + +## Heap en $8100 + traps de cinta de EightyOne — RESUELTO (2026-07-04) + +| Fuente | Prefijo SD81 | Qué prueba | Resultado | +|---|---|---|---| +| `tests_debug/heaptest.bas` | HEAPA..HEAPE | Gestor de memoria dinámica (3 fases: crecimiento char a char, REALLOC grandes, STR$ en bucle) con el heap en distintas direcciones | Bisección que aisló el bug | +| `tests_debug/memtest.bas` | MEMTST | R/W patrón dependiente de dirección en $8100-$BFFF (2 pasadas) | OK — descartó el hardware/paginación | +| `tests_debug/inputtest.bas` | INTEST | INPUT() mínimo aislado | Reproducía el cuelgue | +| `examples/sd81/flights_sd81.bas` | FLIGHT | Simulador de vuelo: PEEK COORDS ($8004/5), FP intensivo, INPUT | Adaptación de examples/flights.bas | + +Dos bugs encadenados, cazados el 2026-07-04: + +1. **Compilador (`src/arch/zx81sd/backend/main.py`)**: `heap_size`/`heap_address` + se registraban con `ADD_IF_NOT_DEFINED`, pero el backend Z80 genérico ya + las define antes (4768 / None) → los valores zx81sd ($8100 / 16127) nunca + se aplicaban y el heap acababa inline (DEFS) dentro de la zona ejecutable, + desperdiciando 4768 bytes y limitando el heap. Fix: asignación directa + `OPTIONS.heap_size/heap_address` (la CLI puede seguir sobreescribiendo). + +2. **Emulador (`Eightyone2/src/ZX81/rompatch.cpp`, `PatchTest`)**: los traps + de cinta de la ROM ZX81 ($0207/$02FF/$031E/$0356) se disparaban comparando + PC + `memory[pc]` **plano**, que conserva la ROM aunque el SD81 tenga RAM + mapeada. Con el heap en EQU el runtime baja $12A0 bytes y la división + __DIVU16_FAST aterrizaba en $02FF → el trap de SAVE hacía `DE=1` en mitad + de la división → cociente basura estable → bucle infinito de dígitos en + __PRINTU_LOOP (PUSH AF sin pop) → la pila descendía arrasando el runtime. + Los builds con heap inline eran inmunes de casualidad: las 4 direcciones + trampa caían dentro del bloque DEFS (datos, el PC nunca pasa por ahí). + Fix: `PatchTest` lee el byte con `zx81_PatchPeek()` (mapper-aware). + En hardware real este bug NO existe (no hay traps). + +Metodología que lo resolvió: simulación determinista del binario con la +librería Python `z80` (pip install z80) — diff de integridad de la zona de +código tras cada tramo + breakpoints comparando registros con EightyOne. +El binario era correcto en Z80 puro → la divergencia estaba en el emulador. +Arnés en el scratchpad de la sesión (runsim*.py, reproducible). + +## Scroll de PRINT saltaba a la ROM del Spectrum — RESUELTO (2026-07-04) + +Tercer bug de la cadena de flights.bas (viento=10, dir=100 → HALT): el +`print.asm` de zx81sd conservaba del zx48k el fallback +`__SCROLL_SCR EQU 0DFEh` (rutina CL-SC-ALL de la ROM del Spectrum). En +zx81sd no hay ROM: el primer PRINT que desbordaba la pantalla hacía CALL +a la línea BASIC compilada que casualmente ocupara $0DFE → ejecución +salvaje → RETURN de gosub sacando basura → HALT. Dependía de la entrada +porque el nº de dígitos tecleados movía el cursor: con viento "1"/"0" el +texto no llegaba a desbordar; con "10"/"100" sí. Ningún test anterior lo +pilló porque todos usan PRINT AT (nunca scroll). + +Fix: la implementación por búfer (`__ZXB_ENABLE_BUFFER_SCROLL`, scrollea +vía SCREEN_ADDR/SCREEN_ATTR_ADDR) es ahora la rama única de __SCROLL_SCR +en zx81sd/runtime/print.asm. Verificado con el simulador Python + teclado +scriptado: el bucle principal del juego mantiene SP estable durante miles +de pasos. Ojo futuro: revisar cualquier otro EQU/CALL a direcciones +absolutas de ROM Spectrum al portar ficheros del zx48k (grep hecho: +no queda ninguno en el runtime zx81sd). + +## Esquema de teclado nuevo: mayúsculas, CAPS LOCK y símbolos — 2026-07-04 + +| Fuente | Prefijo SD81 | Qué prueba | Resultado | +|---|---|---|---| +| `tests_debug/keytest.bas` | KEYTST | INKEY$ interactivo: imprime código ASCII + carácter de cada tecla | Verificado por simulación exhaustiva (ver abajo); pendiente de probar a mano en el emulador/hardware | + +El teclado físico del ZX81 no distingue mayúscula/minúscula por tecla +(ver [[zx81sd-keyboard-case]]): SHIFT+letra da un símbolo, no la +mayúscula de esa letra. Hasta ahora `keyscan.asm` solo devolvía +minúsculas siempre. Nuevo esquema (`src/lib/arch/zx81sd/runtime/io/ +keyboard/keyscan.asm`, reescrito de raíz): + +- Sin modificador: minúscula (igual que antes — comecoquitos, snake, + flights, row4 siguen funcionando sin tocar una línea, porque ninguno + usa SHIFT). +- `SHIFT + letra`: MAYÚSCULA de esa letra. +- `SHIFT + "2"`: conmuta CAPS LOCK persistente (mudo, no imprime nada). +- CAPS LOCK activo: minúscula pasa a mayúscula; SHIFT sigue dando + mayúscula igual (es un OR, no hay interacción, decisión tomada con el + usuario). +- `"."` sola: `.` +- `SHIFT + "."`: `,` (igual que en el ZX81 real). +- `"." + otra tecla`: el símbolo impreso en el teclado del ZX81 para esa + tecla (`:` con Z, `)` con O, RUBOUT con 0, etc. — la vieja tabla SHIFT + del ZX81, ahora alcanzable con "." en vez de con SHIFT, ya que SHIFT + se ha redefinido para dar mayúsculas). + +Requirió reescribir el escaneo: antes se paraba en la primera fila con +algo pulsado (bastaba con una tecla a la vez). Ahora hacen falta dos +lecturas de puerto dedicadas para SHIFT (fila 0) y "." (fila 7, columna +1), más un escaneo de las demás filas buscando una tercera tecla +excluyendo esas dos posiciones (rutina `FIND_OTHER`). El combo +`SHIFT+"2"` usa un byte de estado persistente con detección de flanco +para no conmutar varias veces mientras se mantiene pulsado. + +Verificado con un arnés de simulación Python (`z80`, ver metodología ya +usada para los bugs del heap): se llama a `__ZX81SD_KEYSCAN` directamente +inyectando por el callback de E/S los bits de fila exactos de cada +combinación (Z sola, SHIFT+Z, "."+Z, SHIFT+2 mantenido 4 polls seguidos, +etc.), sin pasar por la complejidad de un teclado real. Los offsets de +las tablas y del estado (`_KBD_*`, todos LOCAL al PROC, no aparecen en el +`.map`) se localizaron buscando el patrón de bytes de `UNSHIFT_TABLE` +("zxcvasdfg") en el binario y calculando el resto por desplazamiento fijo +(cada tabla ocupa 39 bytes). Los 12 casos de la tabla de diseño +coincidieron exactamente, incluido el debounce del CAPS LOCK. + +### Corrección 2026-07-04: el modificador "." se movió de keyscan a input.bas + +Al probarlo en el emulador, el usuario detectó que `"."+tecla` (pensado +para dar el símbolo del ZX81 pulsando ambas a la vez, como SHIFT+letra) +era impracticable desde `INPUT`: `PRIVATEInputWaitKey` compromete la +tecla `"."` en cuanto la detecta sola, sin dar tiempo a que la segunda +tecla llegue de verdad a la vez (a diferencia de SHIFT+letra, que sí se +puede sostener cómodamente con la otra mano). Diagnóstico correcto del +usuario: *"la gestión de los símbolos no debe ir en el keyscan sino en +INPUT.bas"*. + +Fix: `keyscan.asm` ahora trata `"."` como una tecla más — sin +modificador da `.`, con SHIFT da `,` (igual que el ZX81 real), sin +ninguna lógica de "tercera tecla" para el punto (se quitó por completo +la exclusión de la fila 7 en `FIND_OTHER`, ya no hace falta). Las tablas +`UNSHIFT_TABLE`/`SYMBOL_TABLE`/`CAPS_TABLE` se promovieron de `LOCAL` a +ámbito de fichero (prefijo `__ZX81SD_`) y se añadió una rutina nueva, +`__ZX81SD_SYMBOL_FOR(char)`, que hace la búsqueda inversa +UNSHIFT_TABLE→SYMBOL_TABLE dado un carácter ya decodificado. + +La composición de símbolos ahora vive en `stdlib/input.bas` como una +"tecla muerta" secuencial: al leer `"."`, la función `input()` lee la +SIGUIENTE tecla por separado (sin exigir simultaneidad) y llama a +`PRIVATEInputSymbolFor()`; si hay símbolo, lo añade; si no, añade el +punto literal y procesa la segunda tecla con normalidad (DEL, ENTER, o +un carácter más). + +Bug propio cazado durante la implementación (antes de que el usuario lo +viera): `"."` + `"0"` resuelve a RUBOUT (12) vía `SYMBOL_TABLE` (es el +símbolo real que el ZX81 imprime sobre la tecla "0"), lo que borraría el +carácter anterior al escribir cualquier decimal terminado en ".0" (muy +habitual: "3.0", "10.0"...). Se excluyó ese valor explícitamente en +`input.bas` — RUBOUT ya se alcanza sin ambigüedad con SHIFT+0 (sí es +cómodo de sostener a la vez). Verificado con simulación de programa +completo (tecleo scriptado "1",".","0",ENTER → `a$="1.0"` correctamente, +y "."," o",ENTER → `a$=")"`), no solo con la función aislada. + +`tests_debug/keytest.bas` (KEYTST) sigue siendo el tester interactivo de +`INKEY$`; `tests_debug/inputtest.bas` (INTEST) es el mismo mini test de +`INPUT()` de antes, ahora también sirve para probar la composición de +símbolos a mano. + +### Refinamiento 2026-07-04: punto dos veces seguidas = punto literal + +Con el diseño anterior, `"."` + una letra con símbolo asociado (p.ej. Z → +`:`) siempre resolvía a ese símbolo — no había forma de escribir +literalmente un punto seguido de esa letra. Pedido del usuario: pulsar +`"."` dos veces seguidas debe confirmar el primer punto como literal +(la coma redundante que antes salía de `"."+"."`, vía `SYMBOL_TABLE`, ya +no hace falta — sale directamente y sin ambigüedad con `SHIFT+"."`), y la +tecla que venga después se lee como una pulsación nueva sin combinar. +Así, para escribir ".Z" se teclea "." "." "Z". + +Implementado en `input.bas`: si la segunda tecla leída tras un "." es +también un ".", se añade el punto, se descarta la segunda pulsación (no +imprime nada por sí misma, `LastK=0`) y el bucle vuelve a leer una tecla +fresca. Verificado con la misma simulación de programa completo: +`"."+"."` → `a$="."`; `"."+"."+"z"` → `a$=".z"` (sin formar el símbolo +`:`). + +## scroll.bas — RESUELTO 2026-07-04 (librería nueva, ejemplo sin cambios) + +| Fuente | Prefijo SD81 | Qué prueba | Resultado | +|---|---|---|---| +| `examples/scroll.bas` | SCROLL | Los 4 scrolls pixel-a-pixel (Right/Down/Left/Up) sobre una ventana de 60×60 px, 30 vueltas | Simulado 1200M ticks sin HALT/RST38; pendiente de ver en el emulador (el ejemplo hace 1920 scrolls de hasta 100×100 px, tarda un rato) | + +`src/lib/arch/zx48k/stdlib/scroll.bas` no tenía override en zx81sd — se +usaba la versión de zx48k tal cual, y las 8 subs (`ScrollRight/Left/Up/ +Down` + sus variantes `*Aligned`) llaman todas a `call 22ACh`, la rutina +*PIXEL-ADD* de la ROM del Spectrum. En zx81sd no hay ROM mapeada: esa +dirección cae en pleno código compilado del programa, y el HALT +reportado (`RST 38` en la traza) era justo el байте que hubiera ahí por +casualidad. + +Fix: `src/lib/arch/zx81sd/stdlib/scroll.bas`, copia idéntica salvo las 8 +llamadas a `$22AC` sustituidas por `call PIXEL_ADDR` (nuestra propia +rutina, `runtime/pixel_addr.asm`, ya usada por `plot.asm`/`draw.asm` — +ver [[zx81sd-pixel-addr-contract]]). El contrato de registros es +IDÉNTICO al de la ROM (A=191, B=Y, C=X → HL=offset, A=X AND 7, destruye +B, preserva D/E), así que no hizo falta tocar ni una línea del cuerpo de +los bucles de scroll, solo el punto de llamada. `SP.PixelDown`/ +`SP.PixelUp` (de zx48k/runtime/SP/) no necesitaron copia: son aritmética +pura sobre `SCREEN_ADDR`, sin ROM, y ya funcionaban igual en zx81sd (se +resuelven por el mecanismo normal de fallback a zx48k cuando no hay +override). + +`examples/scroll.bas` no necesitó ningún cambio de fuente — como +`4inarow.bas`, el problema era enteramente de la librería, no del +programa. Añadido a `CAMBIOS_BASIC.md` con esa misma nota. + +## maskedsprites.bas — RESUELTO 2026-07-04 (cambio de fuente, no de librería) + +| Fuente | Prefijo SD81 | Qué prueba | Resultado | +|---|---|---|---| +| `examples/maskedsprites.bas` → `examples/sd81/maskedsprites_sd81.bas` | MASKED | Sprites enmascarados (AND+OR) con MSFS, 10 sprites animados | Simulado 1000M ticks sin HALT/RST38/escritura ilegal; PC avanza por un rango amplio de direcciones (no atascado) | + +A diferencia de `scroll.bas`, aquí el problema SÍ estaba en el propio +ejemplo (`WaitForNewFrame`, definida directamente en `examples/ +maskedsprites.bas`, no en la librería `cb/maskedsprites.bas`): hace +`EI` + `HALT` esperando la interrupción IM1 de 50Hz de la ROM del +Spectrum, comparando contra el contador `FRAMES` de la ROM en la +dirección absoluta `23672`. En zx81sd las interrupciones están +permanentemente deshabilitadas (todo el runtime corre con `DI`; el +vector `$0038` es solo una trampa `DI;HALT`, no un manejador real) — ese +`HALT` no despierta nunca. Confirmado con la traza: el simulador se +quedaba con `m.halted=True` exactamente en el `HALT` de `WaitForNewFrame` +tras ~31M ticks. + +Fix en `examples/sd81/maskedsprites_sd81.bas`: `WaitForNewFrame` reescrita +para usar `VSYNC_TICK` (`runtime/vsync.asm`, ya usada por `PAUSE`) en vez +de `EI+HALT` — sondea por puerto ($AFh) el contador de pulsos VSYNC +hardware del SD81 Booster, sin depender de interrupciones. El algoritmo +original hacía UN `HALT` inicial y luego un bucle que comprobaba +`FRAMES` SIN esperar de nuevo (confiaba en que la interrupción lo +siguiera incrementando en segundo plano); como en zx81sd nada lo +incrementa solo, el bucle llama a `VSYNC_TICK` explícitamente en cada +vuelta que le falte. `GetInterruptStatusInBorder` se dejó intacta (no se +llama nunca en el bucle principal, solo aparece comentada — se mantiene +únicamente para que la comprobación de compilación del final del fichero +no falle por "función no usada"). + +### Actualización 2026-07-04: MSFS portado de verdad al mapeador (bloque 7) + +El riesgo de `$5B5C`/`$7FFD` de arriba **ya no aplica**: a petición del +usuario ("¿por qué no usamos el bloque 7 que tenemos para bancos?") se +creó `src/lib/arch/zx81sd/stdlib/cb/maskedsprites.bas`, override +completo de la librería compartida (que sigue intacta, regla de +Boriel). Diseño (ver también `CAMBIOS_BASIC.md`): + +- **Hallazgo clave**: las funciones de MSFS (`RegisterSpriteImageInMSFS`, + `FindFirstUnusedBlockInMSFS`, etc.) son agnósticas de banco/dirección + — solo llaman a `GetBankPreservingRegs`/`SetBankPreservingINTs` y + leen/escriben la variable BASIC `MaskedSpritesFileSystemStart`. + Reescribiendo esas dos primitivas (usando el puerto `$E7` sobre el + **bloque 7**, `$E000-$FFFF` — reservado en nuestro mapa de memoria + justo para "banking de datos, mapas, sprites") y el cálculo de esa + dirección en `InitMaskedSpritesFileSystem()` (fija en `$E000` en vez + de "lo que quede hasta `$FFFF`", que asumía RAM plana Spectrum), el + resto del fichero (cientos de líneas de álgebra de bloques/bitmap) se + copió literalmente sin tocar una línea. +- `CheckMemoryPaging()` devuelve `0` (honesto: zx81sd no tiene doble + pantalla visible al estilo banco 5/7 del Spectrum) sin afectar a + MSFS, porque las funciones de MSFS no consultan esa función para + decidir si usar el banco — lo hacen incondicionalmente. +- `SetVisibleScreen`/`GetVisibleScreen`/`ToggleVisibleScreen`/ + `CopyScreen5ToScreen7`/`CopyScreen7ToScreen5`/`SetDrawingScreen5`/ + `SetDrawingScreen7`/`ToggleDrawingScreen` → stubs seguros (doble + buffer de pantalla real no está cubierto; código muerto en este + ejemplo dado que `memoryPaging=0`, pero ya no tocan `$5B5C`/`$7FFD` + por si alguien los llama directamente en el futuro). + +**Bug real encontrado durante la implementación** (no por el usuario, +cazado con el propio arnés de simulación): mi primer intento escribió +`SetBankPreservingINTs`/`GetBankPreservingRegs` en BASIC plano en vez de +ASM a mano. Rompía el contrato de registros documentado en el propio +fichero original ("Preserves: D, E, H, L") que el código ASM de +`RegisterSpriteImageInMSFS` y compañía da por hecho (por ejemplo, para +no perder `spriteImageAddr`, que llega en HL) — una función BASIC +compilada usa registros libremente por dentro sin ninguna garantía de +preservarlos. Resultado: los 6 sprites de prueba se registraban todos en +la MISMA dirección (`$0C07`) en vez de direcciones distintas. Se +reescribieron ambas primitivas en ASM a mano, con el mismo contrato +exacto que el original (solo tocan A, B, C). + +Verificado con simulación: las 6 direcciones de registro (`regHero0`, +`regFoe00`, `regFoe20-23`) salen correlativas cada 96 bytes exactos +(`$E010, $E070, $E0D0, $E130, $E190, $E1F0`, coincidiendo con +`$E000+n*96+16`), sin disparar `STOP`, y el bucle principal +(`WaitForNewFrame`) se alcanza repetidamente sin cuelgue tras 1000 +millones de ticks de simulación sin `HALT`/escritura ilegal. + +Limitación conocida del simulador Python usado en esta sesión: no +modela el mapeador de memoria (los `OUT` a `$E7` son no-op en la +simulación, toda la RAM se trata como plana) — no puede validar que el +intercambio de página *físico* funcione de verdad, solo que la lógica +Z80 es correcta asumiendo que sí. La validación definitiva es en el +emulador/hardware real. + +### Segunda vuelta 2026-07-04: seguía yéndose a HALT en hardware real + +Con el fix de arriba ya compilado, en el emulador real seguía disparando +`__STOP` (mismo síntoma: `RegisterSpriteImageInMSFS` devuelve 0). Traza +confirmó que el problema estaba en `SetBankPreservingINTs`, que hacía el +`OUT` al puerto `$E7` a mano en vez de llamar a `Map()` (mcu.bas): +escribía `A=7` (bloque, sin combinar con la página) con `B=página`. Mi +hipótesis inicial fue que el hardware podía estar en modo simple (donde +el byte de datos debe llevar página Y bloque combinados, +`(página AND 31)<<3 | bloque`, y con solo `A=7` se interpretaría como +página=0) — **el usuario corrigió esto**: el cargador SD81 deja el +mapeador en modo completo desde la línea `LOAD *MAP 7,63`, hasta el +siguiente reset, así que esa explicación concreta no cuadra (en modo +completo solo importan los 3 bits bajos de A, iguales en ambas +versiones). La causa exacta seguía sin confirmarse en el momento de +escribir esto. + +Se cambió `SetBankPreservingINTs` para llamar a `Map()` (código ya +probado en otros contextos) en vez de repetir la lógica del puerto a +mano, preservando D,E,H,L alrededor de la llamada con `push`/`pop` +manuales (`Map()` en sí no preserva nada). Tuvo un efecto secundario: +`Map()` dejó de estar referenciada desde BASIC en ningún sitio (solo +desde ASM a mano), y el eliminador de código muerto del compilador la +quitó del binario → `Undefined GLOBAL label '._Map'`. Se resolvió con +una llamada BASIC explícita y redundante a `Map(7, MaskedSprites_MSFS_Page)` +dentro de `InitMaskedSpritesFileSystem()` (comentada como tal — el +análisis de uso del compilador no cuenta las llamadas hechas desde ASM). + +Verificado de nuevo en simulación (mismas 6 direcciones correlativas, +sin `STOP`) — pero siguió fallando en el emulador real. El cambio a +`Map()` era, como señaló el usuario, un no-op ("el Map de la biblioteca +hace exactamente lo mismo que el out"). + +### Causa raíz REAL (tercera vuelta, 2026-07-04): el FSB nunca se inicializa + +La traza del usuario mostraba `FindFirstUnusedBlockInMSFS` recorriendo +el free-space bitmap COMPLETO (bucle FIND-INT, 254 líneas de RRCA/DEC E) +y saliendo por la rama `full` (`SCF/RET` → `JR C` → `LD HL,0`): **todos +los bloques aparecían como "ocupados"**. `__EQ16` (el sospechoso +inicial) funcionaba perfectamente — `regHero0` realmente era 0. + +Causa: **ni nuestra versión ni la original de zx48k limpian jamás el +FSB** (los bytes del bitmap en `start+2..start+1+l`). En el Spectrum no +hace falta: el test de RAM de la ROM deja toda la memoria a cero en el +arranque, así que el bitmap nace "todo libre" gratis. En zx81sd la +página del bloque 7 llega con basura de fábrica → todos los bits a 1 → +"sin bloques libres" → `RegisterSpriteImageInMSFS` devuelve 0 → `STOP`. + +**Por qué el simulador Python dio falso OK dos veces**: su RAM también +nace a ceros, igual que la del Spectrum tras el test de ROM — +exactamente la condición que oculta el bug. Lección de metodología +incorporada al arnés: para validar código que lee memoria no +inicializada, rellenar toda la RAM no cargada con basura (`$FF`) antes +de simular. Con RAM sucia, el binario sin fix reproduce el `STOP` (el +modo de fallo de la traza) y el binario con fix pasa completo (6 +registros correctos, bucle principal alcanzado). + +Fix (3 líneas + comentario en `InitMaskedSpritesFileSystem`): bucle +`FOR j = start+2 TO start+1+l: poke j,0: NEXT` tras calcular el tamaño +del FSB. + +También se verificó por el camino, con `examples/sd81/block7test.bas` +(prefijo BLOCK7), que el mapeador funciona perfectamente: patrones +distintos escritos en páginas 20 y 63 del bloque 7 sobreviven al +intercambio de página (contenido independiente por página). El +mapeador nunca fue el problema. + +### Cuarta vuelta 2026-07-04: sprites como líneas verticales — página residente + +Con el fix del FSB, en el emulador real MSFS ya inicializaba bien +(pantalla: `Init MSFS at 57344`, `Free Blocks = 85`, y los 6 registros +con los valores EXACTOS que predijo la simulación) pero los sprites se +dibujaban como líneas verticales en vez de sus gráficos. + +Causa (descuido de diseño de este override, no del original): +`SaveBackgroundAndDrawSpriteRegisteredInMSFS` — el que dibuja en el +bucle principal y el que fabrica las imágenes desplazadas bajo demanda — +accede a la MSFS **sin envolver con Get/SetBank**. En el diseño original +no lo necesita: en 128K el banco 7 se queda mapeado en `$c000` +(`SetDrawingScreen7`) y en 48K la MSFS está en RAM plana siempre +visible. Nuestra primera versión "liberaba" el bloque 7 de vuelta a la +página 63 al restaurar tras `Init`/`Register...` → el dibujo leía +máscaras y gráficos de la página 63 (basura) → líneas verticales. El +simulador no podía detectarlo: sin mapeador modelado, página 20 y 63 +son la misma RAM plana. + +Fix: la página de MSFS queda **residente** en el bloque 7 desde el +init — `SetBankPreservingINTs` con valor ≠ 7 solo anota el número, no +desmapea (el "liberar a página 63" era invención de este override, +nada lo necesita). Documentado en la cabecera de la librería: si un +programa usa el bloque 7 para su propio banking, debe remapear su +página él mismo y llamar a `SetBankPreservingINTs(7)` antes de volver a +usar MSFS. diff --git a/docs/zx81sd/PRECAUCIONES.md b/docs/zx81sd/PRECAUCIONES.md new file mode 100644 index 000000000..e1c543fd6 --- /dev/null +++ b/docs/zx81sd/PRECAUCIONES.md @@ -0,0 +1,184 @@ +# Precauciones al escribir o portar software para zx81sd + +zx81sd hace que ZX BASIC genere binarios que "parecen" un Spectrum (la +interfaz SD81 Booster emula su pantalla, y buena parte de la stdlib +compartida asume convenciones del Spectrum), pero **no hay ROM del +Spectrum en ningún sitio**: no hay `RST $28` de la ROM, no hay rutinas +en direcciones fijas, no hay sysvars del Spectrum en `$5C00+`. Casi +todos los bugs de este port han venido de código (de examples/ oficiales +o de la stdlib compartida) que asume silenciosamente alguna de estas +cosas. Antes de portar algo, repasa esta lista. + +## 1. Nunca hay ROM: cuidado con direcciones absolutas y sysvars + +Cualquier `POKE`/`PEEK`/`CALL` a una dirección numérica fija +(23675, 23658, $22AC, $0DFE...) casi seguro que es una sysvar o rutina +de la **ROM del Spectrum**, que en zx81sd no existe: esa dirección cae +en RAM libre, o peor, en pleno código compilado del programa — +ejecutarla o interpretarla como dato produce corrupción silenciosa, +gráficos erróneos, o un `HALT`/reinicio salvaje muy difícil de +relacionar con la causa (varios bugs de este port tardaron sesiones +enteras en diagnosticarse por esto). + +- **Sysvars del Spectrum → sysvars de zx81sd**: la tabla de + equivalencias está en + [`../../src/lib/arch/zx81sd/runtime/sysvars.asm`](../../src/lib/arch/zx81sd/runtime/sysvars.asm) + (todas viven en `$8000+`, no en `$5C00+`). Ejemplos ya resueltos: + `UDG` (23675 → `$8002`), `COORDS` (23677/23678 → `$8004`/`$8005`). + Ver [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md) para el detalle línea a + línea de cada caso ya encontrado. +- **Rutinas de ROM llamadas directamente** (`call $22AC` = PIXEL-ADD, + `call $0DFE` = CL-SC-ALL/scroll, `RST $28` = calculador FP...): si el + fuente o una librería compartida hace esto, hace falta un override en + `src/lib/arch/zx81sd/` que sustituya la llamada por la rutina propia + con el **mismo contrato de registros** que la de la ROM (ver más + abajo). Ejemplo resuelto: `stdlib/scroll.bas`. +- **`grep` preventivo**: al portar un fichero de `zx48k/` a `zx81sd/`, + buscar `EQU 0[0-9A-F]` / `call 0x` / literales de 4-5 dígitos + sospechosos antes de darlo por bueno, no solo cuando algo falla. + +## 2. Contratos de registros de las rutinas ASM: son sagrados + +Varias rutinas del runtime tienen contratos de preservación de +registros explícitos y no negociables porque el código que las llama +(heredado de zx48k, no tocable) depende de ellos al pie de la letra. +Ejemplos: + +- `PIXEL_ADDR` (`runtime/pixel_addr.asm`): A=191, B=Y, C=X → HL=offset, + A=X AND 7; **destruye B, preserva D y E**. `draw.asm` guarda la + coordenada Bresenham en D alrededor de la llamada confiando en esto + literalmente — romperlo (como pasó una vez con un intento que usaba D + de scratch) corrompe cualquier línea con componente vertical sin + tocar para nada las horizontales, lo que despista mucho a la hora de + diagnosticar. +- `GetBankPreservingRegs`/`SetBankPreservingINTs` (MSFS, + `cb/maskedsprites.bas`): contrato documentado "preserva D,E,H,L". + Escribir el reemplazo en BASIC plano en vez de ASM a mano rompe esto + sin ningún aviso del compilador (el código BASIC generado usa + registros libremente por dentro) — un bug real de esta clase hizo que + 6 sprites de prueba se registraran todos en la misma dirección + incorrecta. **Nota**: el port de MSFS/`maskedsprites.bas` sigue en + proceso (aún no funciona del todo bien) — este ejemplo concreto sirve + para ilustrar el tipo de bug, no como confirmación de que la librería + ya esté terminada. + +**Regla práctica**: si vas a sustituir una rutina ASM que tiene un +contrato de registros documentado (o que se puede inferir mirando quién +la llama y qué asume), reimplaza en ASM a mano preservando exactamente +ese contrato. Una función BASIC (`SUB`/`FUNCTION`), por sencilla que +parezca, NO es un reemplazo válido salvo que el contrato sea "ninguno". + +## 3. El teclado es el del ZX81, no el del Spectrum + +El SD81 Booster no tiene teclado Spectrum: reescanea el teclado físico +de 40 teclas del ZX81 (`runtime/io/keyboard/keyscan.asm`). Diferencias +que importan al portar/escribir código: + +- Pulsación directa de una letra: minúscula. Con `SHIFT+letra`: + MAYÚSCULA de esa letra — exactamente igual que en un Spectrum real, a + diferencia del ZX81 original (donde `SHIFT+letra` daba un símbolo, no + una mayúscula). Esta redefinición es una decisión de diseño de este + port, ver [MAP.md](MAP.md) sección "Esquema de teclado nuevo". En + consecuencia, **sí se puede escanear/comparar `INKEY$` contra + mayúsculas de letra** (`IF INKEY$="S"`, pensado para jugarse con + `SHIFT` sostenido al estilo Spectrum) sin ninguna limitación de + hardware: basta con pulsar `SHIFT+S`. +- `SHIFT+"2"` alterna un CAPS LOCK persistente. +- `"."` es una tecla normal (`.` sin shift, `,` con shift). Los símbolos + del ZX81 original asociados a cada tecla (`:` en Z, `)` en O, etc.) se + alcanzan con la secuencia `"." + tecla` **solo desde `INPUT()`** (tecla + muerta gestionada en `stdlib/input.bas`), no desde `INKEY$` a pelo — + no hay forma de "sostener ambas a la vez" con fiabilidad en este + teclado, así que la composición se hace pulsando `.` primero y la + segunda tecla después. Pulsar `.` **dos veces seguidas** confirma el + primer punto como literal y descarta cualquier combo: la tecla que + venga después de ese segundo punto se lee como una pulsación nueva, + sin combinar con nada (permite escribir cualquier tecla justo después + de un punto sin arriesgarse a formar un símbolo por accidente). + +## 4. No hay interrupciones: nunca esperes un `HALT`/`EI` para sincronizar + +El runtime de zx81sd corre permanentemente con interrupciones +deshabilitadas (`DI`); el vector `$0038` es solo una trampa `DI;HALT`, +no un manejador de interrupción real. Cualquier código (típicamente +código ASM inline de un ejemplo, no de la stdlib) que haga `EI` seguido +de `HALT` esperando el pulso de 50Hz de la ROM del Spectrum **se cuelga +para siempre** — no hay nada que lo despierte. + +- **Sustituto**: `VSYNC_TICK` (namespace `core`, en + `runtime/vsync.asm`) sondea por puerto el contador de pulsos VSYNC + real del hardware SD81 Booster. Ya lo usa `PAUSE` internamente. +- Al llamarlo desde un bloque `ASM ... END ASM` que no esté ya dentro de + `push namespace core`, hay que usar el prefijo completo: + `call .core.VSYNC_TICK` (si se omite el prefijo, el compilador da + `Undefined GLOBAL label '.VSYNC_TICK'` — error ya visto más de una + vez en este port). +- Un contador que antes se incrementaba solo por la interrupción en + segundo plano (`FRAMES`/23672 en el Spectrum) hay que actualizarlo a + mano llamando a `VSYNC_TICK` explícitamente en cada vuelta del bucle + de espera, no solo una vez al principio. + +## 5. Namespaces y mangling de etiquetas ASM + +Un `DIM X` o `SUB`/`FUNCTION X` de BASIC se traduce a la etiqueta ASM +`_X` (un solo guion bajo), **salvo** que el fichero envuelva su código +en `push namespace core ... pop namespace`, en cuyo caso hay que +referenciarla desde fuera como `.core._X` (variables) o `.core.X` +(funciones/rutinas). Confundir esto en cualquier dirección produce +`Undefined GLOBAL label`. Si un fichero de este port no usa namespacing +en ningún otro sitio, no hace falta envolver un bloque `ASM` nuevo en +`push namespace core` solo porque otro fichero (como `vsync.asm`) sí lo +use — basta con prefijar la referencia puntual. + +## 6. El eliminador de código muerto no ve las llamadas desde ASM a mano + +El análisis de "¿esto se usa?" del compilador solo cuenta llamadas +hechas con sintaxis BASIC (`Foo(x)`). Una `SUB`/`FUNCTION`/variable +BASIC referenciada **solo** desde un bloque `ASM ... END ASM` (p. ej. +`call _Foo`) puede ser eliminada como código muerto, dando +`Undefined GLOBAL label '._Foo'` al enlazar — el símbolo nunca llegó a +existir en el binario final. Dos salidas: + +- Si es un dato puro (un byte de estado, por ejemplo), declararlo como + ASM puro (`ASM \n _Label: \n DEFB 0 \n END ASM` a nivel de fichero), + no como `DIM`. +- Si es una función que de verdad hace falta que sea BASIC (porque + llama a otras cosas de la stdlib), añadir una llamada BASIC real + (aunque sea redundante/no estrictamente necesaria en ese punto) en + algún sitio alcanzable del código, para que el análisis de uso la + cuente. + +## 7. Metodología de depuración sin hardware + +Para diagnosticar sin gastar ciclos de prueba-error en el emulador o el +hardware real, este port usa simulación directa del binario con el +paquete `z80` de Python. Dos lecciones ya aprendidas por las malas +(documentadas con más detalle en [MAP.md](MAP.md)): + +- Comprobar el PC periódicamente cada N ticks gruesos puede dar falsos + "atascado" si justo cae siempre en el mismo punto de un bucle; usar + breakpoints reales (comparar `m.pc` contra la dirección exacta, + sacada del `.map`) o chunks de tick más finos. +- La RAM del simulador nace a **ceros**, igual que la del Spectrum tras + el test de RAM de su ROM. Esto oculta bugs de memoria no inicializada + (dio dos falsos "OK" seguidos en el bug del free-space-bitmap de + MSFS). Para validar código que lee memoria que no inicializa él + mismo, rellenar toda la RAM no cargada con `0xFF` antes de cargar el + binario, para reproducir las condiciones reales de hardware/tarjeta. +- El simulador **no modela el mapeador de memoria** (`OUT` al puerto + `$E7` es un no-op): puede validar que la lógica Z80 es autoconsistente, + pero no que el intercambio de página físico funcione de verdad — eso + solo se confirma en el emulador (EightyOne) o en hardware real. +- Si un binario falla en EightyOne pero la simulación Python lo ejecuta + limpio, sospecha primero del emulador (ver el bug de traps de cinta ya + encontrado y corregido en `Eightyone2/src/ZX81/rompatch.cpp`, en el + repositorio del emulador, no en este) antes que del runtime — en + hardware real esos traps no existen. + +## Ver también + +- [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md) — catálogo de cambios de fuente + ya necesarios en ejemplos oficiales, con el patrón general a buscar + en cualquier ejemplo nuevo. +- [MAP.md](MAP.md) — bitácora técnica completa, bug a bug, con las + trazas de investigación. diff --git a/docs/zx81sd/README.md b/docs/zx81sd/README.md new file mode 100644 index 000000000..e8d7dd7de --- /dev/null +++ b/docs/zx81sd/README.md @@ -0,0 +1,79 @@ +# Arquitectura `zx81sd`: ZX81 + SD81 Booster + +`zx81sd` es una arquitectura de este compilador para un **ZX81 real con +la tarjeta [SD81 Booster](https://www.sd81.eu/)** (una interfaz que +añade pantalla tipo Spectrum, sonido AY/beeper, mapeador de memoria por +páginas y acceso a tarjeta SD). + +Todo el código específico de esta arquitectura vive exclusivamente bajo: + +``` +src/arch/zx81sd/ (backend del compilador) +src/lib/arch/zx81sd/ (runtime ASM + stdlib BASIC) +``` + +## Regla de oro del port + +Este compilador es compartido por todas las arquitecturas (zx48k, +zx128k, zxnext...). **El port de zx81sd nunca modifica el frontend ni +la stdlib/runtime compartidos.** El mecanismo de resolución de +`#include`/`#require` busca primero en `src/lib/arch/zx81sd/`, y si no +encuentra el fichero cae automáticamente en `src/lib/arch/zx48k/` (el +fichero compartido). Por eso muchos overrides de zx81sd son copias +completas de la versión zx48k con solo unas pocas líneas cambiadas: hay +que copiar el fichero entero, no un parche — un override parcial +simplemente no existe como concepto aquí. + +Antes de tocar cualquier cosa fuera de `zx81sd/`, para: seguramente hay +una forma de resolverlo con un override. + +## Estado del port + +Funcionalmente completo desde 2026-07-02: FP (RST $28 propio), +gráficos (PLOT/DRAW/arcos/CIRCLE), sonido (BEEP y PLAY sobre AY ZonX y +beeper), teclado (INKEY$/INPUT sobre el teclado físico del ZX81), +joystick, la librería MCU completa (ficheros, RTC/BAT, voz, mapeador de +memoria...) y LOAD/SAVE/VERIFY...CODE contra SD. + +Pendiente / sin auditar: el resto de utilidades de pantalla de la +stdlib compartida (`winscroll`, `putchars`, `puttile`, `screen`, +`print42/64`...) — puede que alguna dependa de la ROM del Spectrum +igual que `scroll.bas` (ya arreglado, ver [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md)). +El port de `maskedsprites.bas`/MSFS (sprites enmascarados sobre el +mapeador de memoria) también está en proceso, aún sin terminar de +funcionar bien. + +## Documentación + +- **[USO.md](USO.md)** — cómo compilar un programa, empaquetarlo para + el ZX81 y cargarlo desde la tarjeta SD. +- **[PRECAUCIONES.md](PRECAUCIONES.md)** — qué hay que tener en cuenta + al escribir o portar software para esta arquitectura (mapa de + memoria, sysvars, teclado, cosas que NO existen aquí aunque existan + en Spectrum). +- **[CAMBIOS_BASIC.md](CAMBIOS_BASIC.md)** — qué cambios de fuente + BASIC hizo falta para portar cada ejemplo oficial de `examples/` (con + el porqué de cada uno). Punto de partida obligado antes de portar un + ejemplo nuevo: casi siempre es uno de los patrones ya catalogados + ahí. +- **[MAP.md](MAP.md)** — bitácora técnica detallada de todos los bugs + encontrados y corregidos durante el port (runtime ASM, no fuentes + BASIC), con la traza de investigación de cada uno. Es el documento a + consultar cuando algo falla en tiempo de ejecución de forma que + recuerda a un bug ya resuelto. + +## Ejemplos + +Los programas de ejemplo ya adaptados/probados para esta arquitectura +están en [`examples/sd81/`](../../examples/sd81/) (junto al resto de +`examples/` del compilador). El detalle de qué hubo que tocar en cada +uno, y por qué, está en [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md). + +## Herramientas de empaquetado y pruebas + +El empaquetador (`split_sd81.py`, parte binario plano en páginas de +8KB + genera el cargador `.p` para el ZX81) y el conjunto más amplio de +fuentes de depuración/diagnóstico usados durante el desarrollo del port +viven en un repositorio complementario, no en este. Ver +[USO.md](USO.md) para el flujo completo de compilar → empaquetar → +cargar. diff --git a/docs/zx81sd/USO.md b/docs/zx81sd/USO.md new file mode 100644 index 000000000..25438cfd3 --- /dev/null +++ b/docs/zx81sd/USO.md @@ -0,0 +1,88 @@ +# Uso: compilar, empaquetar y cargar un programa en zx81sd + +## 1. Compilar + +Desde la raíz de este repositorio: + +``` +python -m src.zxbc.zxbc --arch zx81sd -o -M +``` + +- `--arch zx81sd` selecciona el backend y los overrides de esta + arquitectura (ver [README.md](README.md) — regla de oro del port). +- `-M ` es opcional pero muy recomendable: genera el mapa de + símbolos (dirección de cada label ASM/BASIC), imprescindible para + depurar con un emulador o simular el binario (ver [MAP.md](MAP.md) + para ejemplos de arneses de simulación en Python). +- Ejemplos transcritos de Sinclair BASIC clásico (arrays/strings + 1-based) suelen necesitar además `--string-base 1 --array-base 1` + (ver [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md), ejemplo `comecoquitos.bas`). + +También existe `python zxbc.py --arch zx81sd -f bin -o ` +(script de entrada tradicional); ambas formas son equivalentes. + +## 2. Empaquetar para el ZX81 + +El binario que produce el compilador es una imagen plana de hasta 48KB +(bloques 0-5 del mapeador SD81). El ZX81 solo puede cargar de golpe lo +que quepa en su propia RAM visible, así que hay que partir ese binario +en páginas de 8KB y generar un cargador BASIC que las vaya metiendo en +la tarjeta SD81 Booster una a una, remapeando el mapeador de memoria +entre página y página. + +Esto lo hace `split_sd81.py`, una herramienta que vive en el +repositorio complementario de pruebas del port (no en este +repositorio del compilador): + +``` +python split_sd81.py [PREFIJO] +``` + +- `PREFIJO` (opcional) es el nombre base de los ficheros de salida, en + el charset del ZX81 (letras, dígitos, sin guion bajo). Si se omite, + se deriva del nombre del `.bin` de entrada. +- Genera: + - `P8.BIN`, `P9.BIN`, ... — una por cada 8KB del + binario (página SD81 8 = bloque 0, 9 = bloque 1, etc.) + - `_loader.txt` — el listado BASIC del cargador en texto + plano (para leer/depurar). + - `.P` — el mismo cargador, ya tokenizado, listo para + cargar y ejecutar en el ZX81 (`LOAD ""` desde la SD). + +El cargador generado usa `LOAD THEN CLEAR`, `LOAD *MAP` y +`LOAD FAST ... CODE`, extensiones del firmware del SD81 Booster (no +existen en la ROM original del ZX81). Hace lo siguiente, en orden: + +1. Reserva memoria (`CLEAR`) y carga `BOOT1.BIN` (el stage 1, fijo para + todos los programas, también en el repositorio complementario). +2. Por cada página del binario: mapea el bloque 7 a esa página física + (`LOAD *MAP 7,`) y vuelca los datos ahí (`LOAD FAST ... CODE + 57344`, la ventana del bloque 7 en `$E000`). +3. Al terminar, deja el mapeador en modo "página completa" (`LOAD *MAP + 7,63`) — a partir de aquí el mapeador ya no vuelve a modo simple + hasta el siguiente reset — y salta a `BOOT1.BIN` (`RAND USR 24576`), + que hace el mapeo definitivo de los bloques 0-5 y arranca el + programa. + +## 3. Copiar a la tarjeta SD + +Copia a la SD, junto al resto de tu colección: + +- `BOOT1.BIN` (una sola vez, es el mismo para todos los programas) +- Todas las `P.BIN` del programa +- `.P` + +Y en el ZX81 (o en EightyOne apuntando a la imagen de la SD): `LOAD ""` +y selecciona ``. + +## 4. Depurar sin hardware: simulación con Python + +Para diagnosticar cuelgues, HALTs o resultados incorrectos sin tener +que probar en el emulador o el hardware real cada vez, el desarrollo de +este port ha usado el paquete `z80` de Python (`pip install z80`) para +simular el binario plano directamente. La metodología completa +(incluida una trampa importante: la RAM del simulador nace a ceros, lo +que puede ocultar bugs de memoria no inicializada que sí aparecen en +hardware real) está documentada en [MAP.md](MAP.md) — sección "Heap en +$8100 + traps de cinta de EightyOne" y las notas de metodología del bug +de MSFS/maskedsprites. 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/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/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/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/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 + From 507aeb8f292d8eced4e102d8f4cd58ce21c1ff18 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 09:46:37 +0200 Subject: [PATCH 144/169] zx81sd: enlazar repositorios relacionados en el README SD81 Booster (hardware/firmware), EightyOne Cross-platform (emulador usado en el desarrollo) y CPM_SD81. Co-Authored-By: Claude Sonnet 5 --- docs/zx81sd/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/zx81sd/README.md b/docs/zx81sd/README.md index e8d7dd7de..0e7741e92 100644 --- a/docs/zx81sd/README.md +++ b/docs/zx81sd/README.md @@ -77,3 +77,12 @@ fuentes de depuración/diagnóstico usados durante el desarrollo del port viven en un repositorio complementario, no en este. Ver [USO.md](USO.md) para el flujo completo de compilar → empaquetar → cargar. + +## Repositorios relacionados + +- **[SD81 Booster](https://codeberg.org/Retrostuff/SD81-Booster)** — + firmware/hardware de la interfaz para la que se ha hecho este port. +- **[EightyOne Cross-platform](https://codeberg.org/wilco2009/EightyOne-CrossPlatform)** — + emulador usado durante el desarrollo para probar sin hardware real. +- **[CPM_SD81](https://codeberg.org/wilco2009/CPM_SD81)** — CP/M sobre + el SD81 Booster. From a469f4aa5c7202d4a61d1b59db52e9c7d0eb16f7 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 09:53:18 +0200 Subject: [PATCH 145/169] zx81sd: precisar estado de pendientes de stdlib compartida winscroll se cree portado/probado pero sin auditoria formal; putchars/ puttile sin auditar (sin ROM detectada a simple vista); screen.bas SI depende de ROM (STK_END/S_SCRNS_BC/RECLAIM2); print42/print64 SI dependen de sysvars Spectrum (UDG/ATTR_P) y son el siguiente objetivo. Co-Authored-By: Claude Sonnet 5 --- docs/zx81sd/README.md | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/docs/zx81sd/README.md b/docs/zx81sd/README.md index 0e7741e92..6874d7f63 100644 --- a/docs/zx81sd/README.md +++ b/docs/zx81sd/README.md @@ -35,13 +35,29 @@ beeper), teclado (INKEY$/INPUT sobre el teclado físico del ZX81), joystick, la librería MCU completa (ficheros, RTC/BAT, voz, mapeador de memoria...) y LOAD/SAVE/VERIFY...CODE contra SD. -Pendiente / sin auditar: el resto de utilidades de pantalla de la -stdlib compartida (`winscroll`, `putchars`, `puttile`, `screen`, -`print42/64`...) — puede que alguna dependa de la ROM del Spectrum -igual que `scroll.bas` (ya arreglado, ver [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md)). -El port de `maskedsprites.bas`/MSFS (sprites enmascarados sobre el -mapeador de memoria) también está en proceso, aún sin terminar de -funcionar bien. +Pendiente / sin auditar, resto de utilidades de pantalla de la stdlib +compartida: + +- `winscroll.bas`: se cree ya portado y probado, pero sin confirmar con + una auditoría formal (no hay override en `zx81sd/stdlib/` — de ser + cierto, es porque no necesitaba ninguno, como `scroll.bas` antes del + fix o `4inarow.bas`). +- `putchars.bas`/`puttile.bas`: sin auditar ni probar. Un vistazo rápido + al fuente no encuentra direcciones de ROM/sysvars del Spectrum + (`putChars` rellena un rectángulo de caracteres, `putTile` coloca un + tile de 16×16 px), así que son buenos candidatos a funcionar sin + cambios, pero no está confirmado. +- `screen.bas`: **sí depende de la ROM** (`$2538`/`$5C65`/`$19E8`, + rutinas y sysvars fijas del Spectrum para leer de vuelta un carácter + de pantalla) — necesitará un override real, no solo una auditoría. +- `print42.bas`/`print64.bas`: **sí dependen de sysvars del Spectrum** + (`$5C7B` = UDG, `23693` = ATTR_P) — confirmado pendiente de migrar, + próximo objetivo. + +Ver [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md) para el patrón general de este +tipo de fix. El port de `maskedsprites.bas`/MSFS (sprites enmascarados +sobre el mapeador de memoria) también está en proceso, aún sin terminar +de funcionar bien. ## Documentación From 488f991ed8f6ca7e67a3a564064bb743ad13ddc5 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 11:55:28 +0200 Subject: [PATCH 146/169] zx81sd: portar print42.bas/print64.bas (PRINT de 42/64 columnas) Overrides completos de la libreria compartida (Britlion): las sysvars fijas de la ROM del Spectrum (UDG $5C7B, CHARS-256 15360, ATTR_P 23693) se sustituyen por los sysvars propios de zx81sd, y las constantes de base de pantalla/atributos (antes literales de $4000/$5800) se parchean en tiempo de ejecucion via automodificacion de codigo, leyendo el byte alto real de SCREEN_ADDR/SCREEN_ATTR_ADDR. Bug real encontrado durante el port: un #include once dentro de estos ficheros, si resultaba ser la primera inclusion de todo el programa, arrastraba bootstrap.asm/charset.asm (con su INCBIN del font completo) justo en el punto del fichero fuente donde se puso el include -- a nivel BASIC eso da "illegal preprocessor character", y dentro de un bloque ASM se emite como codigo ejecutable real (HALT en direccion basura). Fix: no incluir sysvars.asm desde estos ficheros, solo referenciar los simbolos con el prefijo .core. (confiando en que CLS/PRINT ya lo trajeron). Documentado en PRECAUCIONES.md (nueva seccion 8) y CAMBIOS_BASIC.md (nueva seccion 9) como gotcha general para cualquier libreria nueva que necesite sysvars propios. Verificado por simulacion (examples/sd81/print4264.bas): ambas cadenas de prueba se renderizan legibles pixel a pixel, sin corrupcion de memoria, con HALT normal en __END_PROGRAM. Pendiente de confirmar en emulador/hardware real. Co-Authored-By: Claude Sonnet 5 --- docs/zx81sd/CAMBIOS_BASIC.md | 64 +++ docs/zx81sd/PRECAUCIONES.md | 36 ++ docs/zx81sd/README.md | 9 +- examples/sd81/print4264.bas | 13 + src/lib/arch/zx81sd/stdlib/print42.bas | 586 +++++++++++++++++++++++++ src/lib/arch/zx81sd/stdlib/print64.bas | 320 ++++++++++++++ 6 files changed, 1025 insertions(+), 3 deletions(-) create mode 100644 examples/sd81/print4264.bas create mode 100644 src/lib/arch/zx81sd/stdlib/print42.bas create mode 100644 src/lib/arch/zx81sd/stdlib/print64.bas diff --git a/docs/zx81sd/CAMBIOS_BASIC.md b/docs/zx81sd/CAMBIOS_BASIC.md index 5228bfff4..56eaa17fa 100644 --- a/docs/zx81sd/CAMBIOS_BASIC.md +++ b/docs/zx81sd/CAMBIOS_BASIC.md @@ -272,6 +272,63 @@ de `Map()`/`MapGet` sobre el bloque 7. --- +## 9. `print42.bas`/`print64.bas` — librerías nuevas (sin cambios de ejemplo) + +`stdlib/print42.bas` y `stdlib/print64.bas` (Britlion) implementan un +`PRINT` de 42/64 columnas dibujando pixel a pixel, en vez de usar el +`PRINT` normal de 32 columnas. La versión zx48k depende de la ROM del +Spectrum en varios sitios: + +- `print42.bas`: `ld hl,$5C7B` (sysvar `UDG`), `ld de,15360` + (`CHARS-256` fijo) y `ld a,(23693)` (sysvar `ATTR_P`). +- `print64.bas`: solo `ld a,(23693)` (`ATTR_P`) — su charset de 4px es + propio, no usa `UDG`/`CHARS`. +- Ambos, además, sitúan la pantalla y los atributos con constantes + **inmediatas** de base fija (`add a,64` / `add a,88`, los bytes altos + de `$4000`/`$5800`) en vez de leer un puntero — funciona en el + Spectrum porque esas direcciones son literales de ROM, pero en zx81sd + `SCREEN_ADDR`/`SCREEN_ATTR_ADDR` son variables (bloque 6, `$C000`/ + `$D800`). + +Se creó `src/lib/arch/zx81sd/stdlib/print42.bas` y `print64.bas` +(overrides completos): sysvars fijas → sus equivalentes zx81sd +(`UDG`/`CHARS`/`ATTR_P`, ver `sysvars.asm`), y las dos constantes de +base de pantalla/atributos se **parchean una vez al entrar en la +función** (automodificación de código sobre la propia instrucción +`ADD A,n`), leyendo el byte alto real de `SCREEN_ADDR`/ +`SCREEN_ATTR_ADDR` en ese momento — igual que hacía la ROM original con +sus propias rutinas, solo que con un valor variable en vez de fijo. + +**Bug real cazado durante el port** (no en el ejemplo, en la librería +misma): un `#include once ` puesto ingenuamente al +principio del fichero (nivel BASIC, fuera de cualquier bloque `ASM`) +producía `error: illegal preprocessor character`, y puesto dentro del +bloque `ASM` de la función compilaba pero colgaba el programa en +tiempo de ejecución (`HALT` en una dirección de código basura). Causa: +si esta librería es la **primera** en incluir `sysvars.asm` en todo el +programa, arrastra tras de sí `bootstrap.asm`/`charset.asm` — y este +último hace `INCBIN "specfont.bin"` (el font completo, no texto +ensamblador) — justo en ese punto del fichero fuente. Puesto a nivel +BASIC, el lexer de BASIC intenta tokenizar bytes binarios de fuente +como si fueran texto (de ahí el "illegal preprocessor character"). +Puesto dentro de la función, el binario del font se emite literalmente +en medio del cuerpo compilado de la función, y la CPU lo "ejecuta" +como si fueran instrucciones en cuanto el flujo cae ahí. Fix: no +incluir `sysvars.asm` en absoluto desde estos ficheros — confían en que +ya esté incluido por el resto del runtime (`CLS`/`PRINT`, que cualquier +programa que use `print42`/`print64` va a usar también), y solo +referencian los símbolos ya namespaced (`.core.CHARS`, etc.). Detalle +completo en [PRECAUCIONES.md](PRECAUCIONES.md) y [MAP.md](MAP.md). + +Verificado por simulación (arnés Python de siempre): las dos cadenas de +prueba (`tests_debug/print4264.bas`, repositorio complementario) se +renderizan legibles píxel a píxel en la posición esperada, sin +escrituras fuera de pantalla/atributos/variables propias del fichero, y +el programa llega a su `__END_PROGRAM`/`HALT` normal. Pendiente de +probar en el emulador/hardware real. + +--- + ## Resumen para el manual | Ejemplo | Copia en zx81sd | Cambios de fuente | Flags de compilación | @@ -284,6 +341,7 @@ de `Map()`/`MapGet` sobre el bloque 7. | maskedsprites.bas | `maskedsprites_sd81.bas` | `WaitForNewFrame` reescrita (EI+HALT → VSYNC_TICK); librería `cb/maskedsprites.bas` portada al mapeador (bloque 7) — **en proceso, aún no funciona del todo bien** | ninguna especial | | pong.bas (no oficial) | `pong.bas` en `examples/sd81/` | 1 línea ASM (namespace de VSYNC_TICK) | ninguna especial | | block7test.bas (no oficial) | `block7test.bas` en `examples/sd81/` | n/a (escrito directamente para zx81sd) | ninguna especial | +| print42.bas/print64.bas (librerías) | no aplica (no son ejemplos) | ninguno — fix fue en `stdlib/print42.bas`/`print64.bas` | ninguna especial | Patrón identificado para futuros ejemplos: @@ -302,3 +360,9 @@ Patrón identificado para futuros ejemplos: `push namespace core` (como `VSYNC_TICK`) necesita el prefijo `.core.` si el bloque `ASM` que lo invoca no está ya dentro de ese namespace (ver caso de `pong.bas` arriba). +4. Si una librería nueva necesita sysvars propios (`CHARS`/`UDG`/ + `ATTR_P`/`SCREEN_ADDR`/...), **no hacer `#include once ` + dentro del fichero** — solo referenciar los símbolos con el prefijo + `.core.`, confiando en que el resto del runtime (`CLS`/`PRINT`) ya lo + incluyó. Ver caso de `print42.bas`/`print64.bas` arriba y + [PRECAUCIONES.md](PRECAUCIONES.md). diff --git a/docs/zx81sd/PRECAUCIONES.md b/docs/zx81sd/PRECAUCIONES.md index e1c543fd6..a83c15551 100644 --- a/docs/zx81sd/PRECAUCIONES.md +++ b/docs/zx81sd/PRECAUCIONES.md @@ -175,6 +175,42 @@ paquete `z80` de Python. Dos lecciones ya aprendidas por las malas repositorio del emulador, no en este) antes que del runtime — en hardware real esos traps no existen. +## 8. Nunca hagas `#include once ` desde una librería propia + +Si necesitas los sysvars propios de zx81sd (`CHARS`/`UDG`/`ATTR_P`/ +`SCREEN_ADDR`/`SCREEN_ATTR_ADDR`...) desde un fichero `stdlib/*.bas` +nuevo, **no lo incluyas tú mismo con `#include once `** — +solo referencia los símbolos con el prefijo `.core.` (ver punto 5) y +confía en que el resto del runtime ya lo trajo. + +`sysvars.asm` arrastra tras de sí `bootstrap.asm` → `charset.asm`, y +este último hace `INCBIN "specfont.bin"` (el font completo, bytes +binarios, no texto ensamblador). Si tu fichero resulta ser el +**primero** en incluir `sysvars.asm` en todo el programa compilado +(fácil que pase: un `#include ` al principio del fuente +del usuario se procesa antes que cualquier `CLS`/`PRINT` textualmente +posterior), ese `INCBIN` se emite **justo en el punto del fichero fuente +donde pusiste el `#include`**: + +- Puesto a nivel BASIC (fuera de un bloque `ASM ... END ASM`): el lexer + de BASIC intenta tokenizar esos bytes binarios como si fueran texto + fuente → `error: illegal preprocessor character` en líneas que no + tienen nada que ver con el problema real (mal atribuidas al inicio + del fichero). +- Puesto dentro de un bloque `ASM` (p. ej. al principio del cuerpo de + una función): el binario del font se emite literalmente en medio del + código compilado de esa función — compila sin error, pero la CPU + "ejecuta" esos bytes de font como si fueran instrucciones en cuanto + el flujo de control cae ahí, produciendo un `HALT` o comportamiento + errático en una dirección que no tiene relación aparente con el bug + (encontrado al portar `print42.bas`/`print64.bas`, ver + [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md)). + +Cualquier programa real que use tu librería casi seguro que también usa +`CLS`/`PRINT` en algún punto, y esas rutinas ya requieren `sysvars.asm` +— así que omitir el `#include` en tu fichero es seguro en la práctica, +no una chapuza. + ## Ver también - [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md) — catálogo de cambios de fuente diff --git a/docs/zx81sd/README.md b/docs/zx81sd/README.md index 6874d7f63..9bf6ff056 100644 --- a/docs/zx81sd/README.md +++ b/docs/zx81sd/README.md @@ -50,9 +50,12 @@ compartida: - `screen.bas`: **sí depende de la ROM** (`$2538`/`$5C65`/`$19E8`, rutinas y sysvars fijas del Spectrum para leer de vuelta un carácter de pantalla) — necesitará un override real, no solo una auditoría. -- `print42.bas`/`print64.bas`: **sí dependen de sysvars del Spectrum** - (`$5C7B` = UDG, `23693` = ATTR_P) — confirmado pendiente de migrar, - próximo objetivo. +- `print42.bas`/`print64.bas`: **portados** (override completo en + `stdlib/`) — sysvars fijas → equivalentes zx81sd, y las constantes de + base de pantalla/atributos se parchean en tiempo de ejecución + (automodificación de código) en vez de ser fijas. Verificado por + simulación (texto legible píxel a píxel, sin corrupción de memoria); + pendiente de confirmar en el emulador/hardware real. Ver [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md) para el patrón general de este tipo de fix. El port de `maskedsprites.bas`/MSFS (sprites enmascarados 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/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 From 67f33150571aa05a182863a9a3eb1a35e0b40376 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 12:11:29 +0200 Subject: [PATCH 147/169] zx81sd: mover documentacion y herramientas a src/arch/zx81sd/ - docs/zx81sd/README.md -> src/arch/zx81sd/README.md - docs/zx81sd/{USO,PRECAUCIONES,CAMBIOS_BASIC,MAP}.md -> src/arch/zx81sd/doc/ - Copia del repositorio complementario (solo para pruebas, no fuente canonica) las herramientas de empaquetado a src/arch/zx81sd/tools/: split_sd81.py, zx81_p_loader.py, boot1.asm, boot1.bin. Enlaces relativos corregidos en toda la documentacion movida. Probado end-to-end (compilar + split_sd81.py) desde la nueva ubicacion. Co-Authored-By: Claude Sonnet 5 --- {docs => src/arch}/zx81sd/README.md | 66 ++- .../arch/zx81sd/doc}/CAMBIOS_BASIC.md | 0 {docs/zx81sd => src/arch/zx81sd/doc}/MAP.md | 6 +- .../arch/zx81sd/doc}/PRECAUCIONES.md | 2 +- {docs/zx81sd => src/arch/zx81sd/doc}/USO.md | 11 +- src/arch/zx81sd/tools/boot1.asm | 85 +++ src/arch/zx81sd/tools/boot1.bin | Bin 0 -> 27 bytes src/arch/zx81sd/tools/split_sd81.py | 205 +++++++ src/arch/zx81sd/tools/zx81_p_loader.py | 545 ++++++++++++++++++ 9 files changed, 880 insertions(+), 40 deletions(-) rename {docs => src/arch}/zx81sd/README.md (61%) rename {docs/zx81sd => src/arch/zx81sd/doc}/CAMBIOS_BASIC.md (100%) rename {docs/zx81sd => src/arch/zx81sd/doc}/MAP.md (99%) rename {docs/zx81sd => src/arch/zx81sd/doc}/PRECAUCIONES.md (99%) rename {docs/zx81sd => src/arch/zx81sd/doc}/USO.md (90%) create mode 100644 src/arch/zx81sd/tools/boot1.asm create mode 100644 src/arch/zx81sd/tools/boot1.bin create mode 100644 src/arch/zx81sd/tools/split_sd81.py create mode 100644 src/arch/zx81sd/tools/zx81_p_loader.py diff --git a/docs/zx81sd/README.md b/src/arch/zx81sd/README.md similarity index 61% rename from docs/zx81sd/README.md rename to src/arch/zx81sd/README.md index 9bf6ff056..890495505 100644 --- a/docs/zx81sd/README.md +++ b/src/arch/zx81sd/README.md @@ -8,7 +8,8 @@ páginas y acceso a tarjeta SD). Todo el código específico de esta arquitectura vive exclusivamente bajo: ``` -src/arch/zx81sd/ (backend del compilador) +src/arch/zx81sd/ (backend del compilador, esta documentación, + y las herramientas de empaquetado en tools/) src/lib/arch/zx81sd/ (runtime ASM + stdlib BASIC) ``` @@ -57,45 +58,50 @@ compartida: simulación (texto legible píxel a píxel, sin corrupción de memoria); pendiente de confirmar en el emulador/hardware real. -Ver [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md) para el patrón general de este -tipo de fix. El port de `maskedsprites.bas`/MSFS (sprites enmascarados -sobre el mapeador de memoria) también está en proceso, aún sin terminar -de funcionar bien. +Ver [doc/CAMBIOS_BASIC.md](doc/CAMBIOS_BASIC.md) para el patrón general +de este tipo de fix. El port de `maskedsprites.bas`/MSFS (sprites +enmascarados sobre el mapeador de memoria) también está en proceso, aún +sin terminar de funcionar bien. ## Documentación -- **[USO.md](USO.md)** — cómo compilar un programa, empaquetarlo para - el ZX81 y cargarlo desde la tarjeta SD. -- **[PRECAUCIONES.md](PRECAUCIONES.md)** — qué hay que tener en cuenta - al escribir o portar software para esta arquitectura (mapa de +- **[doc/USO.md](doc/USO.md)** — cómo compilar un programa, empaquetarlo + para el ZX81 y cargarlo desde la tarjeta SD. +- **[doc/PRECAUCIONES.md](doc/PRECAUCIONES.md)** — qué hay que tener en + cuenta al escribir o portar software para esta arquitectura (mapa de memoria, sysvars, teclado, cosas que NO existen aquí aunque existan en Spectrum). -- **[CAMBIOS_BASIC.md](CAMBIOS_BASIC.md)** — qué cambios de fuente - BASIC hizo falta para portar cada ejemplo oficial de `examples/` (con - el porqué de cada uno). Punto de partida obligado antes de portar un - ejemplo nuevo: casi siempre es uno de los patrones ya catalogados - ahí. -- **[MAP.md](MAP.md)** — bitácora técnica detallada de todos los bugs - encontrados y corregidos durante el port (runtime ASM, no fuentes - BASIC), con la traza de investigación de cada uno. Es el documento a - consultar cuando algo falla en tiempo de ejecución de forma que - recuerda a un bug ya resuelto. +- **[doc/CAMBIOS_BASIC.md](doc/CAMBIOS_BASIC.md)** — qué cambios de + fuente BASIC hizo falta para portar cada ejemplo oficial de + `examples/` (con el porqué de cada uno). Punto de partida obligado + antes de portar un ejemplo nuevo: casi siempre es uno de los patrones + ya catalogados ahí. +- **[doc/MAP.md](doc/MAP.md)** — bitácora técnica detallada de todos los + bugs encontrados y corregidos durante el port (runtime ASM, no + fuentes BASIC), con la traza de investigación de cada uno. Es el + documento a consultar cuando algo falla en tiempo de ejecución de + forma que recuerda a un bug ya resuelto. ## Ejemplos Los programas de ejemplo ya adaptados/probados para esta arquitectura -están en [`examples/sd81/`](../../examples/sd81/) (junto al resto de +están en [`examples/sd81/`](../../../examples/sd81/) (junto al resto de `examples/` del compilador). El detalle de qué hubo que tocar en cada -uno, y por qué, está en [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md). - -## Herramientas de empaquetado y pruebas - -El empaquetador (`split_sd81.py`, parte binario plano en páginas de -8KB + genera el cargador `.p` para el ZX81) y el conjunto más amplio de -fuentes de depuración/diagnóstico usados durante el desarrollo del port -viven en un repositorio complementario, no en este. Ver -[USO.md](USO.md) para el flujo completo de compilar → empaquetar → -cargar. +uno, y por qué, está en [doc/CAMBIOS_BASIC.md](doc/CAMBIOS_BASIC.md). + +## Herramientas de empaquetado + +[`tools/split_sd81.py`](tools/split_sd81.py) parte el binario plano +generado por el compilador en páginas de 8KB y genera el cargador `.p` +para el ZX81; [`tools/zx81_p_loader.py`](tools/zx81_p_loader.py) es el +tokenizador BASIC que usa para generarlo; [`tools/boot1.asm`](tools/boot1.asm)/ +[`tools/boot1.bin`](tools/boot1.bin) son el cargador de segunda etapa +(stage 1), fijo para todos los programas. Ver +[doc/USO.md](doc/USO.md) para el flujo completo de compilar → +empaquetar → cargar. El conjunto más amplio de fuentes de depuración/ +diagnóstico usados durante el desarrollo del port (no herramientas, +solo pruebas puntuales) vive en un repositorio complementario privado, +no en este. ## Repositorios relacionados diff --git a/docs/zx81sd/CAMBIOS_BASIC.md b/src/arch/zx81sd/doc/CAMBIOS_BASIC.md similarity index 100% rename from docs/zx81sd/CAMBIOS_BASIC.md rename to src/arch/zx81sd/doc/CAMBIOS_BASIC.md diff --git a/docs/zx81sd/MAP.md b/src/arch/zx81sd/doc/MAP.md similarity index 99% rename from docs/zx81sd/MAP.md rename to src/arch/zx81sd/doc/MAP.md index a6eec0504..2ffd4e9f1 100644 --- a/docs/zx81sd/MAP.md +++ b/src/arch/zx81sd/doc/MAP.md @@ -4,7 +4,7 @@ Nota sobre rutas: esta bitácora se escribió originalmente en el repositorio complementario de pruebas del port (donde los `.bas` citados vivían todos en un directorio `tests_debug/`). Los ejemplos que se consideraron suficientemente maduros para publicarse ya están copiados -en este repositorio, en [`examples/sd81/`](../../examples/sd81/) +en este repositorio, en [`examples/sd81/`](../../../../examples/sd81/) (`flights_sd81.bas`, `snake_sd81.bas`, `maskedsprites_sd81.bas`, `pong.bas`, `block7test.bas` — ver [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md)). El resto de fuentes de depuración puntual mencionadas aquí (`diag1-6`, @@ -14,9 +14,9 @@ repositorio complementario, no en este. Compilación de cada uno (desde la raíz de este repositorio): ``` python -m src.zxbc.zxbc examples\sd81\.bas --arch zx81sd -o .bin -python split_sd81.py .bin +python src\arch\zx81sd\tools\split_sd81.py .bin ``` -(`split_sd81.py` vive en el repositorio complementario — ver [USO.md](USO.md).) +(ver [USO.md](USO.md) para el detalle del empaquetador.) | Fuente | Prefijo SD81 | Qué prueba | Resultado obtenido | |------------------------|--------------|---------------------------------------------------------------------|---------------------| diff --git a/docs/zx81sd/PRECAUCIONES.md b/src/arch/zx81sd/doc/PRECAUCIONES.md similarity index 99% rename from docs/zx81sd/PRECAUCIONES.md rename to src/arch/zx81sd/doc/PRECAUCIONES.md index a83c15551..8c22fb451 100644 --- a/docs/zx81sd/PRECAUCIONES.md +++ b/src/arch/zx81sd/doc/PRECAUCIONES.md @@ -22,7 +22,7 @@ enteras en diagnosticarse por esto). - **Sysvars del Spectrum → sysvars de zx81sd**: la tabla de equivalencias está en - [`../../src/lib/arch/zx81sd/runtime/sysvars.asm`](../../src/lib/arch/zx81sd/runtime/sysvars.asm) + [`../../../lib/arch/zx81sd/runtime/sysvars.asm`](../../../lib/arch/zx81sd/runtime/sysvars.asm) (todas viven en `$8000+`, no en `$5C00+`). Ejemplos ya resueltos: `UDG` (23675 → `$8002`), `COORDS` (23677/23678 → `$8004`/`$8005`). Ver [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md) para el detalle línea a diff --git a/docs/zx81sd/USO.md b/src/arch/zx81sd/doc/USO.md similarity index 90% rename from docs/zx81sd/USO.md rename to src/arch/zx81sd/doc/USO.md index 25438cfd3..6ed141ddc 100644 --- a/docs/zx81sd/USO.md +++ b/src/arch/zx81sd/doc/USO.md @@ -9,7 +9,7 @@ python -m src.zxbc.zxbc --arch zx81sd -o -M ` es opcional pero muy recomendable: genera el mapa de símbolos (dirección de cada label ASM/BASIC), imprescindible para depurar con un emulador o simular el binario (ver [MAP.md](MAP.md) @@ -30,12 +30,10 @@ en páginas de 8KB y generar un cargador BASIC que las vaya metiendo en la tarjeta SD81 Booster una a una, remapeando el mapeador de memoria entre página y página. -Esto lo hace `split_sd81.py`, una herramienta que vive en el -repositorio complementario de pruebas del port (no en este -repositorio del compilador): +Esto lo hace [`../tools/split_sd81.py`](../tools/split_sd81.py): ``` -python split_sd81.py [PREFIJO] +python src/arch/zx81sd/tools/split_sd81.py [PREFIJO] ``` - `PREFIJO` (opcional) es el nombre base de los ficheros de salida, en @@ -54,7 +52,8 @@ El cargador generado usa `LOAD THEN CLEAR`, `LOAD *MAP` y existen en la ROM original del ZX81). Hace lo siguiente, en orden: 1. Reserva memoria (`CLEAR`) y carga `BOOT1.BIN` (el stage 1, fijo para - todos los programas, también en el repositorio complementario). + todos los programas — fuente en [`../tools/boot1.asm`](../tools/boot1.asm), + binario ya ensamblado en [`../tools/boot1.bin`](../tools/boot1.bin)). 2. Por cada página del binario: mapea el bloque 7 a esa página física (`LOAD *MAP 7,`) y vuelca los datos ahí (`LOAD FAST ... CODE 57344`, la ventana del bloque 7 en `$E000`). diff --git a/src/arch/zx81sd/tools/boot1.asm b/src/arch/zx81sd/tools/boot1.asm new file mode 100644 index 000000000..16217c4de --- /dev/null +++ b/src/arch/zx81sd/tools/boot1.asm @@ -0,0 +1,85 @@ +; =========================================================================== +; bootstrap_stage1.asm — Stage 1 Bootstrap ZX81 + SD81 Booster +; +; Reside en $6000 (bloque 3). El cargador BASIC lo envía allí antes de +; nada porque es una zona neutral: BASIC del ZX81 no la necesita, y aún +; no hemos remapeado ningún bloque. +; +; Secuencia de inicio (ver ZX81-SD81-Adaptation-Plan.md): +; 1. DI — deshabilitar interrupciones (el ZX81 FAST mode ya lo hace, pero +; lo repetimos por seguridad antes de tocar la paginación) +; 2. Activar modo Superfast HiRes Spectrum vía FPGA (POKE 2045 = $07FD) +; 3. Desactivar IO mapeado en memoria (POKE 2056 = $0808) +; 4. Mapear bloque 0 → página 8 (OUT ($E7), page=8, block=0) +; El stage 2 ($0100-$0FFF en página 8) está ahora listo para ejecutar. +; 5. JP $0100 — salta al stage 2 en la RAM recién mapeada +; +; Notas: +; - HFILE=$C000 es el valor por defecto de la FPGA al activar modo 172. +; Si tu hardware requiere configurarlo explícitamente, descomenta el +; bloque HFILE al final. +; - El stage 1 NO inicializa SP; el stage 2 lo hace (ld sp, $7FFF). +; - Bloques 1-5 se mapean en el stage 2 (ya ejecutando desde página 8). +; +; Compilar: +; zxbasm bootstrap_stage1.asm -o bootstrap_stage1.bin +; o con pasmo: +; pasmo --bin bootstrap_stage1.asm bootstrap_stage1.bin +; +; Cargar en el emulador / hardware a dirección $6000 (24576 decimal). +; Ejecutar desde BASIC con: +; RANDOMIZE USR 24576 +; =========================================================================== + + org $6000 + +SD81_STAGE1: + + di ; Interrupciones desactivadas + + ; -------------------------------------------------------------------------- + ; Asignar la direccion del framebuffer (HFILE) + ; high=2044 low=(2043) + ; + + ld hl, $C000 + ld ($07FB), hl + + ; -------------------------------------------------------------------------- + ; ------------------------------------------------------------------ + ; Activar modo Superfast HiRes Spectrum + ; POKE 2045, 172 → ld a, 172 / ld ($07FD), a + ; La FPGA del SD81 Booster interpreta esto como: + ; modo 172 ($AC) = Spectrum 256x192 desde HFILE=$C000 + ; ------------------------------------------------------------------ + ld a, 172 + ld ($07FD), a + + ; ------------------------------------------------------------------ + ; Desactivar IO mapeado en memoria + ; POKE 2056, 0 → ld ($0808), a + ; Evita colisiones entre las instrucciones IN/OUT y el espacio de RAM + ; ------------------------------------------------------------------ + xor a + ld ($0808), a + + ; ------------------------------------------------------------------ + ; Mapear bloque 0 ($0000-$1FFF) → página 8 del SD81 + ; Puerto $E7: modo full 64 páginas, OUT (C), A con B=página, A=bloque + ; La página 8 contiene el binario compilado (vectors + stage 2 + runtime) + ; ------------------------------------------------------------------ + ld b, 8 ; página SD81 destino (8 = primera página de usuario) + ld a, 0 ; bloque Z80 a reasignar (bloque 0 = $0000-$1FFF) + ld c, $E7 ; puerto de paginación SD81 + out (c), a ; mapear (B=página, A=bloque) + + ; ------------------------------------------------------------------ + ; Saltar al stage 2 en la RAM recién mapeada + ; A partir de aquí el bloque 0 contiene la página 8: + ; $0000-$0067 vectores RST + ; $0100 __START_PROGRAM (inicio del stage 2) + ; ------------------------------------------------------------------ + 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 0000000000000000000000000000000000000000..0fc567529aa295b6cf20ef3bc636ee3ce75b37f6 GIT binary patch literal 27 jcmeyY$Z$aEH@n>$qrdFyjW{^iIP4hsp1-X;%)kf$jm8Oe literal 0 HcmV?d00001 diff --git a/src/arch/zx81sd/tools/split_sd81.py b/src/arch/zx81sd/tools/split_sd81.py new file mode 100644 index 000000000..5dfe7db79 --- /dev/null +++ b/src/arch/zx81sd/tools/split_sd81.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +""" +split_sd81.py — Particionador de binario ZX81+SD81 Booster + generador de loader + +Divide el binario plano generado por zxbc.py (--arch zx81sd, -f bin) +en páginas de 8KB para su carga desde el cargador BASIC del ZX81, y genera +el listado BASIC (texto plano, no tokenizado) necesario para cargarlas. + +Mapa de páginas: + Página 8 → bloque 0 ($0000-$1FFF): vectors + stage2 + runtime + código usuario + Página 9 → bloque 1 ($2000-$3FFF): continuación si el binario supera 8KB + Página 10 → bloque 2 ($4000-$5FFF): ídem + Página 11 → bloque 3 ($6000-$7FFF): ídem (stage 1 externo reside aquí antes del salto) + Página 12 → bloque 4 ($8000-$9FFF): sysvars + heap (datos, no ejecutable sin MC45) + Página 13 → bloque 5 ($A000-$BFFF): heap continuación + +El binario empieza en $0000 y no incluye cabecera .tap/.tzx. +Cada fichero de salida se llama P.BIN (mayúsculas) donde N es la página. + +El loader BASIC generado (texto plano, aún no tokenizado a .p) sigue la +secuencia validada manualmente sobre hardware/emulador: + + 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 + +Nota: "LOAD THEN CLEAR", "LOAD *MAP" y "LOAD FAST ... CODE" son extensiones +propias del firmware del SD81 Booster sobre el BASIC del ZX81 (no existen en +la ROM original). El bloque 7 ($E000/57344) se usa como ventana de paginación +temporal para volcar cada página física antes de que BOOT1.BIN haga el mapeo +definitivo de bloques 0-5 vía el puerto $E7. El "LOAD *MAP 7,63" final fuerza +el cambio a modo full-paging (64 páginas) y deja el bloque 7 en un estado +neutro antes de saltar al programa. + +Además del listado en texto plano, genera el .p tokenizado real (listo para +cargar/ejecutar) usando zx81_p_loader.py, un puerto a Python del tokenizador +de EightyOne (zx81BasicLoader.cpp / IBasicLoader.cpp). + +Uso: + python split_sd81.py [salida_base] + + Si no se especifica salida_base, se usa el nombre de entrada sin extensión. + +Ejemplo: + python split_sd81.py test_sd81.bin + → TEST_SD81P8.BIN (primera página, siempre presente) + → TEST_SD81P9.BIN (solo si el binario supera 8 KB) + → ... + → test_sd81_loader.txt (listado BASIC del cargador, texto plano) + → TEST_SD81.P (mismo listado, tokenizado) +""" + +import sys +import os + +from zx81_p_loader import build_p_file + +PAGE_SIZE = 8192 # 8KB por página +FIRST_PAGE = 8 # página SD81 asignada al bloque 0 +CLEANUP_PAGE = 63 # página neutra: fuerza modo full-paging (64 páginas) +BOOT1_FILE = "BOOT1.BIN" +BOOT1_ADDR = 24576 # $6000 +CLEAR_ADDR = 24575 # protege BOOT1.BIN (reservado justo debajo) +LOAD_WINDOW_ADDR = 57344 # $E000, ventana de bloque 7 + + +def split(input_path: str, output_base: str) -> list[str]: + with open(input_path, "rb") as f: + data = f.read() + + if not data: + sys.exit(f"Error: {input_path} está vacío") + + pages_written = [] + page_num = FIRST_PAGE + offset = 0 + + while offset < len(data): + chunk = data[offset : offset + PAGE_SIZE] + + # Rellenar hasta PAGE_SIZE con 0xFF (valor indefinido de FLASH/RAM sin inicializar) + 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(out_path) + print( + f" Pagina {page_num} (bloque {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(page_files: list[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 i, page_file in enumerate(page_files): + page_num = FIRST_PAGE + i + 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() + + # Los nombres de fichero de las paginas y del loader.p se escriben en el + # charset del ZX81 (letras, digitos y unos pocos simbolos: sin guion + # bajo ni otros caracteres no representables). Fallar pronto y claro en + # vez de que reviente mas tarde al tokenizar el 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: el nombre base {output_base!r} contiene {c!r}, " + "no representable en el charset ZX81 (evita '_' y similares)." + ) + + if not os.path.isfile(input_path): + sys.exit(f"Error: no se encuentra {input_path!r}") + + size = os.path.getsize(input_path) + num_pages = (size + PAGE_SIZE - 1) // PAGE_SIZE + print(f"Binario: {input_path} ({size} bytes, {size/1024:.1f} KB, {num_pages} pagina/s)") + print(f"Particionando en paginas de {PAGE_SIZE} bytes (pagina SD81 inicial = {FIRST_PAGE})...") + print() + + pages = split(input_path, output_base) + + print() + print(f"Generados {len(pages)} fichero/s.") + if len(pages) == 1: + print("El programa cabe en una sola pagina (8 KB).") + else: + print(f"Paginas {FIRST_PAGE} a {FIRST_PAGE + len(pages) - 1}.") + + loader_lines = generate_loader_lines(pages) + loader_text = generate_loader_text(loader_lines) + + # El .p es un nombre de fichero real para el ZX81 (charset sin guion + # bajo): se deriva de output_base (mismo prefijo que las paginas), + # concatenado sin separador, igual que "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"Loader BASIC (texto plano): {loader_txt_path}") + print("---") + print(loader_text, end="") + print("---") + print(f"Loader BASIC tokenizado (.p): {loader_p_path} ({len(p_data)} bytes)") + print() + print("NOTA: 'LOAD THEN CLEAR', 'LOAD *MAP' y 'LOAD FAST ... CODE' son extensiones") + print("del firmware SD81 Booster, codificadas con el mismo mecanismo de tokens que") + print("el BASIC estandar del ZX81 (ver zx81_p_loader.py).") + print(f"Copia {BOOT1_FILE}, los ficheros de pagina y {os.path.basename(loader_p_path)}") + print("a la tarjeta SD.") + + +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 100644 index 000000000..75dfb166d --- /dev/null +++ b/src/arch/zx81sd/tools/zx81_p_loader.py @@ -0,0 +1,545 @@ +#!/usr/bin/env python3 +""" +zx81_p_loader.py — Generador de ficheros .p (BASIC tokenizado) para ZX81 + +Puerto directo a Python del algoritmo de tokenización usado por el emulador +EightyOne (zx81BasicLoader.cpp / IBasicLoader.cpp), referencia: + C:\\ClaudeCode\\Eightyone2\\src\\zx81\\zx81BasicLoader.cpp + C:\\ClaudeCode\\Eightyone2\\src\\BasicLoader\\IBasicLoader.cpp + +Los comandos extendidos del firmware SD81 Booster (LOAD THEN CLEAR, LOAD *MAP, +LOAD FAST ... CODE, etc.) no necesitan tratamiento especial: aunque esa +sintaxis no la admite el intérprete de la ROM del ZX81, están compuestos por: + - Tokens estándar del ZX81 (THEN, CLEAR, FAST, CODE, LOAD...) en posiciones + que la ROM no usaría pero que SÍ se pueden teclear (p.ej. SHIFT+3 = THEN). + - Palabras sueltas tras un asterisco (p.ej. *MAP, *VER) que son letras + corrientes, no tokens. +Por tanto basta con tokenizar el texto igual que cualquier BASIC estándar: +el propio algoritmo genérico ya produce la codificación correcta. + +No implementa (no hace falta para este proyecto): etiquetas @label, bloques +numéricos [DEC:...]/[HEX:...]/[BIN:...], secuencias de escape \\xx, códigos +gráficos, ni "alternate keyword spelling". Documentados en IBasicLoader.cpp +si en el futuro hicieran falta. +""" + +import math +import re +import struct + +BLANK = "\x01" # marcador interno: posición ya consumida +NEWLINE = 0x76 +NUMBER_MARK = 0x7E # precede a los 5 bytes de número embebido +DOUBLE_QUOTE = 0xC0 # comilla escapada ("") dentro de una cadena + +# Tokens estándar ZX81 (de zx81BasicLoader::ExtractTokens, sin variantes +# "alternate spelling" ni extensiones ZXpand) +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: + """Puerto de 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"Caracter invalido: {c!r}") + + +def zx81_float(value: float) -> bytes: + """Puerto de zx81BasicLoader::OutputFloatingPointEncoding.""" + exponent = 0 + mantissa = 0 + + if value != 0: + neg = value < 0 + if neg: + value = -value + + exponent = int(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 = int(math.floor(mantissa_val)) + + exponent += 129 + + return bytes([ + exponent & 0xFF, + (mantissa >> 24) & 0xFF, + (mantissa >> 16) & 0xFF, + (mantissa >> 8) & 0xFF, + mantissa & 0xFF, + ]) + + +class _Line: + """Estado de trabajo para una linea BASIC (sin numero de linea), replica + los buffers mLineBuffer / mLineBufferOutput / mLineBufferPopulated. + + Todos los arrays tienen longitud self.n + 1: el ultimo hueco (indice n) + es un relleno de seguridad para cuando un token consume el espacio + artificial añadido al final para detectar tokens sin espacio de cierre + en el texto original (igual que el buffer sobredimensionado de C++). + """ + + def __init__(self, text: str): + content = " " + text # ReadLine antepone siempre un espacio + self.n = len(content) + self.buf = list(content) + [BLANK] # +1 hueco de seguridad + self.out = [BLANK] * (self.n + 1) + self.populated = [False] * (self.n + 1) + # BlankLineStart: sin numero de linea embebido, deja 1 espacio real + self.buf[0] = ' ' + + +def _mask_copy(chars: list) -> list: + return list(chars) + + +def _mask_out_strings(tokenised: list): + """Puerto de 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): + """Puerto de 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): + """Puerto de zx81BasicLoader::ExtractDoubleQuoteCharacters (sin soporte + de REM tokenizado ni alternate spelling, no necesarios aqui).""" + 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): + """Puerto de IBasicLoader::DoTokenise. Recorre los tokens de codigo mas + alto a mas bajo (equivalente al reverse_iterator sobre un std::map), + mutando `tokenised` (busqueda) y line.buf/out/populated (resultado).""" + 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"Bucle de tokenizacion sin fin para {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): + # Coincidencia pegada a un identificador: no es el token, + # se "rompe" localmente para seguir buscando mas adelante. + 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: + """Determina si `index` es el inicio de un literal numerico. + + Difiere deliberadamente del original IBasicLoader::StartOfNumber en un + punto: aqui solo se mira el caracter INMEDIATAMENTE anterior (retrocede + sobre espacios reales pero se detiene tan pronto encuentra CUALQUIER + caracter que no sea espacio, sin seguir retrocediendo mas alla). El + original retrocedia sobre TODOS los espacios consecutivos y comprobaba + el caracter previo a todos ellos, lo cual en BASIC estandar es + equivalente (los tokens siempre consumen su propio espacio final como + BLANK, no como ' ' literal) pero falla en comandos custom del SD81 como + "*MAP 7,8": "MAP" no es un token reconocido, por lo que el espacio tras + el queda como ' ' literal, y el algoritmo original terminaba mirando la + 'P' de MAP (alfabetica) y decidia que "7" NO era un numero. En un ZX81 + real, cualquier digito tecleado tras un espacio genera su propio float + embebido sin importar que palabra le precede, asi que aqui se replica + ese comportamiento (mas simple y mas correcto para este caso). + """ + 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: + """Puerto de IBasicLoader::OutputEmbeddedNumber. Devuelve el nuevo + indice (ya avanzado mas alla del numero, listo para `i += 1` fuera).""" + 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"Numero invalido en posicion {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: + """Tokeniza una linea BASIC (sin numero de linea) y devuelve los bytes + ya codificados (sin NEWLINE final, que se añade en encode_line).""" + line = _Line(text) + n = line.n + + _extract_double_quote_characters(line) + + # Copia de trabajo para deteccion de limites de token: el contenido real + # (sin el hueco de seguridad de line.buf) + un espacio real extra para + # detectar tokens sin espacio de cierre en el texto original. Longitud + # n+1, igual que line.buf, para que los indices sigan alineados. + tokenised = list(line.buf[:n]) + [' '] + + _mask_out_strings(tokenised) + _mask_out_rem_contents(tokenised) + + _do_tokenise(line, tokenised) + + # Rellenar cualquier posicion no consumida con su codigo ZX81 directo + 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: lista de tuplas (numero_de_linea:int, texto:str).""" + return b"".join(encode_line(n, t) for n, t in lines) + + +def build_p_file(lines: list) -> bytes: + """Puerto de zx81BasicLoader::OutputStartOfProgramData + + ProcessLine(*) + OutputEndOfProgramData. Devuelve el contenido completo + del fichero .p (desde 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 bytes vacios) + out_byte(0x76) # PRBUFF (byte 33, NEWLINE) + data.extend([0x00] * 30) # MEMBOT + out_word(0x0000) # SPARE + + # --- Lineas BASIC --- + 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) # display file colapsado (vacio) + + vars_address = START_OF_RAM + len(data) + out_byte(0x80) # fin de VARS (sin 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 => sin autorun) + + return bytes(data) + + +if __name__ == "__main__": + import sys + + # Autotest rapido con el loader validado manualmente + 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"Generado {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}") From 418dd1ffd86cb4303321894f2394f09e822f59fd Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 12:43:52 +0200 Subject: [PATCH 148/169] zx81sd: fix loading instructions and translate documentation to English Fix: the packaged program is loaded with LOAD FAST "" (nothing to select afterwards), not LOAD "" + select. Corrected in USO.md (now USAGE.md) and split_sd81.py's docstring. Translate all zx81sd documentation and tooling to English, including file names: - doc/USO.md -> doc/USAGE.md - doc/PRECAUCIONES.md -> doc/PRECAUTIONS.md - doc/CAMBIOS_BASIC.md -> doc/BASIC_CHANGES.md - doc/MAP.md (kept name, content translated) - README.md (content translated) - tools/split_sd81.py, tools/zx81_p_loader.py, tools/boot1.asm (docstrings/comments translated) - backend/main.py, backend/generic.py (comments translated) Co-Authored-By: Claude Sonnet 5 --- src/arch/zx81sd/README.md | 204 +++-- src/arch/zx81sd/backend/generic.py | 10 +- src/arch/zx81sd/backend/main.py | 130 +-- src/arch/zx81sd/doc/BASIC_CHANGES.md | 368 +++++++++ src/arch/zx81sd/doc/CAMBIOS_BASIC.md | 368 --------- src/arch/zx81sd/doc/MAP.md | 1030 ++++++++++++------------ src/arch/zx81sd/doc/PRECAUCIONES.md | 220 ----- src/arch/zx81sd/doc/PRECAUTIONS.md | 216 +++++ src/arch/zx81sd/doc/USAGE.md | 88 ++ src/arch/zx81sd/doc/USO.md | 87 -- src/arch/zx81sd/tools/boot1.asm | 88 +- src/arch/zx81sd/tools/split_sd81.py | 133 +-- src/arch/zx81sd/tools/zx81_p_loader.py | 167 ++-- 13 files changed, 1559 insertions(+), 1550 deletions(-) create mode 100644 src/arch/zx81sd/doc/BASIC_CHANGES.md delete mode 100644 src/arch/zx81sd/doc/CAMBIOS_BASIC.md delete mode 100644 src/arch/zx81sd/doc/PRECAUCIONES.md create mode 100644 src/arch/zx81sd/doc/PRECAUTIONS.md create mode 100644 src/arch/zx81sd/doc/USAGE.md delete mode 100644 src/arch/zx81sd/doc/USO.md diff --git a/src/arch/zx81sd/README.md b/src/arch/zx81sd/README.md index 890495505..3ed854eec 100644 --- a/src/arch/zx81sd/README.md +++ b/src/arch/zx81sd/README.md @@ -1,113 +1,109 @@ -# Arquitectura `zx81sd`: ZX81 + SD81 Booster +# `zx81sd` architecture: ZX81 + SD81 Booster -`zx81sd` es una arquitectura de este compilador para un **ZX81 real con -la tarjeta [SD81 Booster](https://www.sd81.eu/)** (una interfaz que -añade pantalla tipo Spectrum, sonido AY/beeper, mapeador de memoria por -páginas y acceso a tarjeta SD). +`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). -Todo el código específico de esta arquitectura vive exclusivamente bajo: +All code specific to this architecture lives exclusively under: ``` -src/arch/zx81sd/ (backend del compilador, esta documentación, - y las herramientas de empaquetado en tools/) -src/lib/arch/zx81sd/ (runtime ASM + stdlib BASIC) +src/arch/zx81sd/ (compiler backend, this documentation, + and the packaging tools under tools/) +src/lib/arch/zx81sd/ (ASM runtime + BASIC stdlib) ``` -## Regla de oro del port - -Este compilador es compartido por todas las arquitecturas (zx48k, -zx128k, zxnext...). **El port de zx81sd nunca modifica el frontend ni -la stdlib/runtime compartidos.** El mecanismo de resolución de -`#include`/`#require` busca primero en `src/lib/arch/zx81sd/`, y si no -encuentra el fichero cae automáticamente en `src/lib/arch/zx48k/` (el -fichero compartido). Por eso muchos overrides de zx81sd son copias -completas de la versión zx48k con solo unas pocas líneas cambiadas: hay -que copiar el fichero entero, no un parche — un override parcial -simplemente no existe como concepto aquí. - -Antes de tocar cualquier cosa fuera de `zx81sd/`, para: seguramente hay -una forma de resolverlo con un override. - -## Estado del port - -Funcionalmente completo desde 2026-07-02: FP (RST $28 propio), -gráficos (PLOT/DRAW/arcos/CIRCLE), sonido (BEEP y PLAY sobre AY ZonX y -beeper), teclado (INKEY$/INPUT sobre el teclado físico del ZX81), -joystick, la librería MCU completa (ficheros, RTC/BAT, voz, mapeador de -memoria...) y LOAD/SAVE/VERIFY...CODE contra SD. - -Pendiente / sin auditar, resto de utilidades de pantalla de la stdlib -compartida: - -- `winscroll.bas`: se cree ya portado y probado, pero sin confirmar con - una auditoría formal (no hay override en `zx81sd/stdlib/` — de ser - cierto, es porque no necesitaba ninguno, como `scroll.bas` antes del - fix o `4inarow.bas`). -- `putchars.bas`/`puttile.bas`: sin auditar ni probar. Un vistazo rápido - al fuente no encuentra direcciones de ROM/sysvars del Spectrum - (`putChars` rellena un rectángulo de caracteres, `putTile` coloca un - tile de 16×16 px), así que son buenos candidatos a funcionar sin - cambios, pero no está confirmado. -- `screen.bas`: **sí depende de la ROM** (`$2538`/`$5C65`/`$19E8`, - rutinas y sysvars fijas del Spectrum para leer de vuelta un carácter - de pantalla) — necesitará un override real, no solo una auditoría. -- `print42.bas`/`print64.bas`: **portados** (override completo en - `stdlib/`) — sysvars fijas → equivalentes zx81sd, y las constantes de - base de pantalla/atributos se parchean en tiempo de ejecución - (automodificación de código) en vez de ser fijas. Verificado por - simulación (texto legible píxel a píxel, sin corrupción de memoria); - pendiente de confirmar en el emulador/hardware real. - -Ver [doc/CAMBIOS_BASIC.md](doc/CAMBIOS_BASIC.md) para el patrón general -de este tipo de fix. El port de `maskedsprites.bas`/MSFS (sprites -enmascarados sobre el mapeador de memoria) también está en proceso, aún -sin terminar de funcionar bien. - -## Documentación - -- **[doc/USO.md](doc/USO.md)** — cómo compilar un programa, empaquetarlo - para el ZX81 y cargarlo desde la tarjeta SD. -- **[doc/PRECAUCIONES.md](doc/PRECAUCIONES.md)** — qué hay que tener en - cuenta al escribir o portar software para esta arquitectura (mapa de - memoria, sysvars, teclado, cosas que NO existen aquí aunque existan - en Spectrum). -- **[doc/CAMBIOS_BASIC.md](doc/CAMBIOS_BASIC.md)** — qué cambios de - fuente BASIC hizo falta para portar cada ejemplo oficial de - `examples/` (con el porqué de cada uno). Punto de partida obligado - antes de portar un ejemplo nuevo: casi siempre es uno de los patrones - ya catalogados ahí. -- **[doc/MAP.md](doc/MAP.md)** — bitácora técnica detallada de todos los - bugs encontrados y corregidos durante el port (runtime ASM, no - fuentes BASIC), con la traza de investigación de cada uno. Es el - documento a consultar cuando algo falla en tiempo de ejecución de - forma que recuerda a un bug ya resuelto. - -## Ejemplos - -Los programas de ejemplo ya adaptados/probados para esta arquitectura -están en [`examples/sd81/`](../../../examples/sd81/) (junto al resto de -`examples/` del compilador). El detalle de qué hubo que tocar en cada -uno, y por qué, está en [doc/CAMBIOS_BASIC.md](doc/CAMBIOS_BASIC.md). - -## Herramientas de empaquetado - -[`tools/split_sd81.py`](tools/split_sd81.py) parte el binario plano -generado por el compilador en páginas de 8KB y genera el cargador `.p` -para el ZX81; [`tools/zx81_p_loader.py`](tools/zx81_p_loader.py) es el -tokenizador BASIC que usa para generarlo; [`tools/boot1.asm`](tools/boot1.asm)/ -[`tools/boot1.bin`](tools/boot1.bin) son el cargador de segunda etapa -(stage 1), fijo para todos los programas. Ver -[doc/USO.md](doc/USO.md) para el flujo completo de compilar → -empaquetar → cargar. El conjunto más amplio de fuentes de depuración/ -diagnóstico usados durante el desarrollo del port (no herramientas, -solo pruebas puntuales) vive en un repositorio complementario privado, -no en este. - -## Repositorios relacionados +## 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`: believed to be already ported and tested, but not + confirmed by a formal audit (no override in `zx81sd/stdlib/` — if + true, it's because it didn't need one, like `scroll.bas` before its + fix, or `4inarow.bas`). +- `putchars.bas`/`puttile.bas`: not audited or tested. A quick look at + the source finds no Spectrum ROM/sysvar addresses (`putChars` fills a + rectangle of characters, `putTile` places a 16×16 px tile), so + they're good candidates to work unchanged, but this isn't confirmed. +- `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** (full override in `stdlib/`) + — fixed sysvars → zx81sd equivalents, and the screen/attribute base + constants are patched at runtime (self-modifying code) instead of + being fixed. Verified by simulation (legible pixel-by-pixel text, no + memory corruption); pending confirmation on the emulator/real + hardware. + +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/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/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)** — - firmware/hardware de la interfaz para la que se ha hecho este port. + the hardware/firmware for the interface this port targets. - **[EightyOne Cross-platform](https://codeberg.org/wilco2009/EightyOne-CrossPlatform)** — - emulador usado durante el desarrollo para probar sin hardware real. -- **[CPM_SD81](https://codeberg.org/wilco2009/CPM_SD81)** — CP/M sobre - el SD81 Booster. + 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/backend/generic.py b/src/arch/zx81sd/backend/generic.py index 196556ddc..f9492c660 100644 --- a/src/arch/zx81sd/backend/generic.py +++ b/src/arch/zx81sd/backend/generic.py @@ -1,6 +1,6 @@ # -------------------------------------------------------------------- # SPDX-License-Identifier: AGPL-3.0-or-later -# ZX81 + SD81 Booster — handler del opcode END +# ZX81 + SD81 Booster — END opcode handler # -------------------------------------------------------------------- from src.arch.interface.quad import Quad @@ -8,11 +8,11 @@ def _end(ins: Quad): - """Secuencia de fin de programa para ZX81 + SD81 Booster. + """End-of-program sequence for ZX81 + SD81 Booster. - No hay ROM ni BASIC al que volver: detiene la CPU de forma segura. - Si END aparece varias veces en el programa (salidas anticipadas), - los casos posteriores generan un JP al primer bloque END emitido. + 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") diff --git a/src/arch/zx81sd/backend/main.py b/src/arch/zx81sd/backend/main.py index c4e1378af..a3d3ecc9a 100644 --- a/src/arch/zx81sd/backend/main.py +++ b/src/arch/zx81sd/backend/main.py @@ -14,47 +14,47 @@ from .generic import _end # --------------------------------------------------------------------------- -# Constantes de arquitectura ZX81 + SD81 Booster +# ZX81 + SD81 Booster architecture constants # --------------------------------------------------------------------------- -# Mapa de memoria (flat, ORG $0000): -# $0000-$00FF vectors.asm — vectores RST + relleno hasta $0100 -# $0100-$0FFF stage 2 bootstrap (prólogo) + rutinas de sistema -# $1000-$7FFF runtime ZX BASIC + código del usuario (28 KB) -# $8000-$80FF sysvars del runtime -# $8100-$BFFF heap + datos del usuario (~15.75 KB) -# $C000-$D7FF bitmap pantalla Spectrum (bloque 6, página dedicada) -# $D800-$DAFF atributos pantalla -# $E000-$FFFF bloque 7 — banking de datos (mapas, sprites...) +# Memory map (flat, ORG $0000): +# $0000-$00FF vectors.asm — RST vectors + 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 # el binario comienza en $0000 (vectors.asm lo rellena hasta $0100) -_STAGE2_ENTRY = 0x0100 # punto de entrada del stage 2 bootstrap +_ORG = 0x0000 # the binary starts at $0000 (vectors.asm pads up to $0100) +_STAGE2_ENTRY = 0x0100 # stage 2 bootstrap's entry point -_HEAP_ADDR = 0x8100 # heap en zona de datos ($8100-$BFFF) +_HEAP_ADDR = 0x8100 # heap in the data area ($8100-$BFFF) _HEAP_SIZE = 0x3EFF # ~15.75 KB -# Páginas SD81 asignadas a cada bloque (las carga el BASIC antes del salto) -# Página 8 → bloque 0 ($0000-$1FFF) ← el stage 1 solo mapea este -# Página 9 → bloque 1 ($2000-$3FFF) ┐ -# Página 10 → bloque 2 ($4000-$5FFF) │ el stage 2 (aquí) mapea estos -# Página 11 → bloque 3 ($6000-$7FFF) │ -# Página 12 → bloque 4 ($8000-$9FFF) │ (datos, no ejecutable sin MC45) -# Página 13 → bloque 5 ($A000-$BFFF) ┘ +# 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), # bloque 1 → página 9 - (2, 10), # bloque 2 → página 10 - (3, 11), # bloque 3 → página 11 - (4, 12), # bloque 4 → página 12 - (5, 13), # bloque 5 → página 13 + (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 ] -_SD81_PAGE_PORT = 0xE7 # puerto mapeador de memoria (modo full: OUT (C), A) -_STACK_TOP = 0x7FFF # pila al tope de la zona ejecutable +_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]: - """Emite OUT (C), A para mapear una página a un bloque (modo full, 64 pág.).""" + """Emits OUT (C), A to map a page to a block (full mode, 64 pages).""" return [ f"ld b, {page}", f"ld a, {block}", @@ -69,13 +69,14 @@ def init(self): OPTIONS(Action.ADD_IF_NOT_DEFINED, name="org", type=int, default=_ORG) - # El backend Z80 generico (super().init(), justo arriba) ya registra - # "heap_size" (4768) y "heap_address" (None) con ADD_IF_NOT_DEFINED, - # asi que un ADD_IF_NOT_DEFINED aqui seria un no-op y el heap acababa - # reservado inline (DEFS) con 4768 bytes dentro de la zona ejecutable. - # Se asigna directamente para que el heap viva en la zona de datos - # $8100-$BFFF; los flags --heap-address/-H de la CLI se aplican - # despues y siguen pudiendo sobreescribir estos valores. + # 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 @@ -97,23 +98,24 @@ def init(self): @staticmethod def emit_prologue() -> list[str]: """ - Prólogo del programa para ZX81 + SD81 Booster. + Program prologue for ZX81 + SD81 Booster. - Estructura del binario generado (ORG $0000): - $0000-$00FF vectors.asm (vectores RST, incluido desde sysvars.asm) - $0100 START_LABEL (entrada del stage 2 bootstrap) - - mapeo de bloques 1-5 a sus páginas definitivas + Structure of the generated binary (ORG $0000): + $0000-$00FF vectors.asm (RST vectors, included from sysvars.asm) + $0100 START_LABEL (stage 2 bootstrap's entry point) + - mapping blocks 1-5 to their final pages - SP = $7FFF - - CALL (SD81_INIT_SYSVARS, etc.) + - CALL <#init routines> (SD81_INIT_SYSVARS, etc.) - JP __MAIN_LABEL__ - El stage 1 bootstrap (externo, en el cargador BASIC a $6000) ya ha: - - Configurado HFILE=$C000 y activado modo Spectrum (POKE 2045, 172) - - Desactivado el IO mapeado en memoria (POKE 2056) - - Desactivado interrupciones (DI) - - Mapeado bloque 0 → página 8 (JP $0100 ya ejecuta en RAM limpia) + 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) """ - # -- Definiciones del heap ------------------------------------------ + # -- Heap definitions ------------------------------------------ heap_init = [f"{common.DATA_LABEL}:"] if common.REQUIRES.intersection(common.MEMINITS) or f"{NAMESPACE}.__MEM_INIT" in common.INITS: @@ -143,17 +145,17 @@ def emit_prologue() -> list[str]: f"{NAMESPACE}.__LABEL__.ZXBASIC_USER_DATA EQU {common.DATA_LABEL}" ) - # -- Tabla de vectores RST ($0000-$00FF) ---------------------------- - # Debe ser lo primero en el binario. Cada RST ocupa 8 bytes. - # Usamos org absoluto para cada entrada: el ensamblador rellena los - # huecos con ceros, lo que es correcto para una zona no ejecutable. + # -- 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("jp $0100") # $0000: reset / RST 0 -> stage 2 output.append("org $0008") - output.append("di") # $0008: RST $08 (error handler Spectrum) + output.append("di") # $0008: RST $08 (Spectrum error handler) output.append("halt") output.append("org $0010") - output.append("di") # $0010-$0037: RSTs no usados + output.append("di") # $0010-$0037: unused RSTs output.append("halt") output.append("org $0018") output.append("di") @@ -162,17 +164,17 @@ def emit_prologue() -> list[str]: output.append("di") output.append("halt") output.append("org $0028") - output.append("jp .core.FP_CALC_ENTRY") # $0028: RST $28 -> calculador FP propio + 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") - output.append("di") # $0038: RST $38 IM1 — DI permanente, nunca llega + output.append("di") # $0038: RST $38 IM1 — permanent DI, never reached output.append("halt") output.append("org $0066") - output.append("retn") # $0066: NMI desactivada, pero el vector debe existir + output.append("retn") # $0066: NMI disabled, but the vector must exist - # -- Stage 2 bootstrap en $0100 ------------------------------------ + # -- Stage 2 bootstrap at $0100 ------------------------------------ output.append(f"org {_STAGE2_ENTRY}") output.append(f"{common.START_LABEL}:") @@ -180,19 +182,19 @@ def emit_prologue() -> list[str]: output.extend(heap_init) return output - # Mapeo de bloques 1-5 a sus páginas definitivas. - # El bloque 0 ya fue mapeado a la página 8 por el stage 1 externo. + # 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)) - # Pila al tope de la zona ejecutable + # Stack at the top of the executable area output.append(f"ld sp, {_STACK_TOP:#06x}") - # Llamadas a rutinas de inicialización registradas con #init - # (SD81_INIT_SYSVARS y cualquier otra del runtime incluido) + # 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)) - # Salto al programa del usuario + # Jump to the user's program output.append(f"jp {common.MAIN_LABEL}") output.extend(heap_init) diff --git a/src/arch/zx81sd/doc/BASIC_CHANGES.md b/src/arch/zx81sd/doc/BASIC_CHANGES.md new file mode 100644 index 000000000..13dde89f5 --- /dev/null +++ b/src/arch/zx81sd/doc/BASIC_CHANGES.md @@ -0,0 +1,368 @@ +# 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. + +--- + +## 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`. Still pending confirmation on the +emulator/real hardware. + +--- + +## 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 | +| 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 | + +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). diff --git a/src/arch/zx81sd/doc/CAMBIOS_BASIC.md b/src/arch/zx81sd/doc/CAMBIOS_BASIC.md deleted file mode 100644 index 56eaa17fa..000000000 --- a/src/arch/zx81sd/doc/CAMBIOS_BASIC.md +++ /dev/null @@ -1,368 +0,0 @@ -# Cambios en fuentes BASIC para portar ejemplos oficiales a zx81sd - -Este fichero recopila, para cada ejemplo oficial de `examples/` que se ha -probado en zx81sd, si hizo falta tocar el fuente BASIC (nunca el original: -siempre una copia en `examples/sd81/`) o solo cambiar las flags de -compilación. - -Regla de fondo: el fuente oficial de zxbasic **nunca se modifica**. Cuando -un programa depende de una sysvar o dirección absoluta específica del -Spectrum, se hace una copia adaptada junto al original. - ---- - -## 1. `examples/english/comecoquitos.bas` — SIN cambios de fuente - -Compila tal cual. Solo necesita flags de línea de comandos: - -``` -python -m src.zxbc.zxbc comecoquitos.bas --arch zx81sd --string-base 1 --array-base 1 -o comecocos.bin -``` - -**Por qué**: el fuente usa indexación de strings 1-based (estilo Sinclair -BASIC clásico, `l$(f)`, slicing). Con la base 0 por defecto de zxbasic, -las comparaciones de colisión salen desplazadas una posición — no es un -bug del port, en zx48k pasa exactamente igual sin esas flags. - ---- - -## 2. `examples/english/snake_en.bas` → `examples/sd81/snake_sd81.bas` - -Un solo cambio, una línea: - -```diff --73 POKE UINTEGER 23675, @udg(0, 0): REM Sets UDG variable to first element -+73 POKE UINTEGER $8002, @udg(0, 0): REM sysvar UDG de zx81sd (en Spectrum era 23675) -``` - -**Por qué**: `23675` ($5C7B) es la dirección absoluta del sysvar `UDG` en -el mapa de memoria del **Spectrum**. En zx81sd el mismo sysvar vive en -`$8002` (ver `SYSVAR_BASE+2` en `src/lib/arch/zx81sd/runtime/sysvars.asm`). -Sin el cambio, el POKE cae en RAM libre y los UDGs (cabeza y fruta de la -serpiente) nunca se activarían — el juego funcionaría pero pintaría -espacios en vez de los gráficos. - -**Patrón general**: cualquier ejemplo que haga `POKE`/`PEEK` a una -dirección de sysvar del Spectrum en vez de usar la función de más alto -nivel (aquí habría bastado con no usar UDG a pelo) necesita este tipo de -traducción de dirección. Ver también flights.bas más abajo. - ---- - -## 3. `examples/flights.bas` → `examples/sd81/flights_sd81.bas` - -Tres cambios de contenido (más limpieza cosmética de espacios finales sin -efecto funcional, no listada): - -### 3.1 Eliminar un POKE de sysvar del Spectrum sin equivalente - -```diff --1 POKE 23658,8: BORDER 1: PAPER 1: INK 7: CLS -+1 BORDER 1: PAPER 1: INK 7: CLS -``` - -**Por qué**: `23658` ($5C6A) es `REPDEL` (retardo de repetición de tecla) -en el Spectrum — no existe ese sysvar en zx81sd, y el POKE escribiría -sobre una dirección de RAM arbitraria. Se elimina sin más: el efecto -(ajustar el auto-repeat del teclado) no tiene equivalente ni falta le -hace al juego. - -### 3.2 Traducir el sysvar COORDS a su dirección real en zx81sd - -```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 -``` -(y tres apariciones más idénticas en las líneas 2370, 2378 y 2445) - -**Por qué**: `23677`/`23678` ($5C7D/$5C7E) son el sysvar `COORDS` -(última coordenada de `PLOT`) del Spectrum. En zx81sd vive en `$8004`/ -`$8005` (`COORDS EQU SYSVAR_BASE+$04` en `sysvars.asm`). Sin la -traducción, los `PEEK` leen RAM del programa compilado en vez de la -coordenada real, y el `DRAW` de la línea del horizonte sale con un -desplazamiento aleatorio dependiente del contenido de esa RAM — este fue -el primer síntoma reportado ("pinta mal la línea del horizonte"). - -### 3.3 Comparaciones de tecla a minúsculas - -```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 -``` -(y las comparaciones de "Y"/"N" en las líneas 6150/6160) - -**Por qué (este cambio corresponde al esquema de teclado antiguo — ver -nota de "ya no es necesario" más abajo)**: en el momento de portar -`flights.bas`, `zx81sd/runtime/io/keyboard/keyscan.asm` (reescaneo a -mano del **teclado físico del ZX81**, 40 teclas, ya que el SD81 Booster -no tiene teclado Spectrum) solo devolvía minúscula siempre, sin importar -si se pulsaba `SHIFT` o no — `SHIFT+letra` no producía la mayúscula de -esa letra, sino uno de los símbolos clásicos del ZX81 (dos puntos, -comillas, `+`, `-`...). Con ese esquema no existía combinación física -que reprodujera "CAPS SHIFT+S" al estilo Spectrum, y la única -adaptación posible era comparar contra minúscula sin shift. Mismo -motivo, ya resuelto antes por otra vía en `comecoquitos.bas` (ahí el -propio fuente ya comparaba en minúscula, "o","p","q","a","y","n" — -coincidencia de que su autor tecleó así, no algo que tuviéramos que -tocar) y en `snake_sd81.bas` (compara "O"/"o" con `OR`, cubriendo ambos -casos sin necesidad de tocarlo). - -**Ya no es necesario (2026-07-04)**: `keyscan.asm` se rediseñó para que -la pulsación directa de una letra dé minúscula y `SHIFT+letra` dé la -MAYÚSCULA de esa letra (además de `SHIFT+"2"` como CAPS LOCK persistente -y los símbolos clásicos del ZX81 alcanzables con `"."+tecla` desde -`INPUT()`). Con el esquema actual, `INKEY$="S"` ya funcionaría de forma -natural pulsando `SHIFT+S`, exactamente como en un Spectrum — el cambio -de caso de `flights_sd81.bas` documentado arriba fue necesario en su -momento pero ya no lo sería si se portara hoy desde cero. Se deja -constancia aquí en vez de revertir la copia ya probada. Detalle completo -del rediseño en [MAP.md](MAP.md), sección "Esquema de teclado nuevo". - ---- - -## 4. `examples/english/4inarow.bas` — SIN cambios de fuente - -Compila con flags por defecto: - -``` -python -m src.zxbc.zxbc 4inarow.bas --arch zx81sd -o row4.bin -``` - -Sus arrays usan índices 1..8 que caben en base 0 por defecto sin -colisión de slicing (no hace slicing de strings), y sus comparaciones de -tecla ya usan minúsculas ("y"/"n" en los prompts). Ejercita el código de -arcos (`DRAW 8,0,PI`) y `CIRCLE` sin necesitar ningún parche. - ---- - -## 5. `examples/scroll.bas` — SIN cambios de fuente - -Compila con flags por defecto: - -``` -python -m src.zxbc.zxbc scroll.bas --arch zx81sd -o scroll.bin -``` - -El problema no estaba en el ejemplo sino en la librería: `scroll.bas` no -tenía versión zx81sd — se usaba tal cual la de zx48k, cuyas 8 subs -(`ScrollRight/Left/Up/Down` + variantes `*Aligned`) llaman a `$22AC` -(rutina *PIXEL-ADD* de la ROM del Spectrum, inexistente en zx81sd). Se -creó `src/lib/arch/zx81sd/stdlib/scroll.bas`, idéntica salvo sustituir -esas 8 llamadas por `call PIXEL_ADDR` (nuestra propia implementación, -mismo contrato de registros que la ROM — ver [MAP.md](MAP.md)). El -ejemplo en sí no necesitó ningún cambio, igual que 4inarow.bas. - ---- - -## 6. `examples/maskedsprites.bas` → `examples/sd81/maskedsprites_sd81.bas` - -**En proceso — aún no funciona del todo bien.** - -Un cambio de fondo: `WaitForNewFrame` (definida en el propio ejemplo, no -en la librería) reescrita para no depender de interrupciones: - -```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 ; garantiza al menos un frame esperado (sustituye 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 -``` - -**Por qué**: `23672` es el contador `FRAMES` de la ROM del Spectrum, -incrementado automáticamente por la interrupción IM1 de 50Hz. El -original hace un `HALT` inicial (espera a la interrupción) y luego un -bucle que confía en que la interrupción lo siga incrementando en segundo -plano mientras el bucle solo *comprueba* sin volver a esperar. En -zx81sd no hay interrupciones (DI permanente; el vector `$0038` es una -trampa `DI;HALT`, no un manejador real) — ese `HALT` no despertaría -nunca. Se sustituye por `VSYNC_TICK` (sondeo del contador de pulsos -VSYNC hardware del SD81 Booster por puerto, la misma rutina que ya usa -`PAUSE`), llamada explícitamente en cada vuelta que haga falta esperar. - -**Actualización — MSFS portado al mapeador de zx81sd (bloque 7)**: el -riesgo de arriba ya no aplica. Se creó -`src/lib/arch/zx81sd/stdlib/cb/maskedsprites.bas` (override completo de -la librería compartida, que sigue intacta): `SetBankPreservingINTs`/ -`GetBankPreservingRegs` reescritas en ASM a mano sobre el puerto `$E7` -(bloque 7, `$E000-$FFFF` — reservado en nuestro mapa de memoria para -"banking de datos, mapas, sprites"), y `MaskedSpritesFileSystemStart` -fija en `$E000` en vez de "lo que quede hasta `$FFFF`" (que asumía RAM -plana Spectrum). El resto de MSFS (cientos de líneas de álgebra de -bloques) es agnóstico de banco/dirección y se copió sin tocar una línea. -Detalle completo, incluido un bug real cazado en el proceso (registros -no preservados si se escriben en BASIC en vez de ASM), en [MAP.md](MAP.md). - -Pese a los fixes de arriba (confirmados con simulación y, en parte, en -emulador/hardware real), la librería en su estado actual **sigue sin -funcionar del todo bien** — trabajo en curso, no dar por cerrado este -ejemplo todavía. - ---- - -## 7. `examples/sd81/pong.bas` — transcripción clásica, un cambio ASM - -No es un ejemplo oficial de `examples/` sino una transcripción clásica -de Pong al estilo Sinclair BASIC (GOSUB/números de línea), añadida -directamente en `examples/sd81/`. Usa comparaciones de tecla ya en -minúscula ("4","q","3","a"), coincidentes con el esquema de teclado -actual sin necesidad de tocar nada — el único cambio fue sustituir la -sincronización de pantalla: - -```diff - ASM -- call VSYNC_TICK ; garantiza al menos un frame esperado (sustituye EI+HALT) -+ call .core.VSYNC_TICK ; garantiza al menos un frame esperado (sustituye EI+HALT) - ; halt ; Avoids screen flickering - END ASM -``` - -**Por qué**: `vsync.asm` envuelve `VSYNC_TICK` en -`push namespace core ... pop namespace`; al llamarlo desde un bloque -`ASM ... END ASM` que no está dentro de ese namespace hace falta el -prefijo completo `.core.VSYNC_TICK` — mismo patrón de error que en -`maskedsprites_sd81.bas` (ver [PRECAUCIONES.md](PRECAUCIONES.md), -sección 5). Sin el prefijo, el compilador da -`Undefined GLOBAL label '.VSYNC_TICK'`. - ---- - -## 8. `examples/sd81/block7test.bas` — sin equivalente oficial, prueba de mapeador - -Programa mínimo (no transcripción de ningún ejemplo) escrito para -confirmar, de forma aislada, que el mapeador de memoria del SD81 Booster -provee almacenamiento independiente por página: escribe patrones -distintos en las páginas 20 y 63 del bloque 7 (`$E000-$FFFF`) vía -`Map()` y verifica que no se pisan entre sí. Sirvió para descartar el -mapeador como causa durante la investigación del bug de MSFS (ver -[MAP.md](MAP.md)). Se conserva como ejemplo de referencia de uso directo -de `Map()`/`MapGet` sobre el bloque 7. - ---- - -## 9. `print42.bas`/`print64.bas` — librerías nuevas (sin cambios de ejemplo) - -`stdlib/print42.bas` y `stdlib/print64.bas` (Britlion) implementan un -`PRINT` de 42/64 columnas dibujando pixel a pixel, en vez de usar el -`PRINT` normal de 32 columnas. La versión zx48k depende de la ROM del -Spectrum en varios sitios: - -- `print42.bas`: `ld hl,$5C7B` (sysvar `UDG`), `ld de,15360` - (`CHARS-256` fijo) y `ld a,(23693)` (sysvar `ATTR_P`). -- `print64.bas`: solo `ld a,(23693)` (`ATTR_P`) — su charset de 4px es - propio, no usa `UDG`/`CHARS`. -- Ambos, además, sitúan la pantalla y los atributos con constantes - **inmediatas** de base fija (`add a,64` / `add a,88`, los bytes altos - de `$4000`/`$5800`) en vez de leer un puntero — funciona en el - Spectrum porque esas direcciones son literales de ROM, pero en zx81sd - `SCREEN_ADDR`/`SCREEN_ATTR_ADDR` son variables (bloque 6, `$C000`/ - `$D800`). - -Se creó `src/lib/arch/zx81sd/stdlib/print42.bas` y `print64.bas` -(overrides completos): sysvars fijas → sus equivalentes zx81sd -(`UDG`/`CHARS`/`ATTR_P`, ver `sysvars.asm`), y las dos constantes de -base de pantalla/atributos se **parchean una vez al entrar en la -función** (automodificación de código sobre la propia instrucción -`ADD A,n`), leyendo el byte alto real de `SCREEN_ADDR`/ -`SCREEN_ATTR_ADDR` en ese momento — igual que hacía la ROM original con -sus propias rutinas, solo que con un valor variable en vez de fijo. - -**Bug real cazado durante el port** (no en el ejemplo, en la librería -misma): un `#include once ` puesto ingenuamente al -principio del fichero (nivel BASIC, fuera de cualquier bloque `ASM`) -producía `error: illegal preprocessor character`, y puesto dentro del -bloque `ASM` de la función compilaba pero colgaba el programa en -tiempo de ejecución (`HALT` en una dirección de código basura). Causa: -si esta librería es la **primera** en incluir `sysvars.asm` en todo el -programa, arrastra tras de sí `bootstrap.asm`/`charset.asm` — y este -último hace `INCBIN "specfont.bin"` (el font completo, no texto -ensamblador) — justo en ese punto del fichero fuente. Puesto a nivel -BASIC, el lexer de BASIC intenta tokenizar bytes binarios de fuente -como si fueran texto (de ahí el "illegal preprocessor character"). -Puesto dentro de la función, el binario del font se emite literalmente -en medio del cuerpo compilado de la función, y la CPU lo "ejecuta" -como si fueran instrucciones en cuanto el flujo cae ahí. Fix: no -incluir `sysvars.asm` en absoluto desde estos ficheros — confían en que -ya esté incluido por el resto del runtime (`CLS`/`PRINT`, que cualquier -programa que use `print42`/`print64` va a usar también), y solo -referencian los símbolos ya namespaced (`.core.CHARS`, etc.). Detalle -completo en [PRECAUCIONES.md](PRECAUCIONES.md) y [MAP.md](MAP.md). - -Verificado por simulación (arnés Python de siempre): las dos cadenas de -prueba (`tests_debug/print4264.bas`, repositorio complementario) se -renderizan legibles píxel a píxel en la posición esperada, sin -escrituras fuera de pantalla/atributos/variables propias del fichero, y -el programa llega a su `__END_PROGRAM`/`HALT` normal. Pendiente de -probar en el emulador/hardware real. - ---- - -## Resumen para el manual - -| Ejemplo | Copia en zx81sd | Cambios de fuente | Flags de compilación | -|---|---|---|---| -| comecoquitos.bas | no hace falta copia si no se toca el original | ninguno | `--string-base 1 --array-base 1` | -| snake_en.bas | `snake_sd81.bas` | 1 línea (dirección UDG) | ninguna especial | -| flights.bas | `flights_sd81.bas` | 3 tipos de cambio (POKE eliminado, 4× PEEK COORDS, 8× case de tecla) | ninguna especial | -| 4inarow.bas | no hace falta copia | ninguno | ninguna especial | -| scroll.bas | no hace falta copia | ninguno (el fix fue en la librería `stdlib/scroll.bas`) | ninguna especial | -| maskedsprites.bas | `maskedsprites_sd81.bas` | `WaitForNewFrame` reescrita (EI+HALT → VSYNC_TICK); librería `cb/maskedsprites.bas` portada al mapeador (bloque 7) — **en proceso, aún no funciona del todo bien** | ninguna especial | -| pong.bas (no oficial) | `pong.bas` en `examples/sd81/` | 1 línea ASM (namespace de VSYNC_TICK) | ninguna especial | -| block7test.bas (no oficial) | `block7test.bas` en `examples/sd81/` | n/a (escrito directamente para zx81sd) | ninguna especial | -| print42.bas/print64.bas (librerías) | no aplica (no son ejemplos) | ninguno — fix fue en `stdlib/print42.bas`/`print64.bas` | ninguna especial | - -Patrón identificado para futuros ejemplos: - -1. Buscar `POKE`/`PEEK` a literales numéricos: casi siempre es una - sysvar del Spectrum que hay que traducir a su dirección zx81sd - equivalente (ver `src/lib/arch/zx81sd/runtime/sysvars.asm`). -2. Comparaciones de `INKEY$` contra mayúsculas de teclas de control - (`"S"`, `"Q"`, etc.): con el esquema de teclado **actual** - (pulsación directa = minúscula, `SHIFT+letra` = mayúscula) esto ya - funciona igual que en un Spectrum y **no hace falta tocar nada** — - el cambio a minúscula documentado arriba para `flights.bas` - corresponde a una versión anterior de `keyscan.asm` (ver nota "Ya no - es necesario" en la sección 3.3) y se mantiene aquí solo como - registro histórico de esa copia ya generada. -3. Código ASM inline que llame a rutinas envueltas en - `push namespace core` (como `VSYNC_TICK`) necesita el prefijo - `.core.` si el bloque `ASM` que lo invoca no está ya dentro de ese - namespace (ver caso de `pong.bas` arriba). -4. Si una librería nueva necesita sysvars propios (`CHARS`/`UDG`/ - `ATTR_P`/`SCREEN_ADDR`/...), **no hacer `#include once ` - dentro del fichero** — solo referenciar los símbolos con el prefijo - `.core.`, confiando en que el resto del runtime (`CLS`/`PRINT`) ya lo - incluyó. Ver caso de `print42.bas`/`print64.bas` arriba y - [PRECAUCIONES.md](PRECAUCIONES.md). diff --git a/src/arch/zx81sd/doc/MAP.md b/src/arch/zx81sd/doc/MAP.md index 2ffd4e9f1..758cb67b6 100644 --- a/src/arch/zx81sd/doc/MAP.md +++ b/src/arch/zx81sd/doc/MAP.md @@ -1,529 +1,533 @@ -# Mapa de tests de depuración (sesión DRAW3 / arco) +# Debug test log (DRAW3 / arc session) -Nota sobre rutas: esta bitácora se escribió originalmente en el -repositorio complementario de pruebas del port (donde los `.bas` citados -vivían todos en un directorio `tests_debug/`). Los ejemplos que se -consideraron suficientemente maduros para publicarse ya están copiados -en este repositorio, en [`examples/sd81/`](../../../../examples/sd81/) +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` — ver [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md)). -El resto de fuentes de depuración puntual mencionadas aquí (`diag1-6`, -`t_arc*`, `trig_test`, `heaptest`, `keytest`...) siguen solo en el -repositorio complementario, no en este. +`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. -Compilación de cada uno (desde la raíz de este repositorio): +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 +python -m src.zxbc.zxbc examples\sd81\.bas --arch zx81sd -o .bin +python src\arch\zx81sd\tools\split_sd81.py .bin ``` -(ver [USO.md](USO.md) para el detalle del empaquetador.) +(see [USAGE.md](USAGE.md) for the packager's detail.) -| Fuente | Prefijo SD81 | Qué prueba | Resultado obtenido | +| Source | SD81 prefix | What it tests | Result obtained | |------------------------|--------------|---------------------------------------------------------------------|---------------------| -| `str_test.bas` | STRTEST | PRINT/STR$ de FLOAT (Fase 3) | OK: 7 / 7.5 / -3.25 / 0 / 123.5 / -0.5 | -| `trig_test.bas` | TRIGTST | SQR/SIN/COS/EXP/LN/ATN con argumentos triviales (Fase 4) | OK: 3 / 1.41421 / 0 / 1 / 2.71828 / 1 / 3.14159 | -| `trig2_test.bas` | TRIG2 | SIN/COS con argumentos NO triviales (1, 1.5708, 3.14159) | OK: 0.8147(4?) / 0.5403 / 0.99999 / -0.99999 / 0 | -| `draw_arc_test.bas` | DRAWARC | DRAW+DRAW+CIRCLE combinados (primer intento de arco) | Solo aparece una línea vertical, CIRCLE nunca se ve | -| `t_line.bas` | TLINE | `DRAW 20,0` (2 args, sin ángulo, sin FP) | OK: línea horizontal | -| `t_circle.bas` | TCIRC | `CIRCLE 60,30,20` sola | OK: círculo correcto | -| `t_arc.bas` | TARC | `DRAW 20,0,3.14159` (arco solo, offset horizontal, 180°) | BUG: línea vertical (no horizontal, no arco) | -| `t_arc2.bas` | TARC2 | `DRAW 30,10,1.5708` (offsets asimétricos, 90°) | BUG: línea vertical con leve inclinación a la derecha | -| `t_arc3.bas` | TARC3 | `PLOT 100,96` + `DRAW 20,0,3.14159` (centrado, con margen) | BUG: patrón de varias líneas en estrella/caóticas desde el centro; una se sale de pantalla hacia el área de atributos | -| `diag1.bas` | DIAG1 | ASM inline: llama a CD-PRMS1 (L247D) directamente con z=40, A=pi/2, e imprime mem-1/mem-3/mem-4/mem-0/nº de líneas | Pantalla en blanco — bug del propio test (`#include` dentro de `ASM` mete código ejecutable en el flujo lineal). Sustituido por diag2 | -| `diag2.bas` | DIAG2 | Igual que diag1 pero sin includes manuales: variables BASIC + PRINT normal | OK: valores exactos esperados → CD-PRMS1 numéricamente correcto | -| `diag4.bas` | DIAG4 | Con `-D DRAW3_DEBUG`: imprime solo N (nº de segmentos capturados en el hook de draw3.asm) | OK: 16 → el hook de captura funciona | -| `diag5.bas` | DIAG5 | diag4 + bucle de recorrido del buffer (2 páginas SD81) | No imprimía; descartado, sustituido por diag6 (1 página) | -| `diag6.bas` | DIAG6/DIAG7 | diag4 + bucle, 1 página. Su volcado de $152D + breakpoints de escritura en $8004 localizaron el bug | OK: 16/16. DIAG7 = idéntico recompilado tras el fix | -| `arcfix.bas` | ARCFIX | Test de referencia idéntico al Spectrum real: `PLOT 100,100` + 2 DRAW arco + CIRCLE | **OK tras el fix**: gancho + círculo, igual que la foto del Spectrum real | - -## Bug de DRAW3 (arco) — RESUELTO - -**Causa raíz** (nada que ver con el calculador FP, cuya matemática resultó -ser exacta): `src/lib/arch/zx81sd/runtime/pixel_addr.asm` destruía el -registro **D** (lo usaba como scratch para V=191−Y). Pero `draw.asm` -(heredado de zx48k) salva la coordenada Y del Bresenham en D' alrededor de -la llamada (`ld d,b / call PIXEL_ADDR / ld b,d`), porque PIXEL-ADD ($22AC) -de la ROM Spectrum preserva DE. Resultado: cada línea con componente -vertical arrancaba internamente con Y=191−y1, corrompiendo COORDS y el -trazado. Las líneas horizontales (t_line) y CIRCLE (que no pasa por ese -camino) salían bien, lo que despistó la investigación inicial. - -**Fix**: PIXEL_ADDR reescrito para usar B como scratch (ya se destruía, -igual que en la ROM) y preservar D y E. Verificado en hardware con ARCFIX -(idéntico al resultado del Spectrum real) y DIAG7. - -**Método de localización**: hook `#ifdef DRAW3_DEBUG` en draw3.asm (activado -con `-D DRAW3_DEBUG`) que captura por cada segmento: |Dy|,|Dx|,signos y -COORDS previas en un buffer (`DRAW3_DEBUG_BUF`); volcado de memoria del -buffer en el debugger de EightyOne + breakpoints de escritura en COORDS -($8004/$8005). El volcado demostró que la posición FP acumulada era -perfecta y que COORDS quedaba mal tras cada línea → el bug estaba en -__DRAW/PIXEL_ADDR, no en fp_calc.asm ni draw3.asm. - -## Ficheros fuente modificados/creados en esta sesión (motor FP + arco) - -- `src/lib/arch/zx81sd/runtime/fp_calc.asm` — Fases 1-5 (motor CALCULATE, - trig/log/exp/sqrt, y ahora STK-TO-A/STK-TO-BC/CD-PRMS1 para el arco). - También se le añadió `#include once ` (bug independiente: - se incluye siempre en todo binario zx81sd, así que debía bastarse a - sí mismo). -- `src/lib/arch/zx81sd/runtime/draw3.asm` — NUEVO. Override de - `zx48k/runtime/draw3.asm` que sustituye las llamadas a direcciones - ROM fijas por las rutinas portadas en `fp_calc.asm`. Incluye - instrumentación de traza tras `#ifdef DRAW3_DEBUG` (inactiva por - defecto; se activa compilando con `-D DRAW3_DEBUG`). -- `src/lib/arch/zx81sd/runtime/pixel_addr.asm` — FIX del bug del arco: - ahora preserva D y E (antes destruía D, rompiendo el Bresenham de - draw.asm en toda línea no horizontal). +| `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` — - de la Fase 3 (PRINT/STR$ de FLOAT), sin cambios en esta sesión. - -## Sonido: BEEP y PLAY (chips AY ZonX del SD81) - -| Fuente | Prefijo SD81 | Qué prueba | Resultado | -|-----------------|--------------|----------------------------------------------------------|-----------| -| `beeptest.bas` | BEEPTS2 | BEEP variable (runtime FP) + BEEP constante (corrección de reloj 13/14 en __BEEPER) | OK: escala + DO/DO' + LA 440 | -| `playtest.bas` | PLAYTS2 | PLAY 3 canales + comparación AY/beeper | OK (¡ojo: notas en minúscula = octava abajo!) | -| `aycal.bas` | AYCAL | Emparejamiento AY vs beeper por semitonos | Sirvió para detectar el desfase | -| `aycal2.bas` | AYCAL2 | Duraciones cronometrables + pareja directa | FFT: beeper 434.5 (correcto, pacing emulador), AY 220 | -| `aycal3.bas` | AYCAL3 | Igual con notas en MAYÚSCULA | OK: unísono 440/440 | - -Lección de la investigación de la "octava fantasma": en el MML de PLAY -(semántica del BASIC 128K), las notas en MINÚSCULA suenan una octava por -debajo de la octava actual. Los tests iniciales usaban minúsculas y el AY -sonaba a 220 Hz *por diseño*. El emulador, la tabla de divisores -(1.625 MHz) y el beeper (3.25 MHz) eran correctos. Se verificó con FFT -sobre la salida de audio grabada. De regalo se corrigió un bug latente -real de EightyOne (el reloj del AY quedaba con el de la tarjeta del -diálogo de hardware en vez del ZonX forzado por el SD81: faltaba llamar a -Sound.InitDevices() tras forzar machine.aytype). - -## Librería MCU (SD81 Booster) — `zx81sd/stdlib/mcu.bas` + `joy.bas` - -| Fuente | Prefijo SD81 | Qué prueba | Resultado | + 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) + validación local + eco INKEY$ | OK | -| `mcutest.bas` | MCUTST | VERSION, GET/SETBYTE, PWD, SAVE+LOAD+verificación, DEL, FREE, RTC, BAT, DIR, AY2 por registros, AyPlay | pendiente | -| `maptest.bas` | MAPTST | `Map(bloque,pagina)`/`MapGet` — mapeador $E7: firmas en 2 páginas conmutando el bloque 5 y verificación | OK | -| `exttest.bas` | EXTTST | Extensiones no-MCU: `HexPoke` (*HEX), `MemMove` (*LDIR/*LDDR, stdlib), `StrInv`/`StrBold` (*INV/*BOLD) | OK | -| `ftest.bas` | FTEST | Handles F_*: SAVE, F_OPEN_ZX81 (cmd 58), F_SEEK, F_READ con verificación, F_WRITE+relectura, F_CLOSE, DEL | OK | -| `lstest.bas` | LSTEST | Statements LOAD/SAVE/VERIFY ... CODE nativos → SD (override runtime load.asm/save.asm, cmd 9/10): SAVE+LOAD+verificación, VERIFY ok/corrupto (ERR 26), fichero inexistente (ERR 26) | OK | - -Arquitectura: `mcu.bas` contiene las primitivas del protocolo en ASM -(McuSend/McuRecv/McuSendBlock/McuRecvBlock — las *Block son el camino -crítico de LOAD/SAVE/F_READ/F_WRITE) y los wrappers de todos los -comandos del manual (sistema, ficheros, handles F_*, hardware, voz, -AY2/VGM/PEG, RTC/BAT). Conversión ASCII↔ZX81 automática en los comandos -de texto. `joy.bas` es una capa fina sobre `mcu.bas`. - -Notas de protocolo (extraídas de SD81Booster.cpp del emulador): -- Tras CADA operación en $A7 se espera el cambio del bit 7 de $AF. - NUNCA escribir en $AF (reset del MCU). -- Strings Z80→MCU: byte de longitud + datos (el MCU convierte ZX81→ASCII - salvo comandos "raw": JOY, BINARY_SAY, F_OPEN). -- Streams MCU→Z80 (PWD/DIR/TYPE/FREE_TXT): pedir cada carácter - escribiendo CMD_NEXTCH ($0D); fin = EOT ($6F); después llega el status. -- Respuestas de longitud fija (LOAD, FREE, RTC, BAT, F_READ): ráfaga - de bytes leyendo $A7 con espera de reloj entre cada uno. -- F_OPEN: confirmado en el firmware (COMMANDS.cpp) que el MCU asigna el - handle y lo devuelve (el manual estaba mal y se ha corregido). Añadido - F_OPEN_ZX81 (58) a la librería y al emulador. -- OPENDIR/GETROWLEN/GETROW (16-18) no están emulados en EightyOne: - probarlos solo en hardware real. - -## Test de integración: comecoquitos.bas (ejemplo oficial de zxbasic) - -| Fuente | Prefijo SD81 | Qué prueba | Resultado | +| `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 | Juego completo de 1985: FP, strings/slices, arrays, UDGs, bloques gráficos, color/FLASH/BRIGHT, INKEY$, BEEP, RND | OK — idéntico al .tap de Spectrum | -| `fpleak.bas` | FPLEAK | Detector de fugas de la pila FP por bloques (lee $8024 tras cada idiom) | Sirvió para acotar; la "fuga" era corrupción por UDGs | -| `blocktest.bas` | BLKTST | Los 16 gráficos de bloque CHR$(128)-143 | OK tras el fix de PO_GR_1 | - -Tres bugs del runtime cazados con este juego (commits 9903c866 y 9a24059e): -1. UDG apuntaba 128 bytes más allá del final de la fuente (96 chars, no - 256): los POKE USR CHR$ machacaban código del runtime → cuelgue en el - primer uso del calculador. Fix: área dedicada de 21 UDGs. -2. INKEY$ devolvía mayúsculas; el modo L del Spectrum (y los programas de - la época) usan minúsculas. Fix: tabla de keyscan en minúscula. -3. PO_GR_1 (bloques CHR$(128)-143) generaba patrones corruptos (OR al - registro equivocado + cuadrantes izq/der invertidos). Fix: algoritmo - literal de la ROM. - -RECETA para ejemplos clásicos transcritos de Sinclair BASIC: compilar con -`--string-base 1 --array-base 1` (indexación 1-based). Sin ello, las -colisiones por slicing de strings salen desplazadas una posición (no es -un bug del port: en zx48k pasa igual). - -## Heap en $8100 + traps de cinta de EightyOne — RESUELTO (2026-07-04) - -| Fuente | Prefijo SD81 | Qué prueba | Resultado | +| `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 | Gestor de memoria dinámica (3 fases: crecimiento char a char, REALLOC grandes, STR$ en bucle) con el heap en distintas direcciones | Bisección que aisló el bug | -| `tests_debug/memtest.bas` | MEMTST | R/W patrón dependiente de dirección en $8100-$BFFF (2 pasadas) | OK — descartó el hardware/paginación | -| `tests_debug/inputtest.bas` | INTEST | INPUT() mínimo aislado | Reproducía el cuelgue | -| `examples/sd81/flights_sd81.bas` | FLIGHT | Simulador de vuelo: PEEK COORDS ($8004/5), FP intensivo, INPUT | Adaptación de examples/flights.bas | - -Dos bugs encadenados, cazados el 2026-07-04: - -1. **Compilador (`src/arch/zx81sd/backend/main.py`)**: `heap_size`/`heap_address` - se registraban con `ADD_IF_NOT_DEFINED`, pero el backend Z80 genérico ya - las define antes (4768 / None) → los valores zx81sd ($8100 / 16127) nunca - se aplicaban y el heap acababa inline (DEFS) dentro de la zona ejecutable, - desperdiciando 4768 bytes y limitando el heap. Fix: asignación directa - `OPTIONS.heap_size/heap_address` (la CLI puede seguir sobreescribiendo). - -2. **Emulador (`Eightyone2/src/ZX81/rompatch.cpp`, `PatchTest`)**: los traps - de cinta de la ROM ZX81 ($0207/$02FF/$031E/$0356) se disparaban comparando - PC + `memory[pc]` **plano**, que conserva la ROM aunque el SD81 tenga RAM - mapeada. Con el heap en EQU el runtime baja $12A0 bytes y la división - __DIVU16_FAST aterrizaba en $02FF → el trap de SAVE hacía `DE=1` en mitad - de la división → cociente basura estable → bucle infinito de dígitos en - __PRINTU_LOOP (PUSH AF sin pop) → la pila descendía arrasando el runtime. - Los builds con heap inline eran inmunes de casualidad: las 4 direcciones - trampa caían dentro del bloque DEFS (datos, el PC nunca pasa por ahí). - Fix: `PatchTest` lee el byte con `zx81_PatchPeek()` (mapper-aware). - En hardware real este bug NO existe (no hay traps). - -Metodología que lo resolvió: simulación determinista del binario con la -librería Python `z80` (pip install z80) — diff de integridad de la zona de -código tras cada tramo + breakpoints comparando registros con EightyOne. -El binario era correcto en Z80 puro → la divergencia estaba en el emulador. -Arnés en el scratchpad de la sesión (runsim*.py, reproducible). - -## Scroll de PRINT saltaba a la ROM del Spectrum — RESUELTO (2026-07-04) - -Tercer bug de la cadena de flights.bas (viento=10, dir=100 → HALT): el -`print.asm` de zx81sd conservaba del zx48k el fallback -`__SCROLL_SCR EQU 0DFEh` (rutina CL-SC-ALL de la ROM del Spectrum). En -zx81sd no hay ROM: el primer PRINT que desbordaba la pantalla hacía CALL -a la línea BASIC compilada que casualmente ocupara $0DFE → ejecución -salvaje → RETURN de gosub sacando basura → HALT. Dependía de la entrada -porque el nº de dígitos tecleados movía el cursor: con viento "1"/"0" el -texto no llegaba a desbordar; con "10"/"100" sí. Ningún test anterior lo -pilló porque todos usan PRINT AT (nunca scroll). - -Fix: la implementación por búfer (`__ZXB_ENABLE_BUFFER_SCROLL`, scrollea -vía SCREEN_ADDR/SCREEN_ATTR_ADDR) es ahora la rama única de __SCROLL_SCR -en zx81sd/runtime/print.asm. Verificado con el simulador Python + teclado -scriptado: el bucle principal del juego mantiene SP estable durante miles -de pasos. Ojo futuro: revisar cualquier otro EQU/CALL a direcciones -absolutas de ROM Spectrum al portar ficheros del zx48k (grep hecho: -no queda ninguno en el runtime zx81sd). - -## Esquema de teclado nuevo: mayúsculas, CAPS LOCK y símbolos — 2026-07-04 - -| Fuente | Prefijo SD81 | Qué prueba | Resultado | +| `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 | INKEY$ interactivo: imprime código ASCII + carácter de cada tecla | Verificado por simulación exhaustiva (ver abajo); pendiente de probar a mano en el emulador/hardware | - -El teclado físico del ZX81 no distingue mayúscula/minúscula por tecla -(ver [[zx81sd-keyboard-case]]): SHIFT+letra da un símbolo, no la -mayúscula de esa letra. Hasta ahora `keyscan.asm` solo devolvía -minúsculas siempre. Nuevo esquema (`src/lib/arch/zx81sd/runtime/io/ -keyboard/keyscan.asm`, reescrito de raíz): - -- Sin modificador: minúscula (igual que antes — comecoquitos, snake, - flights, row4 siguen funcionando sin tocar una línea, porque ninguno - usa SHIFT). -- `SHIFT + letra`: MAYÚSCULA de esa letra. -- `SHIFT + "2"`: conmuta CAPS LOCK persistente (mudo, no imprime nada). -- CAPS LOCK activo: minúscula pasa a mayúscula; SHIFT sigue dando - mayúscula igual (es un OR, no hay interacción, decisión tomada con el - usuario). -- `"."` sola: `.` -- `SHIFT + "."`: `,` (igual que en el ZX81 real). -- `"." + otra tecla`: el símbolo impreso en el teclado del ZX81 para esa - tecla (`:` con Z, `)` con O, RUBOUT con 0, etc. — la vieja tabla SHIFT - del ZX81, ahora alcanzable con "." en vez de con SHIFT, ya que SHIFT - se ha redefinido para dar mayúsculas). - -Requirió reescribir el escaneo: antes se paraba en la primera fila con -algo pulsado (bastaba con una tecla a la vez). Ahora hacen falta dos -lecturas de puerto dedicadas para SHIFT (fila 0) y "." (fila 7, columna -1), más un escaneo de las demás filas buscando una tercera tecla -excluyendo esas dos posiciones (rutina `FIND_OTHER`). El combo -`SHIFT+"2"` usa un byte de estado persistente con detección de flanco -para no conmutar varias veces mientras se mantiene pulsado. - -Verificado con un arnés de simulación Python (`z80`, ver metodología ya -usada para los bugs del heap): se llama a `__ZX81SD_KEYSCAN` directamente -inyectando por el callback de E/S los bits de fila exactos de cada -combinación (Z sola, SHIFT+Z, "."+Z, SHIFT+2 mantenido 4 polls seguidos, -etc.), sin pasar por la complejidad de un teclado real. Los offsets de -las tablas y del estado (`_KBD_*`, todos LOCAL al PROC, no aparecen en el -`.map`) se localizaron buscando el patrón de bytes de `UNSHIFT_TABLE` -("zxcvasdfg") en el binario y calculando el resto por desplazamiento fijo -(cada tabla ocupa 39 bytes). Los 12 casos de la tabla de diseño -coincidieron exactamente, incluido el debounce del CAPS LOCK. - -### Corrección 2026-07-04: el modificador "." se movió de keyscan a input.bas - -Al probarlo en el emulador, el usuario detectó que `"."+tecla` (pensado -para dar el símbolo del ZX81 pulsando ambas a la vez, como SHIFT+letra) -era impracticable desde `INPUT`: `PRIVATEInputWaitKey` compromete la -tecla `"."` en cuanto la detecta sola, sin dar tiempo a que la segunda -tecla llegue de verdad a la vez (a diferencia de SHIFT+letra, que sí se -puede sostener cómodamente con la otra mano). Diagnóstico correcto del -usuario: *"la gestión de los símbolos no debe ir en el keyscan sino en -INPUT.bas"*. - -Fix: `keyscan.asm` ahora trata `"."` como una tecla más — sin -modificador da `.`, con SHIFT da `,` (igual que el ZX81 real), sin -ninguna lógica de "tercera tecla" para el punto (se quitó por completo -la exclusión de la fila 7 en `FIND_OTHER`, ya no hace falta). Las tablas -`UNSHIFT_TABLE`/`SYMBOL_TABLE`/`CAPS_TABLE` se promovieron de `LOCAL` a -ámbito de fichero (prefijo `__ZX81SD_`) y se añadió una rutina nueva, -`__ZX81SD_SYMBOL_FOR(char)`, que hace la búsqueda inversa -UNSHIFT_TABLE→SYMBOL_TABLE dado un carácter ya decodificado. - -La composición de símbolos ahora vive en `stdlib/input.bas` como una -"tecla muerta" secuencial: al leer `"."`, la función `input()` lee la -SIGUIENTE tecla por separado (sin exigir simultaneidad) y llama a -`PRIVATEInputSymbolFor()`; si hay símbolo, lo añade; si no, añade el -punto literal y procesa la segunda tecla con normalidad (DEL, ENTER, o -un carácter más). - -Bug propio cazado durante la implementación (antes de que el usuario lo -viera): `"."` + `"0"` resuelve a RUBOUT (12) vía `SYMBOL_TABLE` (es el -símbolo real que el ZX81 imprime sobre la tecla "0"), lo que borraría el -carácter anterior al escribir cualquier decimal terminado en ".0" (muy -habitual: "3.0", "10.0"...). Se excluyó ese valor explícitamente en -`input.bas` — RUBOUT ya se alcanza sin ambigüedad con SHIFT+0 (sí es -cómodo de sostener a la vez). Verificado con simulación de programa -completo (tecleo scriptado "1",".","0",ENTER → `a$="1.0"` correctamente, -y "."," o",ENTER → `a$=")"`), no solo con la función aislada. - -`tests_debug/keytest.bas` (KEYTST) sigue siendo el tester interactivo de -`INKEY$`; `tests_debug/inputtest.bas` (INTEST) es el mismo mini test de -`INPUT()` de antes, ahora también sirve para probar la composición de -símbolos a mano. - -### Refinamiento 2026-07-04: punto dos veces seguidas = punto literal - -Con el diseño anterior, `"."` + una letra con símbolo asociado (p.ej. Z → -`:`) siempre resolvía a ese símbolo — no había forma de escribir -literalmente un punto seguido de esa letra. Pedido del usuario: pulsar -`"."` dos veces seguidas debe confirmar el primer punto como literal -(la coma redundante que antes salía de `"."+"."`, vía `SYMBOL_TABLE`, ya -no hace falta — sale directamente y sin ambigüedad con `SHIFT+"."`), y la -tecla que venga después se lee como una pulsación nueva sin combinar. -Así, para escribir ".Z" se teclea "." "." "Z". - -Implementado en `input.bas`: si la segunda tecla leída tras un "." es -también un ".", se añade el punto, se descarta la segunda pulsación (no -imprime nada por sí misma, `LastK=0`) y el bucle vuelve a leer una tecla -fresca. Verificado con la misma simulación de programa completo: -`"."+"."` → `a$="."`; `"."+"."+"z"` → `a$=".z"` (sin formar el símbolo -`:`). - -## scroll.bas — RESUELTO 2026-07-04 (librería nueva, ejemplo sin cambios) - -| Fuente | Prefijo SD81 | Qué prueba | Resultado | +| `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 | Los 4 scrolls pixel-a-pixel (Right/Down/Left/Up) sobre una ventana de 60×60 px, 30 vueltas | Simulado 1200M ticks sin HALT/RST38; pendiente de ver en el emulador (el ejemplo hace 1920 scrolls de hasta 100×100 px, tarda un rato) | - -`src/lib/arch/zx48k/stdlib/scroll.bas` no tenía override en zx81sd — se -usaba la versión de zx48k tal cual, y las 8 subs (`ScrollRight/Left/Up/ -Down` + sus variantes `*Aligned`) llaman todas a `call 22ACh`, la rutina -*PIXEL-ADD* de la ROM del Spectrum. En zx81sd no hay ROM mapeada: esa -dirección cae en pleno código compilado del programa, y el HALT -reportado (`RST 38` en la traza) era justo el байте que hubiera ahí por -casualidad. - -Fix: `src/lib/arch/zx81sd/stdlib/scroll.bas`, copia idéntica salvo las 8 -llamadas a `$22AC` sustituidas por `call PIXEL_ADDR` (nuestra propia -rutina, `runtime/pixel_addr.asm`, ya usada por `plot.asm`/`draw.asm` — -ver [[zx81sd-pixel-addr-contract]]). El contrato de registros es -IDÉNTICO al de la ROM (A=191, B=Y, C=X → HL=offset, A=X AND 7, destruye -B, preserva D/E), así que no hizo falta tocar ni una línea del cuerpo de -los bucles de scroll, solo el punto de llamada. `SP.PixelDown`/ -`SP.PixelUp` (de zx48k/runtime/SP/) no necesitaron copia: son aritmética -pura sobre `SCREEN_ADDR`, sin ROM, y ya funcionaban igual en zx81sd (se -resuelven por el mecanismo normal de fallback a zx48k cuando no hay -override). - -`examples/scroll.bas` no necesitó ningún cambio de fuente — como -`4inarow.bas`, el problema era enteramente de la librería, no del -programa. Añadido a `CAMBIOS_BASIC.md` con esa misma nota. - -## maskedsprites.bas — RESUELTO 2026-07-04 (cambio de fuente, no de librería) - -| Fuente | Prefijo SD81 | Qué prueba | Resultado | +| `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 | Sprites enmascarados (AND+OR) con MSFS, 10 sprites animados | Simulado 1000M ticks sin HALT/RST38/escritura ilegal; PC avanza por un rango amplio de direcciones (no atascado) | - -A diferencia de `scroll.bas`, aquí el problema SÍ estaba en el propio -ejemplo (`WaitForNewFrame`, definida directamente en `examples/ -maskedsprites.bas`, no en la librería `cb/maskedsprites.bas`): hace -`EI` + `HALT` esperando la interrupción IM1 de 50Hz de la ROM del -Spectrum, comparando contra el contador `FRAMES` de la ROM en la -dirección absoluta `23672`. En zx81sd las interrupciones están -permanentemente deshabilitadas (todo el runtime corre con `DI`; el -vector `$0038` es solo una trampa `DI;HALT`, no un manejador real) — ese -`HALT` no despierta nunca. Confirmado con la traza: el simulador se -quedaba con `m.halted=True` exactamente en el `HALT` de `WaitForNewFrame` -tras ~31M ticks. - -Fix en `examples/sd81/maskedsprites_sd81.bas`: `WaitForNewFrame` reescrita -para usar `VSYNC_TICK` (`runtime/vsync.asm`, ya usada por `PAUSE`) en vez -de `EI+HALT` — sondea por puerto ($AFh) el contador de pulsos VSYNC -hardware del SD81 Booster, sin depender de interrupciones. El algoritmo -original hacía UN `HALT` inicial y luego un bucle que comprobaba -`FRAMES` SIN esperar de nuevo (confiaba en que la interrupción lo -siguiera incrementando en segundo plano); como en zx81sd nada lo -incrementa solo, el bucle llama a `VSYNC_TICK` explícitamente en cada -vuelta que le falte. `GetInterruptStatusInBorder` se dejó intacta (no se -llama nunca en el bucle principal, solo aparece comentada — se mantiene -únicamente para que la comprobación de compilación del final del fichero -no falle por "función no usada"). - -### Actualización 2026-07-04: MSFS portado de verdad al mapeador (bloque 7) - -El riesgo de `$5B5C`/`$7FFD` de arriba **ya no aplica**: a petición del -usuario ("¿por qué no usamos el bloque 7 que tenemos para bancos?") se -creó `src/lib/arch/zx81sd/stdlib/cb/maskedsprites.bas`, override -completo de la librería compartida (que sigue intacta, regla de -Boriel). Diseño (ver también `CAMBIOS_BASIC.md`): - -- **Hallazgo clave**: las funciones de MSFS (`RegisterSpriteImageInMSFS`, - `FindFirstUnusedBlockInMSFS`, etc.) son agnósticas de banco/dirección - — solo llaman a `GetBankPreservingRegs`/`SetBankPreservingINTs` y - leen/escriben la variable BASIC `MaskedSpritesFileSystemStart`. - Reescribiendo esas dos primitivas (usando el puerto `$E7` sobre el - **bloque 7**, `$E000-$FFFF` — reservado en nuestro mapa de memoria - justo para "banking de datos, mapas, sprites") y el cálculo de esa - dirección en `InitMaskedSpritesFileSystem()` (fija en `$E000` en vez - de "lo que quede hasta `$FFFF`", que asumía RAM plana Spectrum), el - resto del fichero (cientos de líneas de álgebra de bloques/bitmap) se - copió literalmente sin tocar una línea. -- `CheckMemoryPaging()` devuelve `0` (honesto: zx81sd no tiene doble - pantalla visible al estilo banco 5/7 del Spectrum) sin afectar a - MSFS, porque las funciones de MSFS no consultan esa función para - decidir si usar el banco — lo hacen incondicionalmente. +| `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` → stubs seguros (doble - buffer de pantalla real no está cubierto; código muerto en este - ejemplo dado que `memoryPaging=0`, pero ya no tocan `$5B5C`/`$7FFD` - por si alguien los llama directamente en el futuro). - -**Bug real encontrado durante la implementación** (no por el usuario, -cazado con el propio arnés de simulación): mi primer intento escribió -`SetBankPreservingINTs`/`GetBankPreservingRegs` en BASIC plano en vez de -ASM a mano. Rompía el contrato de registros documentado en el propio -fichero original ("Preserves: D, E, H, L") que el código ASM de -`RegisterSpriteImageInMSFS` y compañía da por hecho (por ejemplo, para -no perder `spriteImageAddr`, que llega en HL) — una función BASIC -compilada usa registros libremente por dentro sin ninguna garantía de -preservarlos. Resultado: los 6 sprites de prueba se registraban todos en -la MISMA dirección (`$0C07`) en vez de direcciones distintas. Se -reescribieron ambas primitivas en ASM a mano, con el mismo contrato -exacto que el original (solo tocan A, B, C). - -Verificado con simulación: las 6 direcciones de registro (`regHero0`, -`regFoe00`, `regFoe20-23`) salen correlativas cada 96 bytes exactos -(`$E010, $E070, $E0D0, $E130, $E190, $E1F0`, coincidiendo con -`$E000+n*96+16`), sin disparar `STOP`, y el bucle principal -(`WaitForNewFrame`) se alcanza repetidamente sin cuelgue tras 1000 -millones de ticks de simulación sin `HALT`/escritura ilegal. - -Limitación conocida del simulador Python usado en esta sesión: no -modela el mapeador de memoria (los `OUT` a `$E7` son no-op en la -simulación, toda la RAM se trata como plana) — no puede validar que el -intercambio de página *físico* funcione de verdad, solo que la lógica -Z80 es correcta asumiendo que sí. La validación definitiva es en el -emulador/hardware real. - -### Segunda vuelta 2026-07-04: seguía yéndose a HALT en hardware real - -Con el fix de arriba ya compilado, en el emulador real seguía disparando -`__STOP` (mismo síntoma: `RegisterSpriteImageInMSFS` devuelve 0). Traza -confirmó que el problema estaba en `SetBankPreservingINTs`, que hacía el -`OUT` al puerto `$E7` a mano en vez de llamar a `Map()` (mcu.bas): -escribía `A=7` (bloque, sin combinar con la página) con `B=página`. Mi -hipótesis inicial fue que el hardware podía estar en modo simple (donde -el byte de datos debe llevar página Y bloque combinados, -`(página AND 31)<<3 | bloque`, y con solo `A=7` se interpretaría como -página=0) — **el usuario corrigió esto**: el cargador SD81 deja el -mapeador en modo completo desde la línea `LOAD *MAP 7,63`, hasta el -siguiente reset, así que esa explicación concreta no cuadra (en modo -completo solo importan los 3 bits bajos de A, iguales en ambas -versiones). La causa exacta seguía sin confirmarse en el momento de -escribir esto. - -Se cambió `SetBankPreservingINTs` para llamar a `Map()` (código ya -probado en otros contextos) en vez de repetir la lógica del puerto a -mano, preservando D,E,H,L alrededor de la llamada con `push`/`pop` -manuales (`Map()` en sí no preserva nada). Tuvo un efecto secundario: -`Map()` dejó de estar referenciada desde BASIC en ningún sitio (solo -desde ASM a mano), y el eliminador de código muerto del compilador la -quitó del binario → `Undefined GLOBAL label '._Map'`. Se resolvió con -una llamada BASIC explícita y redundante a `Map(7, MaskedSprites_MSFS_Page)` -dentro de `InitMaskedSpritesFileSystem()` (comentada como tal — el -análisis de uso del compilador no cuenta las llamadas hechas desde ASM). - -Verificado de nuevo en simulación (mismas 6 direcciones correlativas, -sin `STOP`) — pero siguió fallando en el emulador real. El cambio a -`Map()` era, como señaló el usuario, un no-op ("el Map de la biblioteca -hace exactamente lo mismo que el out"). - -### Causa raíz REAL (tercera vuelta, 2026-07-04): el FSB nunca se inicializa - -La traza del usuario mostraba `FindFirstUnusedBlockInMSFS` recorriendo -el free-space bitmap COMPLETO (bucle FIND-INT, 254 líneas de RRCA/DEC E) -y saliendo por la rama `full` (`SCF/RET` → `JR C` → `LD HL,0`): **todos -los bloques aparecían como "ocupados"**. `__EQ16` (el sospechoso -inicial) funcionaba perfectamente — `regHero0` realmente era 0. - -Causa: **ni nuestra versión ni la original de zx48k limpian jamás el -FSB** (los bytes del bitmap en `start+2..start+1+l`). En el Spectrum no -hace falta: el test de RAM de la ROM deja toda la memoria a cero en el -arranque, así que el bitmap nace "todo libre" gratis. En zx81sd la -página del bloque 7 llega con basura de fábrica → todos los bits a 1 → -"sin bloques libres" → `RegisterSpriteImageInMSFS` devuelve 0 → `STOP`. - -**Por qué el simulador Python dio falso OK dos veces**: su RAM también -nace a ceros, igual que la del Spectrum tras el test de ROM — -exactamente la condición que oculta el bug. Lección de metodología -incorporada al arnés: para validar código que lee memoria no -inicializada, rellenar toda la RAM no cargada con basura (`$FF`) antes -de simular. Con RAM sucia, el binario sin fix reproduce el `STOP` (el -modo de fallo de la traza) y el binario con fix pasa completo (6 -registros correctos, bucle principal alcanzado). - -Fix (3 líneas + comentario en `InitMaskedSpritesFileSystem`): bucle -`FOR j = start+2 TO start+1+l: poke j,0: NEXT` tras calcular el tamaño -del FSB. - -También se verificó por el camino, con `examples/sd81/block7test.bas` -(prefijo BLOCK7), que el mapeador funciona perfectamente: patrones -distintos escritos en páginas 20 y 63 del bloque 7 sobreviven al -intercambio de página (contenido independiente por página). El -mapeador nunca fue el problema. - -### Cuarta vuelta 2026-07-04: sprites como líneas verticales — página residente - -Con el fix del FSB, en el emulador real MSFS ya inicializaba bien -(pantalla: `Init MSFS at 57344`, `Free Blocks = 85`, y los 6 registros -con los valores EXACTOS que predijo la simulación) pero los sprites se -dibujaban como líneas verticales en vez de sus gráficos. - -Causa (descuido de diseño de este override, no del original): -`SaveBackgroundAndDrawSpriteRegisteredInMSFS` — el que dibuja en el -bucle principal y el que fabrica las imágenes desplazadas bajo demanda — -accede a la MSFS **sin envolver con Get/SetBank**. En el diseño original -no lo necesita: en 128K el banco 7 se queda mapeado en `$c000` -(`SetDrawingScreen7`) y en 48K la MSFS está en RAM plana siempre -visible. Nuestra primera versión "liberaba" el bloque 7 de vuelta a la -página 63 al restaurar tras `Init`/`Register...` → el dibujo leía -máscaras y gráficos de la página 63 (basura) → líneas verticales. El -simulador no podía detectarlo: sin mapeador modelado, página 20 y 63 -son la misma RAM plana. - -Fix: la página de MSFS queda **residente** en el bloque 7 desde el -init — `SetBankPreservingINTs` con valor ≠ 7 solo anota el número, no -desmapea (el "liberar a página 63" era invención de este override, -nada lo necesita). Documentado en la cabecera de la librería: si un -programa usa el bloque 7 para su propio banking, debe remapear su -página él mismo y llamar a `SetBankPreservingINTs(7)` antes de volver a -usar MSFS. + `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. diff --git a/src/arch/zx81sd/doc/PRECAUCIONES.md b/src/arch/zx81sd/doc/PRECAUCIONES.md deleted file mode 100644 index 8c22fb451..000000000 --- a/src/arch/zx81sd/doc/PRECAUCIONES.md +++ /dev/null @@ -1,220 +0,0 @@ -# Precauciones al escribir o portar software para zx81sd - -zx81sd hace que ZX BASIC genere binarios que "parecen" un Spectrum (la -interfaz SD81 Booster emula su pantalla, y buena parte de la stdlib -compartida asume convenciones del Spectrum), pero **no hay ROM del -Spectrum en ningún sitio**: no hay `RST $28` de la ROM, no hay rutinas -en direcciones fijas, no hay sysvars del Spectrum en `$5C00+`. Casi -todos los bugs de este port han venido de código (de examples/ oficiales -o de la stdlib compartida) que asume silenciosamente alguna de estas -cosas. Antes de portar algo, repasa esta lista. - -## 1. Nunca hay ROM: cuidado con direcciones absolutas y sysvars - -Cualquier `POKE`/`PEEK`/`CALL` a una dirección numérica fija -(23675, 23658, $22AC, $0DFE...) casi seguro que es una sysvar o rutina -de la **ROM del Spectrum**, que en zx81sd no existe: esa dirección cae -en RAM libre, o peor, en pleno código compilado del programa — -ejecutarla o interpretarla como dato produce corrupción silenciosa, -gráficos erróneos, o un `HALT`/reinicio salvaje muy difícil de -relacionar con la causa (varios bugs de este port tardaron sesiones -enteras en diagnosticarse por esto). - -- **Sysvars del Spectrum → sysvars de zx81sd**: la tabla de - equivalencias está en - [`../../../lib/arch/zx81sd/runtime/sysvars.asm`](../../../lib/arch/zx81sd/runtime/sysvars.asm) - (todas viven en `$8000+`, no en `$5C00+`). Ejemplos ya resueltos: - `UDG` (23675 → `$8002`), `COORDS` (23677/23678 → `$8004`/`$8005`). - Ver [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md) para el detalle línea a - línea de cada caso ya encontrado. -- **Rutinas de ROM llamadas directamente** (`call $22AC` = PIXEL-ADD, - `call $0DFE` = CL-SC-ALL/scroll, `RST $28` = calculador FP...): si el - fuente o una librería compartida hace esto, hace falta un override en - `src/lib/arch/zx81sd/` que sustituya la llamada por la rutina propia - con el **mismo contrato de registros** que la de la ROM (ver más - abajo). Ejemplo resuelto: `stdlib/scroll.bas`. -- **`grep` preventivo**: al portar un fichero de `zx48k/` a `zx81sd/`, - buscar `EQU 0[0-9A-F]` / `call 0x` / literales de 4-5 dígitos - sospechosos antes de darlo por bueno, no solo cuando algo falla. - -## 2. Contratos de registros de las rutinas ASM: son sagrados - -Varias rutinas del runtime tienen contratos de preservación de -registros explícitos y no negociables porque el código que las llama -(heredado de zx48k, no tocable) depende de ellos al pie de la letra. -Ejemplos: - -- `PIXEL_ADDR` (`runtime/pixel_addr.asm`): A=191, B=Y, C=X → HL=offset, - A=X AND 7; **destruye B, preserva D y E**. `draw.asm` guarda la - coordenada Bresenham en D alrededor de la llamada confiando en esto - literalmente — romperlo (como pasó una vez con un intento que usaba D - de scratch) corrompe cualquier línea con componente vertical sin - tocar para nada las horizontales, lo que despista mucho a la hora de - diagnosticar. -- `GetBankPreservingRegs`/`SetBankPreservingINTs` (MSFS, - `cb/maskedsprites.bas`): contrato documentado "preserva D,E,H,L". - Escribir el reemplazo en BASIC plano en vez de ASM a mano rompe esto - sin ningún aviso del compilador (el código BASIC generado usa - registros libremente por dentro) — un bug real de esta clase hizo que - 6 sprites de prueba se registraran todos en la misma dirección - incorrecta. **Nota**: el port de MSFS/`maskedsprites.bas` sigue en - proceso (aún no funciona del todo bien) — este ejemplo concreto sirve - para ilustrar el tipo de bug, no como confirmación de que la librería - ya esté terminada. - -**Regla práctica**: si vas a sustituir una rutina ASM que tiene un -contrato de registros documentado (o que se puede inferir mirando quién -la llama y qué asume), reimplaza en ASM a mano preservando exactamente -ese contrato. Una función BASIC (`SUB`/`FUNCTION`), por sencilla que -parezca, NO es un reemplazo válido salvo que el contrato sea "ninguno". - -## 3. El teclado es el del ZX81, no el del Spectrum - -El SD81 Booster no tiene teclado Spectrum: reescanea el teclado físico -de 40 teclas del ZX81 (`runtime/io/keyboard/keyscan.asm`). Diferencias -que importan al portar/escribir código: - -- Pulsación directa de una letra: minúscula. Con `SHIFT+letra`: - MAYÚSCULA de esa letra — exactamente igual que en un Spectrum real, a - diferencia del ZX81 original (donde `SHIFT+letra` daba un símbolo, no - una mayúscula). Esta redefinición es una decisión de diseño de este - port, ver [MAP.md](MAP.md) sección "Esquema de teclado nuevo". En - consecuencia, **sí se puede escanear/comparar `INKEY$` contra - mayúsculas de letra** (`IF INKEY$="S"`, pensado para jugarse con - `SHIFT` sostenido al estilo Spectrum) sin ninguna limitación de - hardware: basta con pulsar `SHIFT+S`. -- `SHIFT+"2"` alterna un CAPS LOCK persistente. -- `"."` es una tecla normal (`.` sin shift, `,` con shift). Los símbolos - del ZX81 original asociados a cada tecla (`:` en Z, `)` en O, etc.) se - alcanzan con la secuencia `"." + tecla` **solo desde `INPUT()`** (tecla - muerta gestionada en `stdlib/input.bas`), no desde `INKEY$` a pelo — - no hay forma de "sostener ambas a la vez" con fiabilidad en este - teclado, así que la composición se hace pulsando `.` primero y la - segunda tecla después. Pulsar `.` **dos veces seguidas** confirma el - primer punto como literal y descarta cualquier combo: la tecla que - venga después de ese segundo punto se lee como una pulsación nueva, - sin combinar con nada (permite escribir cualquier tecla justo después - de un punto sin arriesgarse a formar un símbolo por accidente). - -## 4. No hay interrupciones: nunca esperes un `HALT`/`EI` para sincronizar - -El runtime de zx81sd corre permanentemente con interrupciones -deshabilitadas (`DI`); el vector `$0038` es solo una trampa `DI;HALT`, -no un manejador de interrupción real. Cualquier código (típicamente -código ASM inline de un ejemplo, no de la stdlib) que haga `EI` seguido -de `HALT` esperando el pulso de 50Hz de la ROM del Spectrum **se cuelga -para siempre** — no hay nada que lo despierte. - -- **Sustituto**: `VSYNC_TICK` (namespace `core`, en - `runtime/vsync.asm`) sondea por puerto el contador de pulsos VSYNC - real del hardware SD81 Booster. Ya lo usa `PAUSE` internamente. -- Al llamarlo desde un bloque `ASM ... END ASM` que no esté ya dentro de - `push namespace core`, hay que usar el prefijo completo: - `call .core.VSYNC_TICK` (si se omite el prefijo, el compilador da - `Undefined GLOBAL label '.VSYNC_TICK'` — error ya visto más de una - vez en este port). -- Un contador que antes se incrementaba solo por la interrupción en - segundo plano (`FRAMES`/23672 en el Spectrum) hay que actualizarlo a - mano llamando a `VSYNC_TICK` explícitamente en cada vuelta del bucle - de espera, no solo una vez al principio. - -## 5. Namespaces y mangling de etiquetas ASM - -Un `DIM X` o `SUB`/`FUNCTION X` de BASIC se traduce a la etiqueta ASM -`_X` (un solo guion bajo), **salvo** que el fichero envuelva su código -en `push namespace core ... pop namespace`, en cuyo caso hay que -referenciarla desde fuera como `.core._X` (variables) o `.core.X` -(funciones/rutinas). Confundir esto en cualquier dirección produce -`Undefined GLOBAL label`. Si un fichero de este port no usa namespacing -en ningún otro sitio, no hace falta envolver un bloque `ASM` nuevo en -`push namespace core` solo porque otro fichero (como `vsync.asm`) sí lo -use — basta con prefijar la referencia puntual. - -## 6. El eliminador de código muerto no ve las llamadas desde ASM a mano - -El análisis de "¿esto se usa?" del compilador solo cuenta llamadas -hechas con sintaxis BASIC (`Foo(x)`). Una `SUB`/`FUNCTION`/variable -BASIC referenciada **solo** desde un bloque `ASM ... END ASM` (p. ej. -`call _Foo`) puede ser eliminada como código muerto, dando -`Undefined GLOBAL label '._Foo'` al enlazar — el símbolo nunca llegó a -existir en el binario final. Dos salidas: - -- Si es un dato puro (un byte de estado, por ejemplo), declararlo como - ASM puro (`ASM \n _Label: \n DEFB 0 \n END ASM` a nivel de fichero), - no como `DIM`. -- Si es una función que de verdad hace falta que sea BASIC (porque - llama a otras cosas de la stdlib), añadir una llamada BASIC real - (aunque sea redundante/no estrictamente necesaria en ese punto) en - algún sitio alcanzable del código, para que el análisis de uso la - cuente. - -## 7. Metodología de depuración sin hardware - -Para diagnosticar sin gastar ciclos de prueba-error en el emulador o el -hardware real, este port usa simulación directa del binario con el -paquete `z80` de Python. Dos lecciones ya aprendidas por las malas -(documentadas con más detalle en [MAP.md](MAP.md)): - -- Comprobar el PC periódicamente cada N ticks gruesos puede dar falsos - "atascado" si justo cae siempre en el mismo punto de un bucle; usar - breakpoints reales (comparar `m.pc` contra la dirección exacta, - sacada del `.map`) o chunks de tick más finos. -- La RAM del simulador nace a **ceros**, igual que la del Spectrum tras - el test de RAM de su ROM. Esto oculta bugs de memoria no inicializada - (dio dos falsos "OK" seguidos en el bug del free-space-bitmap de - MSFS). Para validar código que lee memoria que no inicializa él - mismo, rellenar toda la RAM no cargada con `0xFF` antes de cargar el - binario, para reproducir las condiciones reales de hardware/tarjeta. -- El simulador **no modela el mapeador de memoria** (`OUT` al puerto - `$E7` es un no-op): puede validar que la lógica Z80 es autoconsistente, - pero no que el intercambio de página físico funcione de verdad — eso - solo se confirma en el emulador (EightyOne) o en hardware real. -- Si un binario falla en EightyOne pero la simulación Python lo ejecuta - limpio, sospecha primero del emulador (ver el bug de traps de cinta ya - encontrado y corregido en `Eightyone2/src/ZX81/rompatch.cpp`, en el - repositorio del emulador, no en este) antes que del runtime — en - hardware real esos traps no existen. - -## 8. Nunca hagas `#include once ` desde una librería propia - -Si necesitas los sysvars propios de zx81sd (`CHARS`/`UDG`/`ATTR_P`/ -`SCREEN_ADDR`/`SCREEN_ATTR_ADDR`...) desde un fichero `stdlib/*.bas` -nuevo, **no lo incluyas tú mismo con `#include once `** — -solo referencia los símbolos con el prefijo `.core.` (ver punto 5) y -confía en que el resto del runtime ya lo trajo. - -`sysvars.asm` arrastra tras de sí `bootstrap.asm` → `charset.asm`, y -este último hace `INCBIN "specfont.bin"` (el font completo, bytes -binarios, no texto ensamblador). Si tu fichero resulta ser el -**primero** en incluir `sysvars.asm` en todo el programa compilado -(fácil que pase: un `#include ` al principio del fuente -del usuario se procesa antes que cualquier `CLS`/`PRINT` textualmente -posterior), ese `INCBIN` se emite **justo en el punto del fichero fuente -donde pusiste el `#include`**: - -- Puesto a nivel BASIC (fuera de un bloque `ASM ... END ASM`): el lexer - de BASIC intenta tokenizar esos bytes binarios como si fueran texto - fuente → `error: illegal preprocessor character` en líneas que no - tienen nada que ver con el problema real (mal atribuidas al inicio - del fichero). -- Puesto dentro de un bloque `ASM` (p. ej. al principio del cuerpo de - una función): el binario del font se emite literalmente en medio del - código compilado de esa función — compila sin error, pero la CPU - "ejecuta" esos bytes de font como si fueran instrucciones en cuanto - el flujo de control cae ahí, produciendo un `HALT` o comportamiento - errático en una dirección que no tiene relación aparente con el bug - (encontrado al portar `print42.bas`/`print64.bas`, ver - [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md)). - -Cualquier programa real que use tu librería casi seguro que también usa -`CLS`/`PRINT` en algún punto, y esas rutinas ya requieren `sysvars.asm` -— así que omitir el `#include` en tu fichero es seguro en la práctica, -no una chapuza. - -## Ver también - -- [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md) — catálogo de cambios de fuente - ya necesarios en ejemplos oficiales, con el patrón general a buscar - en cualquier ejemplo nuevo. -- [MAP.md](MAP.md) — bitácora técnica completa, bug a bug, con las - trazas de investigación. diff --git a/src/arch/zx81sd/doc/PRECAUTIONS.md b/src/arch/zx81sd/doc/PRECAUTIONS.md new file mode 100644 index 000000000..3ab31ace0 --- /dev/null +++ b/src/arch/zx81sd/doc/PRECAUTIONS.md @@ -0,0 +1,216 @@ +# 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 equivalence table is in + [`../../../lib/arch/zx81sd/runtime/sysvars.asm`](../../../lib/arch/zx81sd/runtime/sysvars.asm) + (all live 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 wait on `HALT`/`EI` to synchronize + +The zx81sd runtime always runs with interrupts disabled (`DI`); the +`$0038` vector is only a `DI;HALT` trap, not a real interrupt handler. +Any code (typically inline ASM in an example, not the stdlib) that +does `EI` followed by `HALT` waiting for the Spectrum ROM's 50Hz pulse +**hangs forever** — nothing ever wakes it up. + +- **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. + +## See also + +- [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/USAGE.md b/src/arch/zx81sd/doc/USAGE.md new file mode 100644 index 000000000..43571baf8 --- /dev/null +++ b/src/arch/zx81sd/doc/USAGE.md @@ -0,0 +1,88 @@ +# 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. + +## 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/doc/USO.md b/src/arch/zx81sd/doc/USO.md deleted file mode 100644 index 6ed141ddc..000000000 --- a/src/arch/zx81sd/doc/USO.md +++ /dev/null @@ -1,87 +0,0 @@ -# Uso: compilar, empaquetar y cargar un programa en zx81sd - -## 1. Compilar - -Desde la raíz de este repositorio: - -``` -python -m src.zxbc.zxbc --arch zx81sd -o -M -``` - -- `--arch zx81sd` selecciona el backend y los overrides de esta - arquitectura (ver [../README.md](../README.md) — regla de oro del port). -- `-M ` es opcional pero muy recomendable: genera el mapa de - símbolos (dirección de cada label ASM/BASIC), imprescindible para - depurar con un emulador o simular el binario (ver [MAP.md](MAP.md) - para ejemplos de arneses de simulación en Python). -- Ejemplos transcritos de Sinclair BASIC clásico (arrays/strings - 1-based) suelen necesitar además `--string-base 1 --array-base 1` - (ver [CAMBIOS_BASIC.md](CAMBIOS_BASIC.md), ejemplo `comecoquitos.bas`). - -También existe `python zxbc.py --arch zx81sd -f bin -o ` -(script de entrada tradicional); ambas formas son equivalentes. - -## 2. Empaquetar para el ZX81 - -El binario que produce el compilador es una imagen plana de hasta 48KB -(bloques 0-5 del mapeador SD81). El ZX81 solo puede cargar de golpe lo -que quepa en su propia RAM visible, así que hay que partir ese binario -en páginas de 8KB y generar un cargador BASIC que las vaya metiendo en -la tarjeta SD81 Booster una a una, remapeando el mapeador de memoria -entre página y página. - -Esto lo hace [`../tools/split_sd81.py`](../tools/split_sd81.py): - -``` -python src/arch/zx81sd/tools/split_sd81.py [PREFIJO] -``` - -- `PREFIJO` (opcional) es el nombre base de los ficheros de salida, en - el charset del ZX81 (letras, dígitos, sin guion bajo). Si se omite, - se deriva del nombre del `.bin` de entrada. -- Genera: - - `P8.BIN`, `P9.BIN`, ... — una por cada 8KB del - binario (página SD81 8 = bloque 0, 9 = bloque 1, etc.) - - `_loader.txt` — el listado BASIC del cargador en texto - plano (para leer/depurar). - - `.P` — el mismo cargador, ya tokenizado, listo para - cargar y ejecutar en el ZX81 (`LOAD ""` desde la SD). - -El cargador generado usa `LOAD THEN CLEAR`, `LOAD *MAP` y -`LOAD FAST ... CODE`, extensiones del firmware del SD81 Booster (no -existen en la ROM original del ZX81). Hace lo siguiente, en orden: - -1. Reserva memoria (`CLEAR`) y carga `BOOT1.BIN` (el stage 1, fijo para - todos los programas — fuente en [`../tools/boot1.asm`](../tools/boot1.asm), - binario ya ensamblado en [`../tools/boot1.bin`](../tools/boot1.bin)). -2. Por cada página del binario: mapea el bloque 7 a esa página física - (`LOAD *MAP 7,`) y vuelca los datos ahí (`LOAD FAST ... CODE - 57344`, la ventana del bloque 7 en `$E000`). -3. Al terminar, deja el mapeador en modo "página completa" (`LOAD *MAP - 7,63`) — a partir de aquí el mapeador ya no vuelve a modo simple - hasta el siguiente reset — y salta a `BOOT1.BIN` (`RAND USR 24576`), - que hace el mapeo definitivo de los bloques 0-5 y arranca el - programa. - -## 3. Copiar a la tarjeta SD - -Copia a la SD, junto al resto de tu colección: - -- `BOOT1.BIN` (una sola vez, es el mismo para todos los programas) -- Todas las `P.BIN` del programa -- `.P` - -Y en el ZX81 (o en EightyOne apuntando a la imagen de la SD): `LOAD ""` -y selecciona ``. - -## 4. Depurar sin hardware: simulación con Python - -Para diagnosticar cuelgues, HALTs o resultados incorrectos sin tener -que probar en el emulador o el hardware real cada vez, el desarrollo de -este port ha usado el paquete `z80` de Python (`pip install z80`) para -simular el binario plano directamente. La metodología completa -(incluida una trampa importante: la RAM del simulador nace a ceros, lo -que puede ocultar bugs de memoria no inicializada que sí aparecen en -hardware real) está documentada en [MAP.md](MAP.md) — sección "Heap en -$8100 + traps de cinta de EightyOne" y las notas de metodología del bug -de MSFS/maskedsprites. diff --git a/src/arch/zx81sd/tools/boot1.asm b/src/arch/zx81sd/tools/boot1.asm index 16217c4de..7eaa5830f 100644 --- a/src/arch/zx81sd/tools/boot1.asm +++ b/src/arch/zx81sd/tools/boot1.asm @@ -1,33 +1,33 @@ ; =========================================================================== ; bootstrap_stage1.asm — Stage 1 Bootstrap ZX81 + SD81 Booster ; -; Reside en $6000 (bloque 3). El cargador BASIC lo envía allí antes de -; nada porque es una zona neutral: BASIC del ZX81 no la necesita, y aún -; no hemos remapeado ningún bloque. +; 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. ; -; Secuencia de inicio (ver ZX81-SD81-Adaptation-Plan.md): -; 1. DI — deshabilitar interrupciones (el ZX81 FAST mode ya lo hace, pero -; lo repetimos por seguridad antes de tocar la paginación) -; 2. Activar modo Superfast HiRes Spectrum vía FPGA (POKE 2045 = $07FD) -; 3. Desactivar IO mapeado en memoria (POKE 2056 = $0808) -; 4. Mapear bloque 0 → página 8 (OUT ($E7), page=8, block=0) -; El stage 2 ($0100-$0FFF en página 8) está ahora listo para ejecutar. -; 5. JP $0100 — salta al stage 2 en la RAM recién mapeada +; 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. Disable memory-mapped IO (POKE 2056 = $0808) +; 4. Map block 0 -> page 8 (OUT ($E7), page=8, block=0) +; Stage 2 ($0100-$0FFF on page 8) is now ready to run. +; 5. JP $0100 — jumps to stage 2 in the freshly mapped RAM ; -; Notas: -; - HFILE=$C000 es el valor por defecto de la FPGA al activar modo 172. -; Si tu hardware requiere configurarlo explícitamente, descomenta el -; bloque HFILE al final. -; - El stage 1 NO inicializa SP; el stage 2 lo hace (ld sp, $7FFF). -; - Bloques 1-5 se mapean en el stage 2 (ya ejecutando desde página 8). +; 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. +; - 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). ; -; Compilar: +; Compile: ; zxbasm bootstrap_stage1.asm -o bootstrap_stage1.bin -; o con pasmo: +; or with pasmo: ; pasmo --bin bootstrap_stage1.asm bootstrap_stage1.bin ; -; Cargar en el emulador / hardware a dirección $6000 (24576 decimal). -; Ejecutar desde BASIC con: +; Load on the emulator / hardware at address $6000 (24576 decimal). +; Run from BASIC with: ; RANDOMIZE USR 24576 ; =========================================================================== @@ -35,49 +35,49 @@ SD81_STAGE1: - di ; Interrupciones desactivadas + di ; Interrupts disabled ; -------------------------------------------------------------------------- - ; Asignar la direccion del framebuffer (HFILE) + ; Set the framebuffer address (HFILE) ; high=2044 low=(2043) ; - + ld hl, $C000 - ld ($07FB), hl - + ld ($07FB), hl + ; -------------------------------------------------------------------------- ; ------------------------------------------------------------------ - ; Activar modo Superfast HiRes Spectrum - ; POKE 2045, 172 → ld a, 172 / ld ($07FD), a - ; La FPGA del SD81 Booster interpreta esto como: - ; modo 172 ($AC) = Spectrum 256x192 desde HFILE=$C000 + ; 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 ; ------------------------------------------------------------------ - ; Desactivar IO mapeado en memoria - ; POKE 2056, 0 → ld ($0808), a - ; Evita colisiones entre las instrucciones IN/OUT y el espacio de RAM + ; 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 ; ------------------------------------------------------------------ - ; Mapear bloque 0 ($0000-$1FFF) → página 8 del SD81 - ; Puerto $E7: modo full 64 páginas, OUT (C), A con B=página, A=bloque - ; La página 8 contiene el binario compilado (vectors + stage 2 + runtime) + ; 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 ; página SD81 destino (8 = primera página de usuario) - ld a, 0 ; bloque Z80 a reasignar (bloque 0 = $0000-$1FFF) - ld c, $E7 ; puerto de paginación SD81 - out (c), a ; mapear (B=página, A=bloque) + 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) ; ------------------------------------------------------------------ - ; Saltar al stage 2 en la RAM recién mapeada - ; A partir de aquí el bloque 0 contiene la página 8: - ; $0000-$0067 vectores RST - ; $0100 __START_PROGRAM (inicio del stage 2) + ; 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 diff --git a/src/arch/zx81sd/tools/split_sd81.py b/src/arch/zx81sd/tools/split_sd81.py index 5dfe7db79..51e29fc83 100644 --- a/src/arch/zx81sd/tools/split_sd81.py +++ b/src/arch/zx81sd/tools/split_sd81.py @@ -1,24 +1,24 @@ #!/usr/bin/env python3 """ -split_sd81.py — Particionador de binario ZX81+SD81 Booster + generador de loader +split_sd81.py — ZX81+SD81 Booster binary splitter + loader generator -Divide el binario plano generado por zxbc.py (--arch zx81sd, -f bin) -en páginas de 8KB para su carga desde el cargador BASIC del ZX81, y genera -el listado BASIC (texto plano, no tokenizado) necesario para cargarlas. +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. -Mapa de páginas: - Página 8 → bloque 0 ($0000-$1FFF): vectors + stage2 + runtime + código usuario - Página 9 → bloque 1 ($2000-$3FFF): continuación si el binario supera 8KB - Página 10 → bloque 2 ($4000-$5FFF): ídem - Página 11 → bloque 3 ($6000-$7FFF): ídem (stage 1 externo reside aquí antes del salto) - Página 12 → bloque 4 ($8000-$9FFF): sysvars + heap (datos, no ejecutable sin MC45) - Página 13 → bloque 5 ($A000-$BFFF): heap continuación +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 -El binario empieza en $0000 y no incluye cabecera .tap/.tzx. -Cada fichero de salida se llama P.BIN (mayúsculas) donde N es la página. +The binary starts at $0000 and includes no .tap/.tzx header. Each +output file is named P.BIN (uppercase), where N is the page. -El loader BASIC generado (texto plano, aún no tokenizado a .p) sigue la -secuencia validada manualmente sobre hardware/emulador: +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 @@ -31,30 +31,35 @@ LOAD *MAP 7,63 RAND USR 24576 -Nota: "LOAD THEN CLEAR", "LOAD *MAP" y "LOAD FAST ... CODE" son extensiones -propias del firmware del SD81 Booster sobre el BASIC del ZX81 (no existen en -la ROM original). El bloque 7 ($E000/57344) se usa como ventana de paginación -temporal para volcar cada página física antes de que BOOT1.BIN haga el mapeo -definitivo de bloques 0-5 vía el puerto $E7. El "LOAD *MAP 7,63" final fuerza -el cambio a modo full-paging (64 páginas) y deja el bloque 7 en un estado -neutro antes de saltar al programa. +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. -Además del listado en texto plano, genera el .p tokenizado real (listo para -cargar/ejecutar) usando zx81_p_loader.py, un puerto a Python del tokenizador -de EightyOne (zx81BasicLoader.cpp / IBasicLoader.cpp). +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). -Uso: - python split_sd81.py [salida_base] +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. - Si no se especifica salida_base, se usa el nombre de entrada sin extensión. +Usage: + python split_sd81.py [output_base] -Ejemplo: + 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 (primera página, siempre presente) - → TEST_SD81P9.BIN (solo si el binario supera 8 KB) - → ... - → test_sd81_loader.txt (listado BASIC del cargador, texto plano) - → TEST_SD81.P (mismo listado, tokenizado) + -> 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 sys @@ -62,13 +67,13 @@ from zx81_p_loader import build_p_file -PAGE_SIZE = 8192 # 8KB por página -FIRST_PAGE = 8 # página SD81 asignada al bloque 0 -CLEANUP_PAGE = 63 # página neutra: fuerza modo full-paging (64 páginas) +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 # protege BOOT1.BIN (reservado justo debajo) -LOAD_WINDOW_ADDR = 57344 # $E000, ventana de bloque 7 +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[str]: @@ -76,7 +81,7 @@ def split(input_path: str, output_base: str) -> list[str]: data = f.read() if not data: - sys.exit(f"Error: {input_path} está vacío") + sys.exit(f"Error: {input_path} is empty") pages_written = [] page_num = FIRST_PAGE @@ -85,7 +90,7 @@ def split(input_path: str, output_base: str) -> list[str]: while offset < len(data): chunk = data[offset : offset + PAGE_SIZE] - # Rellenar hasta PAGE_SIZE con 0xFF (valor indefinido de FLASH/RAM sin inicializar) + # 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)) @@ -95,7 +100,7 @@ def split(input_path: str, output_base: str) -> list[str]: pages_written.append(out_path) print( - f" Pagina {page_num} (bloque {page_num - FIRST_PAGE}): " + f" Page {page_num} (block {page_num - FIRST_PAGE}): " f"{out_path} " f"[{offset:#06x} - {min(offset + PAGE_SIZE - 1, len(data) - 1):#06x}]" ) @@ -138,44 +143,44 @@ def main(): 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() - # Los nombres de fichero de las paginas y del loader.p se escriben en el - # charset del ZX81 (letras, digitos y unos pocos simbolos: sin guion - # bajo ni otros caracteres no representables). Fallar pronto y claro en - # vez de que reviente mas tarde al tokenizar el loader. + # 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: el nombre base {output_base!r} contiene {c!r}, " - "no representable en el charset ZX81 (evita '_' y similares)." + 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: no se encuentra {input_path!r}") + 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"Binario: {input_path} ({size} bytes, {size/1024:.1f} KB, {num_pages} pagina/s)") - print(f"Particionando en paginas de {PAGE_SIZE} bytes (pagina SD81 inicial = {FIRST_PAGE})...") + 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"Generados {len(pages)} fichero/s.") + print(f"Generated {len(pages)} file(s).") if len(pages) == 1: - print("El programa cabe en una sola pagina (8 KB).") + print("The program fits in a single page (8 KB).") else: - print(f"Paginas {FIRST_PAGE} a {FIRST_PAGE + len(pages) - 1}.") + print(f"Pages {FIRST_PAGE} to {FIRST_PAGE + len(pages) - 1}.") loader_lines = generate_loader_lines(pages) loader_text = generate_loader_text(loader_lines) - # El .p es un nombre de fichero real para el ZX81 (charset sin guion - # bajo): se deriva de output_base (mismo prefijo que las paginas), - # concatenado sin separador, igual que "P8.BIN". + # 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" @@ -188,17 +193,17 @@ def main(): f.write(p_data) print() - print(f"Loader BASIC (texto plano): {loader_txt_path}") + print(f"BASIC loader (plain text): {loader_txt_path}") print("---") print(loader_text, end="") print("---") - print(f"Loader BASIC tokenizado (.p): {loader_p_path} ({len(p_data)} bytes)") + print(f"Tokenized BASIC loader (.p): {loader_p_path} ({len(p_data)} bytes)") print() - print("NOTA: 'LOAD THEN CLEAR', 'LOAD *MAP' y 'LOAD FAST ... CODE' son extensiones") - print("del firmware SD81 Booster, codificadas con el mismo mecanismo de tokens que") - print("el BASIC estandar del ZX81 (ver zx81_p_loader.py).") - print(f"Copia {BOOT1_FILE}, los ficheros de pagina y {os.path.basename(loader_p_path)}") - print("a la tarjeta SD.") + 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__": diff --git a/src/arch/zx81sd/tools/zx81_p_loader.py b/src/arch/zx81sd/tools/zx81_p_loader.py index 75dfb166d..a2976503b 100644 --- a/src/arch/zx81sd/tools/zx81_p_loader.py +++ b/src/arch/zx81sd/tools/zx81_p_loader.py @@ -1,39 +1,41 @@ #!/usr/bin/env python3 """ -zx81_p_loader.py — Generador de ficheros .p (BASIC tokenizado) para ZX81 +zx81_p_loader.py — .p file (tokenized BASIC) generator for the ZX81 -Puerto directo a Python del algoritmo de tokenización usado por el emulador -EightyOne (zx81BasicLoader.cpp / IBasicLoader.cpp), referencia: +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 -Los comandos extendidos del firmware SD81 Booster (LOAD THEN CLEAR, LOAD *MAP, -LOAD FAST ... CODE, etc.) no necesitan tratamiento especial: aunque esa -sintaxis no la admite el intérprete de la ROM del ZX81, están compuestos por: - - Tokens estándar del ZX81 (THEN, CLEAR, FAST, CODE, LOAD...) en posiciones - que la ROM no usaría pero que SÍ se pueden teclear (p.ej. SHIFT+3 = THEN). - - Palabras sueltas tras un asterisco (p.ej. *MAP, *VER) que son letras - corrientes, no tokens. -Por tanto basta con tokenizar el texto igual que cualquier BASIC estándar: -el propio algoritmo genérico ya produce la codificación correcta. - -No implementa (no hace falta para este proyecto): etiquetas @label, bloques -numéricos [DEC:...]/[HEX:...]/[BIN:...], secuencias de escape \\xx, códigos -gráficos, ni "alternate keyword spelling". Documentados en IBasicLoader.cpp -si en el futuro hicieran falta. +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" # marcador interno: posición ya consumida +BLANK = "\x01" # internal marker: position already consumed NEWLINE = 0x76 -NUMBER_MARK = 0x7E # precede a los 5 bytes de número embebido -DOUBLE_QUOTE = 0xC0 # comilla escapada ("") dentro de una cadena +NUMBER_MARK = 0x7E # precedes the 5 bytes of an embedded number +DOUBLE_QUOTE = 0xC0 # escaped quote ("") inside a string -# Tokens estándar ZX81 (de zx81BasicLoader::ExtractTokens, sin variantes -# "alternate spelling" ni extensiones ZXpand) +# Standard ZX81 tokens (from zx81BasicLoader::ExtractTokens, without +# "alternate spelling" variants or ZXpand extensions) TOKENS = { 64: "RND", 65: "INKEY$", @@ -104,7 +106,7 @@ def ascii_to_zx(c: str) -> int: - """Puerto de zx81BasicLoader::AsciiToZX.""" + """Port of zx81BasicLoader::AsciiToZX.""" if c.isalpha(): return (ord(c.upper()) - ord('A')) + 38 if c.isdigit(): @@ -118,11 +120,11 @@ def ascii_to_zx(c: str) -> int: if c in table: return table[c] - raise ValueError(f"Caracter invalido: {c!r}") + raise ValueError(f"Invalid character: {c!r}") def zx81_float(value: float) -> bytes: - """Puerto de zx81BasicLoader::OutputFloatingPointEncoding.""" + """Port of zx81BasicLoader::OutputFloatingPointEncoding.""" exponent = 0 mantissa = 0 @@ -151,22 +153,22 @@ def zx81_float(value: float) -> bytes: class _Line: - """Estado de trabajo para una linea BASIC (sin numero de linea), replica - los buffers mLineBuffer / mLineBufferOutput / mLineBufferPopulated. + """Working state for a BASIC line (without a line number), mirroring + the mLineBuffer / mLineBufferOutput / mLineBufferPopulated buffers. - Todos los arrays tienen longitud self.n + 1: el ultimo hueco (indice n) - es un relleno de seguridad para cuando un token consume el espacio - artificial añadido al final para detectar tokens sin espacio de cierre - en el texto original (igual que el buffer sobredimensionado de C++). + 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 antepone siempre un espacio + content = " " + text # ReadLine always prepends a space self.n = len(content) - self.buf = list(content) + [BLANK] # +1 hueco de seguridad + self.buf = list(content) + [BLANK] # +1 safety slot self.out = [BLANK] * (self.n + 1) self.populated = [False] * (self.n + 1) - # BlankLineStart: sin numero de linea embebido, deja 1 espacio real + # BlankLineStart: no embedded line number, leaves 1 real space self.buf[0] = ' ' @@ -175,7 +177,7 @@ def _mask_copy(chars: list) -> list: def _mask_out_strings(tokenised: list): - """Puerto de IBasicLoader::MaskOutStrings.""" + """Port of IBasicLoader::MaskOutStrings.""" text = "".join(tokenised) q1 = text.find('"') if q1 == -1: @@ -205,7 +207,7 @@ def _mask_out_strings(tokenised: list): def _mask_out_rem_contents(tokenised: list): - """Puerto de IBasicLoader::MaskOutRemContents.""" + """Port of IBasicLoader::MaskOutRemContents.""" text = "".join(tokenised) rem = " REM " pos = 0 @@ -227,8 +229,8 @@ def _mask_out_rem_contents(tokenised: list): def _extract_double_quote_characters(line: _Line): - """Puerto de zx81BasicLoader::ExtractDoubleQuoteCharacters (sin soporte - de REM tokenizado ni alternate spelling, no necesarios aqui).""" + """Port of zx81BasicLoader::ExtractDoubleQuoteCharacters (without + tokenized-REM support or alternate spelling, not needed here).""" buf = line.buf n = line.n within_quote = False @@ -252,9 +254,9 @@ def _extract_double_quote_characters(line: _Line): def _do_tokenise(line: _Line, tokenised: list): - """Puerto de IBasicLoader::DoTokenise. Recorre los tokens de codigo mas - alto a mas bajo (equivalente al reverse_iterator sobre un std::map), - mutando `tokenised` (busqueda) y line.buf/out/populated (resultado).""" + """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) @@ -278,7 +280,7 @@ def _do_tokenise(line: _Line, tokenised: list): while True: guard += 1 if guard > 10000: - raise RuntimeError(f"Bucle de tokenizacion sin fin para {token!r}") + raise RuntimeError(f"Endless tokenizing loop for {token!r}") text = "".join(tokenised) pos = text.find(token) @@ -292,8 +294,8 @@ def _do_tokenise(line: _Line, tokenised: list): or (end_pos >= len(tokenised)) or (not tokenised[end_pos].isalnum())) if not (prev_ok and next_ok): - # Coincidencia pegada a un identificador: no es el token, - # se "rompe" localmente para seguir buscando mas adelante. + # Match glued to an identifier: not the token, "break" + # it locally so the search keeps going further ahead. tokenised[pos] = '\x02' continue @@ -315,22 +317,23 @@ def _do_tokenise(line: _Line, tokenised: list): def _start_of_number(line: _Line, tokenised: list, index: int) -> bool: - """Determina si `index` es el inicio de un literal numerico. - - Difiere deliberadamente del original IBasicLoader::StartOfNumber en un - punto: aqui solo se mira el caracter INMEDIATAMENTE anterior (retrocede - sobre espacios reales pero se detiene tan pronto encuentra CUALQUIER - caracter que no sea espacio, sin seguir retrocediendo mas alla). El - original retrocedia sobre TODOS los espacios consecutivos y comprobaba - el caracter previo a todos ellos, lo cual en BASIC estandar es - equivalente (los tokens siempre consumen su propio espacio final como - BLANK, no como ' ' literal) pero falla en comandos custom del SD81 como - "*MAP 7,8": "MAP" no es un token reconocido, por lo que el espacio tras - el queda como ' ' literal, y el algoritmo original terminaba mirando la - 'P' de MAP (alfabetica) y decidia que "7" NO era un numero. En un ZX81 - real, cualquier digito tecleado tras un espacio genera su propio float - embebido sin importar que palabra le precede, asi que aqui se replica - ese comportamiento (mas simple y mas correcto para este caso). + """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 @@ -349,8 +352,9 @@ def _start_of_number(line: _Line, tokenised: list, index: int) -> bool: def _output_embedded_number(line: _Line, index: int, out: bytearray) -> int: - """Puerto de IBasicLoader::OutputEmbeddedNumber. Devuelve el nuevo - indice (ya avanzado mas alla del numero, listo para `i += 1` fuera).""" + """Port of IBasicLoader::OutputEmbeddedNumber. Returns the new + index (already advanced past the number, ready for `i += 1` + outside).""" n = line.n text_no_spaces = "" @@ -360,7 +364,7 @@ def _output_embedded_number(line: _Line, index: int, out: bytearray) -> int: m = _NUMBER_RE.match(text_no_spaces) if not m or m.end() == 0: - raise ValueError(f"Numero invalido en posicion {index}: {text_no_spaces!r}") + raise ValueError(f"Invalid number at position {index}: {text_no_spaces!r}") number_len_no_spaces = m.end() value = float(m.group(0)) @@ -390,17 +394,18 @@ def _output_embedded_number(line: _Line, index: int, out: bytearray) -> int: def process_line_body(text: str) -> bytes: - """Tokeniza una linea BASIC (sin numero de linea) y devuelve los bytes - ya codificados (sin NEWLINE final, que se añade en encode_line).""" + """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) - # Copia de trabajo para deteccion de limites de token: el contenido real - # (sin el hueco de seguridad de line.buf) + un espacio real extra para - # detectar tokens sin espacio de cierre en el texto original. Longitud - # n+1, igual que line.buf, para que los indices sigan alineados. + # 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) @@ -408,7 +413,7 @@ def process_line_body(text: str) -> bytes: _do_tokenise(line, tokenised) - # Rellenar cualquier posicion no consumida con su codigo ZX81 directo + # 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]) @@ -433,14 +438,14 @@ def encode_line(line_number: int, text: str) -> bytes: def encode_program(lines: list) -> bytes: - """lines: lista de tuplas (numero_de_linea:int, texto:str).""" + """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: - """Puerto de zx81BasicLoader::OutputStartOfProgramData + - ProcessLine(*) + OutputEndOfProgramData. Devuelve el contenido completo - del fichero .p (desde 0x4009).""" + """Port of zx81BasicLoader::OutputStartOfProgramData + + ProcessLine(*) + OutputEndOfProgramData. Returns the complete + contents of the .p file (from 0x4009).""" data = bytearray() def out_byte(b): @@ -485,12 +490,12 @@ def change_word(offset, w): out_byte(0xBC) # PR_CC out_word(0x1821) # S_POSN out_byte(0x40) # CDFLAG - data.extend([0x00] * 32) # PRBUFF (32 bytes vacios) + data.extend([0x00] * 32) # PRBUFF (32 empty bytes) out_byte(0x76) # PRBUFF (byte 33, NEWLINE) data.extend([0x00] * 30) # MEMBOT out_word(0x0000) # SPARE - # --- Lineas BASIC --- + # --- BASIC lines --- for line_number, text in lines: data.extend(encode_line(line_number, text)) @@ -498,10 +503,10 @@ def change_word(offset, w): START_OF_RAM = 16393 # 0x4009 dfile_address = START_OF_RAM + len(data) - data.extend([NEWLINE] * 25) # display file colapsado (vacio) + data.extend([NEWLINE] * 25) # collapsed display file (empty) vars_address = START_OF_RAM + len(data) - out_byte(0x80) # fin de VARS (sin variables) + out_byte(0x80) # end of VARS (no variables) eline_address = START_OF_RAM + len(data) @@ -512,7 +517,7 @@ def change_word(offset, w): 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 => sin autorun) + change_word(32, vars_address) # NXTLIN (= VARS => no autorun) return bytes(data) @@ -520,7 +525,7 @@ def change_word(offset, w): if __name__ == "__main__": import sys - # Autotest rapido con el loader validado manualmente + # Quick self-test with the manually validated loader lines = [ (2, "FAST"), (5, "LOAD THEN CLEAR 24575"), @@ -538,7 +543,7 @@ def change_word(offset, w): with open(out_path, "wb") as f: f.write(p_data) - print(f"Generado {out_path} ({len(p_data)} bytes)") + 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) From 54d4d3650603b394dd4806de197f69a8a1f8c24e Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 12:52:19 +0200 Subject: [PATCH 149/169] zx81sd: mark print42.bas/print64.bas as confirmed on real hardware Co-Authored-By: Claude Sonnet 5 --- src/arch/zx81sd/README.md | 10 ++++------ src/arch/zx81sd/doc/BASIC_CHANGES.md | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/arch/zx81sd/README.md b/src/arch/zx81sd/README.md index 3ed854eec..d2bbe9ef5 100644 --- a/src/arch/zx81sd/README.md +++ b/src/arch/zx81sd/README.md @@ -48,12 +48,10 @@ Pending / not audited, remaining screen utilities in the shared stdlib: - `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** (full override in `stdlib/`) - — fixed sysvars → zx81sd equivalents, and the screen/attribute base - constants are patched at runtime (self-modifying code) instead of - being fixed. Verified by simulation (legible pixel-by-pixel text, no - memory corruption); pending confirmation on the emulator/real - hardware. +- `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 diff --git a/src/arch/zx81sd/doc/BASIC_CHANGES.md b/src/arch/zx81sd/doc/BASIC_CHANGES.md index 13dde89f5..f7ce909d4 100644 --- a/src/arch/zx81sd/doc/BASIC_CHANGES.md +++ b/src/arch/zx81sd/doc/BASIC_CHANGES.md @@ -324,8 +324,7 @@ 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`. Still pending confirmation on the -emulator/real hardware. +normal `__END_PROGRAM`/`HALT`. **Confirmed working on real hardware.** --- From ecd94da361ff57ca8165212b3997e3b7790eda85 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 13:04:32 +0200 Subject: [PATCH 150/169] zx81sd: add MCU command examples (filesystem, WAV, VGM, PEG, joystick, F_*) New examples exercising the SD81 Booster MCU's command surface (stdlib/mcu.bas/joy.bas), all compile clean but not yet run on real hardware: - filesystem.bas (expanded): McuPwd/McuCd/McuDirPrint/McuMkdir/ McuRmdir/McuSave/McuCopy/McuMove/McuDel/McuFree - wavplayer.bas: McuLoad with a .WAV file (MCU plays it directly) - vgmplayer.bas: McuPlayVgm/McuContVgm/McuPauseVgm/McuStopVgm - pegdemo.bas: McuLoadPeg/McuPlayPeg/McuStopPeg (byte encoding unconfirmed on hardware, see BASIC_CHANGES.md) - joystick.bas: Joy() programmable joystick mapping - randomaccess.bas: McuFOpenZx/McuFSeek/McuFRead/McuFWrite/McuFClose Found along the way: stdlib/input.bas defines input() as a function (a$ = input(n)), not the native INPUT a$ statement; using the statement form after #include fails to compile. Documented in BASIC_CHANGES.md as a new pattern to watch for. Co-Authored-By: Claude Sonnet 5 --- examples/sd81/filesystem.bas | 66 +++++++++++++++++++++----- examples/sd81/joystick.bas | 29 ++++++++++++ examples/sd81/pegdemo.bas | 54 +++++++++++++++++++++ examples/sd81/randomaccess.bas | 70 ++++++++++++++++++++++++++++ examples/sd81/vgmplayer.bas | 50 ++++++++++++++++++++ examples/sd81/wavplayer.bas | 25 ++++++++++ src/arch/zx81sd/doc/BASIC_CHANGES.md | 51 ++++++++++++++++++++ 7 files changed, 334 insertions(+), 11 deletions(-) create mode 100644 examples/sd81/joystick.bas create mode 100644 examples/sd81/pegdemo.bas create mode 100644 examples/sd81/randomaccess.bas create mode 100644 examples/sd81/vgmplayer.bas create mode 100644 examples/sd81/wavplayer.bas diff --git a/examples/sd81/filesystem.bas b/examples/sd81/filesystem.bas index 5adce9089..b21451717 100644 --- a/examples/sd81/filesystem.bas +++ b/examples/sd81/filesystem.bas @@ -1,11 +1,55 @@ -#include -#include -dim version as ubyte -version = McuVersion() -print "SD81 OS version: ";version -print "Current dir: "; McuPwd() -if McuCd("/")=0 then print "Change to root dir" else print "error" -if McuCd("/test")=0 then print "Change to test dir" else print "error" -McuDirPrint("/test/*.*") -McuLoad("/DEMO.WAV",0) -input a$ \ No newline at end of file +' ---------------------------------------------------------------------- +' 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/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/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/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/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/src/arch/zx81sd/doc/BASIC_CHANGES.md b/src/arch/zx81sd/doc/BASIC_CHANGES.md index f7ce909d4..09c250e5b 100644 --- a/src/arch/zx81sd/doc/BASIC_CHANGES.md +++ b/src/arch/zx81sd/doc/BASIC_CHANGES.md @@ -328,6 +328,51 @@ 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` | Compiles clean; not yet run on hardware | +| `examples/sd81/wavplayer.bas` | `McuLoad` with a `.WAV` file (played directly by the MCU, not loaded into memory) | Compiles clean; not yet run on hardware | +| `examples/sd81/vgmplayer.bas` | `McuPlayVgm`, `McuContVgm`, `McuPauseVgm`, `McuStopVgm` | Compiles clean; not yet run on hardware | +| `examples/sd81/pegdemo.bas` | `McuLoadPeg`, `McuPlayPeg`, `McuStopPeg` | Compiles clean; **PEG byte encoding unconfirmed on hardware**, see note below | +| `examples/sd81/joystick.bas` | `Joy()` (joy.bas, MCU cmd 21) | Compiles clean; not yet run on hardware | +| `examples/sd81/randomaccess.bas` | `McuFOpenZx`, `McuFSeek`, `McuFRead`, `McuFWrite`, `McuFClose` | Compiles clean; not yet run on 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, open question**: 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`), but `mcu.bas`'s own +comment on `McuLoadPeg` says the raw bytes it expects are +"2 bytes little-endian per instruction". `pegdemo.bas` follows the +`mcu.bas` comment literally (since that's the function actually being +called) — each instruction's value byte is sent before its opcode +byte. This hasn't been confirmed against a working `.PEB` file or the +`peg.py` assembler mentioned in the manual, so treat the exact byte +order as unverified until tested on hardware. + +--- + ## Summary for the manual | Example | zx81sd copy | Source changes | Compile flags | @@ -341,6 +386,7 @@ normal `__END_PROGRAM`/`HALT`. **Confirmed working on real hardware.** | 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: @@ -365,3 +411,8 @@ Pattern identified for future examples: 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. From 46700e3046b1ae7abc144356b5d0ef02cd6ac146 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 13:33:17 +0200 Subject: [PATCH 151/169] zx81sd: mark MCU command examples as confirmed on real hardware All six (filesystem, wavplayer, vgmplayer, pegdemo, joystick, randomaccess) tested OK, including pegdemo's PEG byte encoding. Co-Authored-By: Claude Sonnet 5 --- src/arch/zx81sd/doc/BASIC_CHANGES.md | 32 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/arch/zx81sd/doc/BASIC_CHANGES.md b/src/arch/zx81sd/doc/BASIC_CHANGES.md index 09c250e5b..b5f2d2ea0 100644 --- a/src/arch/zx81sd/doc/BASIC_CHANGES.md +++ b/src/arch/zx81sd/doc/BASIC_CHANGES.md @@ -340,12 +340,12 @@ from) and was expanded to also demo `McuMkdir`/`McuRmdir`/`McuCopy`/ | File | MCU commands exercised | Status | |---|---|---| -| `examples/sd81/filesystem.bas` | `McuPwd`, `McuCd`, `McuDirPrint`, `McuMkdir`, `McuRmdir`, `McuSave`, `McuCopy`, `McuMove`, `McuDel`, `McuFree` | Compiles clean; not yet run on hardware | -| `examples/sd81/wavplayer.bas` | `McuLoad` with a `.WAV` file (played directly by the MCU, not loaded into memory) | Compiles clean; not yet run on hardware | -| `examples/sd81/vgmplayer.bas` | `McuPlayVgm`, `McuContVgm`, `McuPauseVgm`, `McuStopVgm` | Compiles clean; not yet run on hardware | -| `examples/sd81/pegdemo.bas` | `McuLoadPeg`, `McuPlayPeg`, `McuStopPeg` | Compiles clean; **PEG byte encoding unconfirmed on hardware**, see note below | -| `examples/sd81/joystick.bas` | `Joy()` (joy.bas, MCU cmd 21) | Compiles clean; not yet run on hardware | -| `examples/sd81/randomaccess.bas` | `McuFOpenZx`, `McuFSeek`, `McuFRead`, `McuFWrite`, `McuFClose` | Compiles clean; not yet run on hardware | +| `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 @@ -360,16 +360,16 @@ 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, open question**: 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`), but `mcu.bas`'s own -comment on `McuLoadPeg` says the raw bytes it expects are -"2 bytes little-endian per instruction". `pegdemo.bas` follows the -`mcu.bas` comment literally (since that's the function actually being -called) — each instruction's value byte is sent before its opcode -byte. This hasn't been confirmed against a working `.PEB` file or the -`peg.py` assembler mentioned in the manual, so treat the exact byte -order as unverified until tested on hardware. +**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. --- From dbe0e9d8c235141ad692e05606382a2621dd76c0 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:33:26 +0200 Subject: [PATCH 152/169] zx81sd: fix ruff findings and formatting - split_sd81.py: sort/organize the import block - zx81_p_loader.py: drop redundant int() around math.floor() (RUF046) - ruff format on the three files touched this session ruff check/mypy now clean project-wide; the only remaining ruff format finding (src/zxbpp/zxbpp.py) is pre-existing and unrelated to zx81sd, left untouched. Co-Authored-By: Claude Sonnet 5 --- src/arch/zx81sd/backend/main.py | 48 +++---- src/arch/zx81sd/tools/split_sd81.py | 15 +- src/arch/zx81sd/tools/zx81_p_loader.py | 189 ++++++++++++++----------- 3 files changed, 133 insertions(+), 119 deletions(-) diff --git a/src/arch/zx81sd/backend/main.py b/src/arch/zx81sd/backend/main.py index a3d3ecc9a..6efddcb0b 100644 --- a/src/arch/zx81sd/backend/main.py +++ b/src/arch/zx81sd/backend/main.py @@ -27,11 +27,11 @@ # $D800-$DAFF screen attributes # $E000-$FFFF block 7 — data banking (maps, sprites...) -_ORG = 0x0000 # the binary starts at $0000 (vectors.asm pads up to $0100) +_ORG = 0x0000 # the binary starts at $0000 (vectors.asm pads 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 +_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 @@ -42,7 +42,7 @@ # Page 13 -> block 5 ($A000-$BFFF) ┘ _PAGE_MAP = [ - (1, 9), # block 1 -> page 9 + (1, 9), # block 1 -> page 9 (2, 10), # block 2 -> page 10 (3, 11), # block 3 -> page 11 (4, 12), # block 4 -> page 12 @@ -50,7 +50,7 @@ ] _SD81_PAGE_PORT = 0xE7 # memory mapper port (full mode: OUT (C), A) -_STACK_TOP = 0x7FFF # stack at the top of the executable area +_STACK_TOP = 0x7FFF # stack at the top of the executable area def _map_block(block: int, page: int) -> list[str]: @@ -80,12 +80,9 @@ def init(self): 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) + 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( { @@ -119,43 +116,32 @@ def emit_prologue() -> list[str]: 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) - ) + 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 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}" - ) + 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("jp $0100") # $0000: reset / RST 0 -> stage 2 output.append("org $0008") - output.append("di") # $0008: RST $08 (Spectrum error handler) + 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("di") # $0010-$0037: unused RSTs output.append("halt") output.append("org $0018") output.append("di") @@ -169,10 +155,10 @@ def emit_prologue() -> list[str]: output.append("di") output.append("halt") output.append("org $0038") - output.append("di") # $0038: RST $38 IM1 — permanent DI, never reached + output.append("di") # $0038: RST $38 IM1 — permanent DI, never reached output.append("halt") output.append("org $0066") - output.append("retn") # $0066: NMI disabled, but the vector must exist + output.append("retn") # $0066: NMI disabled, but the vector must exist # -- Stage 2 bootstrap at $0100 ------------------------------------ output.append(f"org {_STAGE2_ENTRY}") diff --git a/src/arch/zx81sd/tools/split_sd81.py b/src/arch/zx81sd/tools/split_sd81.py index 51e29fc83..9f8bfc9e6 100644 --- a/src/arch/zx81sd/tools/split_sd81.py +++ b/src/arch/zx81sd/tools/split_sd81.py @@ -62,17 +62,17 @@ -> TEST_SD81.P (the same listing, tokenized) """ -import sys 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) +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) +BOOT1_ADDR = 24576 # $6000 +CLEAR_ADDR = 24575 # protects BOOT1.BIN (reserved right below it) LOAD_WINDOW_ADDR = 57344 # $E000, block 7's window @@ -148,6 +148,7 @@ def main(): # 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) @@ -162,7 +163,7 @@ def main(): 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"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() diff --git a/src/arch/zx81sd/tools/zx81_p_loader.py b/src/arch/zx81sd/tools/zx81_p_loader.py index a2976503b..5b10bcdb2 100644 --- a/src/arch/zx81sd/tools/zx81_p_loader.py +++ b/src/arch/zx81sd/tools/zx81_p_loader.py @@ -29,10 +29,10 @@ import re import struct -BLANK = "\x01" # internal marker: position already consumed +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 +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) @@ -108,14 +108,29 @@ def ascii_to_zx(c: str) -> int: """Port of zx81BasicLoader::AsciiToZX.""" if c.isalpha(): - return (ord(c.upper()) - ord('A')) + 38 + return (ord(c.upper()) - ord("A")) + 38 if c.isdigit(): - return (ord(c) - ord('0')) + 28 + 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, + " ": 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] @@ -133,23 +148,25 @@ def zx81_float(value: float) -> bytes: if neg: value = -value - exponent = int(math.floor(1e-12 + (math.log(value) / math.log(2.0)))) + 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 = (value / (2.0**exponent)) - 1 mantissa_val *= 0x80000000 - mantissa = int(math.floor(mantissa_val)) + mantissa = math.floor(mantissa_val) exponent += 129 - return bytes([ - exponent & 0xFF, - (mantissa >> 24) & 0xFF, - (mantissa >> 16) & 0xFF, - (mantissa >> 8) & 0xFF, - mantissa & 0xFF, - ]) + return bytes( + [ + exponent & 0xFF, + (mantissa >> 24) & 0xFF, + (mantissa >> 16) & 0xFF, + (mantissa >> 8) & 0xFF, + mantissa & 0xFF, + ] + ) class _Line: @@ -163,13 +180,13 @@ class _Line: """ def __init__(self, text: str): - content = " " + text # ReadLine always prepends a space + content = " " + text # ReadLine always prepends a space self.n = len(content) - self.buf = list(content) + [BLANK] # +1 safety slot + 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] = ' ' + self.buf[0] = " " def _mask_copy(chars: list) -> list: @@ -196,7 +213,7 @@ def _mask_out_strings(tokenised: list): elif within: tokenised[i] = BLANK else: - rest = "".join(tokenised[i + 1:]) + rest = "".join(tokenised[i + 1 :]) nq = rest.find('"') if nq == -1: return @@ -264,16 +281,18 @@ def _do_tokenise(line: _Line, tokenised: list): start_char = token[0] end_char = token[-1] - token_begins_with_space = start_char == ' ' + token_begins_with_space = start_char == " " token_begins_with_alpha = start_char.isalpha() - token_ends_with_space = end_char == ' ' + 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 != "**"): + 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 @@ -287,16 +306,24 @@ def _do_tokenise(line: _Line, tokenised: list): 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())) + 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())) + 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' + tokenised[pos] = "\x02" continue start_offset = 1 if token_begins_with_space else 0 @@ -335,20 +362,20 @@ def _start_of_number(line: _Line, tokenised: list, index: int) -> bool: 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()): + if not (line.buf[index] == "." or line.buf[index].isdigit()): return False if index == 0: return True prev = line.buf[index - 1] - if prev == ' ': + if prev == " ": return True return not (prev.isalpha() or prev.isdigit()) -_NUMBER_RE = re.compile(r'[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?') +_NUMBER_RE = re.compile(r"[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?") def _output_embedded_number(line: _Line, index: int, out: bytearray) -> int: @@ -370,7 +397,7 @@ def _output_embedded_number(line: _Line, index: int, out: bytearray) -> int: with_spaces_index = 0 for _ in range(number_len_no_spaces): - while line.buf[index + with_spaces_index] in (' ', BLANK): + while line.buf[index + with_spaces_index] in (" ", BLANK): with_spaces_index += 1 with_spaces_index += 1 @@ -383,8 +410,8 @@ def _output_embedded_number(line: _Line, index: int, out: bytearray) -> int: out.append(chr_code) i += 1 - while i < n and line.buf[i] == ' ': - out.append(ascii_to_zx(' ')) + while i < n and line.buf[i] == " ": + out.append(ascii_to_zx(" ")) i += 1 out.append(NUMBER_MARK) @@ -406,7 +433,7 @@ def process_line_body(text: str) -> bytes: # (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]) + [' '] + tokenised = list(line.buf[:n]) + [" "] _mask_out_strings(tokenised) _mask_out_rem_contents(tokenised) @@ -434,7 +461,7 @@ def process_line_body(text: str) -> bytes: def encode_line(line_number: int, text: str) -> bytes: body = process_line_body(text) + bytes([NEWLINE]) - return struct.pack('>H', line_number) + struct.pack('H", line_number) + struct.pack(" bytes: @@ -460,40 +487,40 @@ def change_word(offset, w): 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 + 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) + out_byte(0x76) # PRBUFF (byte 33, NEWLINE) data.extend([0x00] * 30) # MEMBOT - out_word(0x0000) # SPARE + out_word(0x0000) # SPARE # --- BASIC lines --- for line_number, text in lines: @@ -510,14 +537,14 @@ def change_word(offset, w): 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) + 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) @@ -545,6 +572,6 @@ def change_word(offset, w): print(f"Generated {out_path} ({len(p_data)} bytes)") for i in range(0, len(p_data), 16): - chunk = p_data[i:i + 16] + chunk = p_data[i : i + 16] hexpart = " ".join(f"{b:02X}" for b in chunk) print(f"{i:04X}: {hexpart}") From 5d0a02d25be0766d3d7ece067039e4ef119b7dbc Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:51:20 +0200 Subject: [PATCH 153/169] zx81sd: confirm examples/winscroll.bas needs no adaptation Audited: WinScrollRight/Left/Up/Down have no Spectrum ROM dependency (everything reads SCREEN_ADDR/SCREEN_ATTR_ADDR dynamically), and the sysvars.asm dependency goes through #require (a link-time tag, not a textual #include), so it doesn't risk the INCBIN-inline bug found while porting print42.bas/print64.bas. Compiles clean and verified by simulation (screen/attributes fully touched, clean HALT at __END_PROGRAM). Pending confirmation on real hardware. Co-Authored-By: Claude Sonnet 5 --- src/arch/zx81sd/README.md | 8 ++++---- src/arch/zx81sd/doc/BASIC_CHANGES.md | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/arch/zx81sd/README.md b/src/arch/zx81sd/README.md index d2bbe9ef5..8c1798898 100644 --- a/src/arch/zx81sd/README.md +++ b/src/arch/zx81sd/README.md @@ -37,10 +37,10 @@ mapper...) and LOAD/SAVE/VERIFY...CODE against SD. Pending / not audited, remaining screen utilities in the shared stdlib: -- `winscroll.bas`: believed to be already ported and tested, but not - confirmed by a formal audit (no override in `zx81sd/stdlib/` — if - true, it's because it didn't need one, like `scroll.bas` before its - fix, or `4inarow.bas`). +- `winscroll.bas`: audited — no ROM dependency, no override needed + (like `scroll.bas` before its fix, or `4inarow.bas`). Compiles clean + and verified by simulation (`examples/winscroll.bas`, no source + changes needed); pending confirmation on real hardware. - `putchars.bas`/`puttile.bas`: not audited or tested. A quick look at the source finds no Spectrum ROM/sysvar addresses (`putChars` fills a rectangle of characters, `putTile` places a 16×16 px tile), so diff --git a/src/arch/zx81sd/doc/BASIC_CHANGES.md b/src/arch/zx81sd/doc/BASIC_CHANGES.md index b5f2d2ea0..ed30868b5 100644 --- a/src/arch/zx81sd/doc/BASIC_CHANGES.md +++ b/src/arch/zx81sd/doc/BASIC_CHANGES.md @@ -166,6 +166,28 @@ implementation, same register contract as the ROM — see --- +## 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). Pending +confirmation on real hardware. + +--- + ## 6. `examples/maskedsprites.bas` → `examples/sd81/maskedsprites_sd81.bas` **In progress — still not fully working.** @@ -382,6 +404,7 @@ documented in the library. | 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 | | 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 | From 470cfbffd4396a645c18971ccb3a11281c37968a Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:56:48 +0200 Subject: [PATCH 154/169] zx81sd: mark winscroll.bas as confirmed on real hardware Co-Authored-By: Claude Sonnet 5 --- src/arch/zx81sd/README.md | 8 ++++---- src/arch/zx81sd/doc/BASIC_CHANGES.md | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/arch/zx81sd/README.md b/src/arch/zx81sd/README.md index 8c1798898..155e33645 100644 --- a/src/arch/zx81sd/README.md +++ b/src/arch/zx81sd/README.md @@ -37,10 +37,10 @@ mapper...) and LOAD/SAVE/VERIFY...CODE against SD. Pending / not audited, remaining screen utilities in the shared stdlib: -- `winscroll.bas`: audited — no ROM dependency, no override needed - (like `scroll.bas` before its fix, or `4inarow.bas`). Compiles clean - and verified by simulation (`examples/winscroll.bas`, no source - changes needed); pending confirmation on real hardware. +- `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`/`puttile.bas`: not audited or tested. A quick look at the source finds no Spectrum ROM/sysvar addresses (`putChars` fills a rectangle of characters, `putTile` places a 16×16 px tile), so diff --git a/src/arch/zx81sd/doc/BASIC_CHANGES.md b/src/arch/zx81sd/doc/BASIC_CHANGES.md index ed30868b5..341489011 100644 --- a/src/arch/zx81sd/doc/BASIC_CHANGES.md +++ b/src/arch/zx81sd/doc/BASIC_CHANGES.md @@ -183,8 +183,8 @@ 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). Pending -confirmation on real hardware. +touched, clean `HALT` at `__END_PROGRAM`, no illegal writes). +**Confirmed working on real hardware.** --- From 0fbb40000c4cf9e24d5b84e2ee038cac2e246d87 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 17:12:41 +0200 Subject: [PATCH 155/169] zx81sd: port puttile.bas, confirm putchars.bas needs no changes putchars.bas: audited, no ROM dependency (SCREEN_ADDR/SCREEN_ATTR_ADDR read dynamically, #require "sysvars.asm" is a safe link-time tag) -- no override needed, used as-is. puttile.bas: needed an override -- 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. Same bug class as print42.bas/print64.bas; fixed the same way (self-modifying code patches both constants once on entering putTile(), reading the real high bytes at that point). New example examples/sd81/putcharstile.bas exercises putChars/paint/ putTile. Verified by simulation with breakpoints right after each function returns (a naive end-of-program check looked broken due to an unrelated screen scroll triggered by the demo's own final PRINT AT landing on the last screen row -- moved to row 20, false alarm). Pending confirmation on real hardware. Co-Authored-By: Claude Sonnet 5 --- examples/sd81/putcharstile.bas | 48 ++++ src/arch/zx81sd/README.md | 13 +- src/arch/zx81sd/doc/BASIC_CHANGES.md | 39 +++ src/lib/arch/zx81sd/stdlib/puttile.bas | 325 +++++++++++++++++++++++++ 4 files changed, 421 insertions(+), 4 deletions(-) create mode 100644 examples/sd81/putcharstile.bas create mode 100644 src/lib/arch/zx81sd/stdlib/puttile.bas 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/src/arch/zx81sd/README.md b/src/arch/zx81sd/README.md index 155e33645..9e40683b0 100644 --- a/src/arch/zx81sd/README.md +++ b/src/arch/zx81sd/README.md @@ -41,10 +41,15 @@ Pending / not audited, remaining screen utilities in the shared stdlib: 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`/`puttile.bas`: not audited or tested. A quick look at - the source finds no Spectrum ROM/sysvar addresses (`putChars` fills a - rectangle of characters, `putTile` places a 16×16 px tile), so - they're good candidates to work unchanged, but this isn't confirmed. +- `putchars.bas`: audited — no ROM dependency, no override needed. + Compiles clean and verified by simulation + (`examples/sd81/putcharstile.bas`); pending confirmation on real + hardware. +- `puttile.bas`: **did need a zx81sd override** — hardcoded screen/ + attribute base constants, same bug class as `print42.bas`/ + `print64.bas` (fixed the same way, self-modifying code patched at + runtime). Verified by simulation; pending confirmation on real + hardware. - `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. diff --git a/src/arch/zx81sd/doc/BASIC_CHANGES.md b/src/arch/zx81sd/doc/BASIC_CHANGES.md index 341489011..6a1053a7d 100644 --- a/src/arch/zx81sd/doc/BASIC_CHANGES.md +++ b/src/arch/zx81sd/doc/BASIC_CHANGES.md @@ -188,6 +188,44 @@ touched, clean `HALT` at `__END_PROGRAM`, no illegal writes). --- +## 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. + +--- + ## 6. `examples/maskedsprites.bas` → `examples/sd81/maskedsprites_sd81.bas` **In progress — still not fully working.** @@ -405,6 +443,7 @@ documented in the library. | 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 | 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 From 9f961b3538a41392252d99e4b4fb76822d898b70 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 17:20:26 +0200 Subject: [PATCH 156/169] zx81sd: mark putchars.bas/puttile.bas as confirmed on real hardware Co-Authored-By: Claude Sonnet 5 --- src/arch/zx81sd/README.md | 16 +++++++--------- src/arch/zx81sd/doc/BASIC_CHANGES.md | 1 + 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/arch/zx81sd/README.md b/src/arch/zx81sd/README.md index 9e40683b0..8e781eb6c 100644 --- a/src/arch/zx81sd/README.md +++ b/src/arch/zx81sd/README.md @@ -41,15 +41,13 @@ Pending / not audited, remaining screen utilities in the shared stdlib: 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`: audited — no ROM dependency, no override needed. - Compiles clean and verified by simulation - (`examples/sd81/putcharstile.bas`); pending confirmation on real - hardware. -- `puttile.bas`: **did need a zx81sd override** — hardcoded screen/ - attribute base constants, same bug class as `print42.bas`/ - `print64.bas` (fixed the same way, self-modifying code patched at - runtime). Verified by simulation; pending confirmation on real - hardware. +- `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. diff --git a/src/arch/zx81sd/doc/BASIC_CHANGES.md b/src/arch/zx81sd/doc/BASIC_CHANGES.md index 6a1053a7d..255cb592c 100644 --- a/src/arch/zx81sd/doc/BASIC_CHANGES.md +++ b/src/arch/zx81sd/doc/BASIC_CHANGES.md @@ -223,6 +223,7 @@ 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.** --- From 0fb1f2ae2229675000dc834b3169b95af3a634e8 Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 17:27:22 +0200 Subject: [PATCH 157/169] make tools executable --- src/arch/zx81sd/tools/split_sd81.py | 0 src/arch/zx81sd/tools/zx81_p_loader.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 src/arch/zx81sd/tools/split_sd81.py mode change 100644 => 100755 src/arch/zx81sd/tools/zx81_p_loader.py diff --git a/src/arch/zx81sd/tools/split_sd81.py b/src/arch/zx81sd/tools/split_sd81.py old mode 100644 new mode 100755 diff --git a/src/arch/zx81sd/tools/zx81_p_loader.py b/src/arch/zx81sd/tools/zx81_p_loader.py old mode 100644 new mode 100755 From ef8a727a38d7c312b7b5f5bc97e0b9cab08ae6af Mon Sep 17 00:00:00 2001 From: wilco2009 <43153593+wilco2009@users.noreply.github.com> Date: Sun, 5 Jul 2026 20:14:12 +0200 Subject: [PATCH 158/169] zx81sd: activate Chroma81 colour explicitly in stage 1 boot Found on real hardware: activating Superfast HiRes Spectrum mode (POKE 2045,172) does not turn on Chroma81 colour by itself -- screen came up monochrome despite correct INK/PAPER/attributes. EightyOne shows colour regardless, which is an emulator bug (needs reporting/ fixing there separately, not something to work around here). Fix: boot1.asm now also writes to the Chroma81 port ($7FEF) right after activating Spectrum mode: ld bc, $7FEF / ld a, 39 / out (c), a (bit5=1 color on, bit4=0 char-code mode) boot1.bin reassembled with zxbasm.py (34 bytes, was 27 -- exactly the 3 new instructions). Shared stage 1 loader used by every zx81sd program: no program needs recompiling, just copy the updated BOOT1.BIN to the SD card. Co-Authored-By: Claude Sonnet 5 --- src/arch/zx81sd/doc/MAP.md | 27 +++++++++++++++++++++++++++ src/arch/zx81sd/tools/boot1.asm | 25 ++++++++++++++++++++++--- src/arch/zx81sd/tools/boot1.bin | Bin 27 -> 34 bytes 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/arch/zx81sd/doc/MAP.md b/src/arch/zx81sd/doc/MAP.md index 758cb67b6..e9f2d7fef 100644 --- a/src/arch/zx81sd/doc/MAP.md +++ b/src/arch/zx81sd/doc/MAP.md @@ -531,3 +531,30 @@ 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. diff --git a/src/arch/zx81sd/tools/boot1.asm b/src/arch/zx81sd/tools/boot1.asm index 7eaa5830f..6ed9ea28c 100644 --- a/src/arch/zx81sd/tools/boot1.asm +++ b/src/arch/zx81sd/tools/boot1.asm @@ -9,15 +9,22 @@ ; 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. Disable memory-mapped IO (POKE 2056 = $0808) -; 4. Map block 0 -> page 8 (OUT ($E7), page=8, block=0) +; 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. -; 5. JP $0100 — jumps to stage 2 in the freshly mapped RAM +; 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). ; @@ -55,6 +62,18 @@ SD81_STAGE1: 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 diff --git a/src/arch/zx81sd/tools/boot1.bin b/src/arch/zx81sd/tools/boot1.bin index 0fc567529aa295b6cf20ef3bc636ee3ce75b37f6..bbaa78a56e9af8a24128a02e26780a1a98ad7115 100644 GIT binary patch literal 34 qcmeyY$Z$aEH@n>$qrdEo@9XW<-&U?S;^1K8uw&qR{$qrdFyjW{^iIP4hsp1-X;%)kf$jm8Oe From 6add187ea942ce08068e6817558badd330886244 Mon Sep 17 00:00:00 2001 From: wilco2009 Date: Sun, 5 Jul 2026 23:01:33 +0200 Subject: [PATCH 159/169] lint: format code --- .bumpversion.cfg | 36 +- .coveragerc | 6 +- .gitattributes | 36 +- .github/ISSUE_TEMPLATE/bug_report.yaml | 108 +- .github/ISSUE_TEMPLATE/bug_report_es.yaml | 108 +- .gitignore | 52 +- .pre-commit-config.yaml | 40 +- .readthedocs.yaml | 42 +- docs/imgs.sh | 30 +- docs/overrides/main.html | 10 +- docs/overrides/stylesheets/extra.css | 296 +-- poetry.lock | 2204 ++++++++--------- pyproject.toml | 374 +-- .../z80/peephole/opts/000_o1_push_pop.opt | 28 +- .../z80/peephole/opts/000_o3_push_pop.opt | 28 +- .../peephole/opts/001_o1_ld_XXYY_ldYYXX.opt | 36 +- .../peephole/opts/002_o1_push_hlde_to_ex.opt | 50 +- .../z80/peephole/opts/003_o1_useless_jp.opt | 34 +- .../peephole/opts/004_o1_push_pop_ldld.opt | 54 +- .../peephole/opts/005_o1_push_af_ld_aX.opt | 46 +- .../peephole/opts/006_o1_push_af_pop_XX.opt | 54 +- .../peephole/opts/006_o3_push_af_pop_XX.opt | 54 +- .../z80/peephole/opts/007_o1_ex_de_hl.opt | 32 +- src/arch/z80/peephole/opts/008_o1_sbc_jp.opt | 56 +- src/arch/z80/peephole/opts/009_o1_inc_mem.opt | 60 +- .../opts/010_o1_ld_de_hl_ex_de_hl.opt | 66 +- .../opts/011_o1_ld_h_a_pop_af_or_h.opt | 54 +- .../opts/012_o1_ld_hl_push_pop_de.opt | 54 +- src/arch/z80/peephole/opts/013_o1_neg_neg.opt | 32 +- .../z80/peephole/opts/014_o1_or_sbc_a_a.opt | 44 +- .../opts/015_o3_ld_r_n_not_required.opt | 36 +- .../z80/peephole/opts/016_o1_xor_a_jp_z.opt | 44 +- .../peephole/opts/017_jp_cond_jp_labell.opt | 60 +- .../z80/peephole/opts/018_EQ16_by_sbchl.opt | 66 +- .../peephole/opts/019_sub1_jpnc_ora_jpz.opt | 56 +- .../peephole/opts/020_o1_bool_norm_empty.opt | 64 +- .../peephole/opts/021_o1_bool_norm_neg.opt | 52 +- .../z80/peephole/opts/022_insr_a_or_a.opt | 48 +- .../z80/peephole/opts/023_ld_hl_bc_outl.opt | 54 +- .../opts/024_o1_bool_norm_duble_neg.opt | 46 +- .../z80/peephole/opts/025_ld_h_a_oper_A.opt | 54 +- .../z80/peephole/opts/027_ld_h_a_oper_A.opt | 44 +- src/arch/z80/peephole/opts/028_o2_pop_up.opt | 56 +- src/arch/z80/peephole/opts/028_o3_pop_up.opt | 54 +- src/arch/z80/peephole/opts/029_cp_0_or_a.opt | 40 +- .../z80/peephole/opts/030_ora_jp_nc_jp.opt | 44 +- src/arch/z80/peephole/opts/031_jpX_Y_jpX.opt | 50 +- .../peephole/opts/032_call_LOADSTR_ld_a1.opt | 44 +- .../peephole/opts/050_o1_ld_a_ld_h_pop.opt | 58 +- .../peephole/opts/051_o1_ld_a_ld_h_a_pop.opt | 58 +- .../z80/peephole/opts/052_o3_ldhl_ld_a_l.opt | 44 +- src/arch/z80/peephole/opts/053_o1_ldh_nnn.opt | 42 +- .../peephole/opts/054_o3_ld_a_N_ld_hl_a.opt | 48 +- .../peephole/opts/100_o3_ld_rr_N_ld_ss_N.opt | 40 +- .../opts/101_o3_ld_rr_N_ld_h_r_ld_l_r.opt | 72 +- .../peephole/opts/102_o3_ld_r_n_ld_r_n2.opt | 36 +- .../z80/peephole/opts/103_o3_or_and_a.opt | 34 +- src/arch/z80/peephole/opts/103_o3_xor_a.opt | 30 +- src/arch/z80/peephole/opts/104_o3_opt27.opt | 58 +- src/arch/z80/peephole/opts/105_o3_opt27.opt | 58 +- src/arch/z80/peephole/opts/106_o3_exdehl.opt | 46 +- .../z80/peephole/opts/107_o3_exdehl_ldde.opt | 50 +- src/arch/z80/peephole/opts/108_o3_inc_dec.opt | 56 +- .../peephole/opts/109_o4_ld_mem_op_ld_mem.opt | 34 +- .../peephole/opts/110_o4_ld_mem_op_ld_mem.opt | 38 +- .../zxnext/peephole/opts/060_o1_mul_de.opt | 50 +- src/lib/arch/zx48k/runtime/spectranet.inc | 286 +-- src/lib/arch/zx48k/stdlib/README | 8 +- src/lib/arch/zxnext/runtime/spectranet.inc | 286 +-- src/lib/arch/zxnext/stdlib/README | 8 +- src/parsetab/tabs.dbm.bak | 8 +- src/parsetab/tabs.dbm.dir | 8 +- src/zxbpp/zxbpp.py | 4 +- tests/functional/Makefile | 82 +- tests/functional/arch/zx48k/border00_IC.ic | 8 +- tests/functional/arch/zx48k/func_call_IC.ic | 34 +- tests/functional/zxbpp/builtin.out | 56 +- tests/functional/zxbpp/emook0.out | 18 +- tests/functional/zxbpp/iflogic.out | 36 +- tests/functional/zxbpp/init_dot.out | 22 +- tests/functional/zxbpp/line_asm.out | 8 +- tests/functional/zxbpp/once.out | 16 +- tests/functional/zxbpp/once_base.out | 24 +- tests/functional/zxbpp/other_arch.out | 510 ++-- tests/functional/zxbpp/prepro00.out | 256 +- tests/functional/zxbpp/prepro01.out | 258 +- tests/functional/zxbpp/prepro02.out | 16 +- tests/functional/zxbpp/prepro03.out | 18 +- tests/functional/zxbpp/prepro04.out | 12 +- tests/functional/zxbpp/prepro05.out | 14 +- tests/functional/zxbpp/prepro06.out | 12 +- tests/functional/zxbpp/prepro09.out | 8 +- tests/functional/zxbpp/prepro10.out | 12 +- tests/functional/zxbpp/prepro11.out | 16 +- tests/functional/zxbpp/prepro12.out | 16 +- tests/functional/zxbpp/prepro13.out | 16 +- tests/functional/zxbpp/prepro14.out | 18 +- tests/functional/zxbpp/prepro15.out | 24 +- tests/functional/zxbpp/prepro16.out | 22 +- tests/functional/zxbpp/prepro17.out | 16 +- tests/functional/zxbpp/prepro18.out | 14 +- tests/functional/zxbpp/prepro19.out | 14 +- tests/functional/zxbpp/prepro20.out | 14 +- tests/functional/zxbpp/prepro21.out | 16 +- tests/functional/zxbpp/prepro23.out | 18 +- tests/functional/zxbpp/prepro24.out | 22 +- tests/functional/zxbpp/prepro25.out | 14 +- tests/functional/zxbpp/prepro26.out | 14 +- tests/functional/zxbpp/prepro27.out | 28 +- tests/functional/zxbpp/prepro29.out | 8 +- tests/functional/zxbpp/prepro30.out | 256 +- tests/functional/zxbpp/prepro31.out | 8 +- tests/functional/zxbpp/prepro32.out | 28 +- tests/functional/zxbpp/prepro33.out | 70 +- tests/functional/zxbpp/prepro34.out | 10 +- tests/functional/zxbpp/prepro36.out | 8 +- tests/functional/zxbpp/prepro37.out | 12 +- tests/functional/zxbpp/prepro38.out | 14 +- tests/functional/zxbpp/prepro39.out | 18 +- tests/functional/zxbpp/prepro40.out | 8 +- tests/functional/zxbpp/prepro41.out | 16 +- tests/functional/zxbpp/prepro42.out | 8 +- tests/functional/zxbpp/prepro43.out | 12 +- tests/functional/zxbpp/prepro44.out | 8 +- tests/functional/zxbpp/prepro45.out | 8 +- tests/functional/zxbpp/prepro46.out | 12 +- tests/functional/zxbpp/prepro47.out | 10 +- tests/functional/zxbpp/prepro48.out | 10 +- tests/functional/zxbpp/prepro49.out | 10 +- tests/functional/zxbpp/prepro50.out | 16 +- tests/functional/zxbpp/prepro51.out | 8 +- tests/functional/zxbpp/prepro52.out | 10 +- tests/functional/zxbpp/prepro53.out | 10 +- tests/functional/zxbpp/prepro54.out | 10 +- tests/functional/zxbpp/prepro55.out | 14 +- tests/functional/zxbpp/prepro56.out | 10 +- tests/functional/zxbpp/prepro57.out | 10 +- tests/functional/zxbpp/prepro58.out | 12 +- tests/functional/zxbpp/prepro59.out | 10 +- tests/functional/zxbpp/prepro60.out | 12 +- tests/functional/zxbpp/prepro61.out | 12 +- tests/functional/zxbpp/prepro62.out | 14 +- tests/functional/zxbpp/prepro63.out | 12 +- tests/functional/zxbpp/prepro64.out | 100 +- tests/functional/zxbpp/prepro70.out | 26 +- tests/functional/zxbpp/prepro71.out | 486 ++-- tests/functional/zxbpp/prepro72.out | 26 +- tests/functional/zxbpp/prepro73.out | 134 +- tests/functional/zxbpp/prepro74.out | 120 +- tests/functional/zxbpp/prepro75.out | 18 +- tests/functional/zxbpp/prepro77.out | 4 +- tests/functional/zxbpp/prepro80.out | 40 +- tests/functional/zxbpp/prepro81.out | 8 +- tests/functional/zxbpp/prepro82.out | 14 +- tests/functional/zxbpp/prepro85.out | 264 +- tests/functional/zxbpp/prepro90.out | 14 +- tests/functional/zxbpp/prepro91.out | 10 +- tests/functional/zxbpp/prepro92.out | 14 +- tests/functional/zxbpp/prepro93.out | 14 +- tests/functional/zxbpp/prepro94.out | 12 +- tests/functional/zxbpp/prepro95.out | 10 +- tests/functional/zxbpp/spectrum.out | 10 +- tests/functional/zxbpp/stringizing0.out | 12 +- tests/functional/zxbpp/stringizing1.out | 16 +- tests/functional/zxbpp/stringizing2.out | 12 +- tests/functional/zxbpp/token-paste0.out | 18 +- tests/functional/zxbpp/token-paste1.out | 16 +- tests/runtime/Makefile | 14 +- tests/runtime/run | 18 +- tests/runtime/test_all | 8 +- tests/runtime/test_case | 34 +- tests/runtime/update_test.sh | 14 +- tools/profile.sh | 8 +- 173 files changed, 5192 insertions(+), 5194 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 9da15245e..8ad31de00 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,18 +1,18 @@ -[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} +[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 2e337207c..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://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 +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 d68e4986c..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://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 +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/.gitignore b/.gitignore index 1ca58c0ee..3282193d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,26 +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.* - -# zx81sd test build artifacts (compila.bat + split_sd81.py output) -/compila.bat -/TESTSD81*.P -/TESTSD81*.BIN +# 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 d69ca1233..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.15.12 - 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/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/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/poetry.lock b/poetry.lock index ca40be381..455d95296 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,1102 +1,1102 @@ -# 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 = "sys_platform == \"win32\" or platform_system == \"Windows\"" -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 = "78.1.1" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -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) ; sys_platform != \"cygwin\"", "ruff (>=0.8.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)", "platformdirs (>=4.2.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 (>=2.2)"] -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.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" -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 = "aa734f52ed314d0af56c786d5eae67b0964d6d199359f642a10418b5cafd1866" +# 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 = "sys_platform == \"win32\" or platform_system == \"Windows\"" +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 = "78.1.1" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +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) ; sys_platform != \"cygwin\"", "ruff (>=0.8.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)", "platformdirs (>=4.2.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 (>=2.2)"] +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.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" +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 = "aa734f52ed314d0af56c786d5eae67b0964d6d199359f642a10418b5cafd1866" diff --git a/pyproject.toml b/pyproject.toml index 387408eb5..a52491fd2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,187 +1,187 @@ -[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 = ">=70.1.1,<79.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/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$', - 'scratch/*', - '.venv/*', - 'venv/*', -] - -[[tool.mypy.overrides]] -module = [ - "src.ply.*", -] -follow_imports = "skip" - -[tool.ruff] -line-length = 120 -target-version = "py314" -exclude = [ - ".venv/", - "venv/", - "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", -] +[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 = ">=70.1.1,<79.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/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$', + 'scratch/*', + '.venv/*', + 'venv/*', +] + +[[tool.mypy.overrides]] +module = [ + "src.ply.*", +] +follow_imports = "skip" + +[tool.ruff] +line-length = 120 +target-version = "py314" +exclude = [ + ".venv/", + "venv/", + "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", +] 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/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/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/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/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/parsetab/tabs.dbm.bak b/src/parsetab/tabs.dbm.bak index 881659c7d..286dd8adf 100644 --- a/src/parsetab/tabs.dbm.bak +++ b/src/parsetab/tabs.dbm.bak @@ -1,4 +1,4 @@ -'zxbpp', (0, 71563) -'asmparse', (71680, 234798) -'zxnext_asmparse', (306688, 259879) -'zxbparser', (566784, 641214) +'zxbpp', (0, 71563) +'asmparse', (71680, 234798) +'zxnext_asmparse', (306688, 259879) +'zxbparser', (566784, 641214) diff --git a/src/parsetab/tabs.dbm.dir b/src/parsetab/tabs.dbm.dir index 881659c7d..286dd8adf 100644 --- a/src/parsetab/tabs.dbm.dir +++ b/src/parsetab/tabs.dbm.dir @@ -1,4 +1,4 @@ -'zxbpp', (0, 71563) -'asmparse', (71680, 234798) -'zxnext_asmparse', (306688, 259879) -'zxbparser', (566784, 641214) +'zxbpp', (0, 71563) +'asmparse', (71680, 234798) +'zxnext_asmparse', (306688, 259879) +'zxbparser', (566784, 641214) diff --git a/src/zxbpp/zxbpp.py b/src/zxbpp/zxbpp.py index 2a81d8024..9339e4d8c 100755 --- a/src/zxbpp/zxbpp.py +++ b/src/zxbpp/zxbpp.py @@ -171,9 +171,7 @@ def set_include_path(): # 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")] - ) + INCLUDE_MAP["zx81sd"].extend([os.path.join(zx48k_pwd, "stdlib"), os.path.join(zx48k_pwd, "runtime")]) INCLUDEPATH = INCLUDE_MAP.get(config.OPTIONS.architecture, []) 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/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/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/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.out b/tests/functional/zxbpp/other_arch.out index 161311b03..5b6695f33 100644 --- a/tests/functional/zxbpp/other_arch.out +++ b/tests/functional/zxbpp/other_arch.out @@ -1,255 +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" +#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 47bdb70b4..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 b21476152..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.out b/tests/functional/zxbpp/prepro85.out index 2847d5075..8207f63be 100644 --- a/tests/functional/zxbpp/prepro85.out +++ b/tests/functional/zxbpp/prepro85.out @@ -1,132 +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" +#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/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/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.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/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 "$@" + From 72611fe1977e92c176382f59cad2914f82319077 Mon Sep 17 00:00:00 2001 From: "Oleg S. Kostenko" Date: Tue, 7 Jul 2026 21:06:22 +0300 Subject: [PATCH 160/169] Play library: implement repetition and halt commands --- examples/play/chaos.bas | 2 +- examples/play/evolution.bas | 8 +-- src/lib/arch/zx48k/stdlib/play.bas | 101 +++++++++++++++++++++++++---- 3 files changed, 93 insertions(+), 18 deletions(-) diff --git a/examples/play/chaos.bas b/examples/play/chaos.bas index a9da8bf33..7df5c978a 100644 --- a/examples/play/chaos.bas +++ b/examples/play/chaos.bas @@ -8,7 +8,7 @@ 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 V8dV7dV6dV5d V8cV7cV6cV5c V8cV7cV6cV5c V8dV7dV6dV5d V8dV7dV6dV5d" +let z$ = "O5 V8 5eV7eV6eV5e V8cV7cV6cV5c (V8dV7dV6dV5d) (V8cV7cV6cV5c) (V8dV7dV6dV5d)" let a$ = x$ + y$ let b$ = z$ diff --git a/examples/play/evolution.bas b/examples/play/evolution.bas index 5affa4498..cb63c9198 100644 --- a/examples/play/evolution.bas +++ b/examples/play/evolution.bas @@ -5,16 +5,14 @@ rem (C) 2026 by Ollibony cls -let z$ = "T160 O3 1e&&Ee&e&&&e&&&e& 1d&&Dd&d&&&d&&&d& 1c&&Cc&c&&&c&&&c& O2 1a&&Aa&a&&&a&&&a& " -let x$ = "O3 1e&&Ee&e&&&e&&&e& 1g&&Gg&g&&&d&4d1& 1c&&Cc&c&&&c&&&c& O2 1a&&Aa&a&&&a&a&a& O5 3g" +let z$ = "T160 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& O5 3g" -let y$ = "O5 1gab&&&ab&&e&&&d& 1gab&&&ab&&e&&&d& 1abC&&&bC&&e&&&d& 1ega&5&&& " -let w$ = "O5 1gab&&&ab&&e&&&d& 1gab&&&ab&&e&&&d& 1abC&&&bC&&e&&&d& 1ega&5&&& 3e" +let b$ = "O5 ((1gab&&&ab&&e&&&d&) 1abC&&&bC&&e&&&d& 1ega&5&&&) 3e" let c$ = "O5 9&&&&& 1bCD&&&CD&&b&&&g& 1CDE&&&DE&&C&&&g& 1Cba&5&&& O4 3b" let a$ = z$ + x$ -let b$ = y$ + w$ print ink 1; "Channel A" print a$: print diff --git a/src/lib/arch/zx48k/stdlib/play.bas b/src/lib/arch/zx48k/stdlib/play.bas index f37d47ca6..2493b03c8 100644 --- a/src/lib/arch/zx48k/stdlib/play.bas +++ b/src/lib/arch/zx48k/stdlib/play.bas @@ -16,8 +16,12 @@ ' --------------------------------------------------------------------------------------------------------------------- ' 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: @@ -31,30 +35,42 @@ ' - 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 ' ' 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. +' +' - 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. ' --------------------------------------------------------------------------------------------------------------------- @@ -143,11 +159,15 @@ dim _Play_ContextPtr as uinteger ' Main sub. ' -' Implementation note: +' 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 @@ -178,11 +198,15 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") 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 + 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 = 15 + const ChannelContextSize as ubyte = 26 ' Channel context data is stored here. dim ChannelContextBuffer(0 to ChannelContextSize * ChannelCount - 1) as ubyte @@ -210,6 +234,11 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") 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. + 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 @@ -323,6 +352,7 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") dim channel as ubyte dim strLen as uinteger + dim ptr as uinteger _PLAY_CTX_FIRST_CHANNEL() @@ -330,16 +360,18 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") ' 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))) + ptr = peek(uinteger, _PLAY_CTX_GET(uinteger, _CharPtr)) ' read the string length - strLen = peek(uinteger, _PLAY_CTX_GET(uinteger, _CharPtr)) + strLen = peek(uinteger, ptr) ' adjust the pointer so it points to the first char - _PLAY_CTX_ADD(uinteger, _CharPtr, 2) + ptr = ptr + 2 - ' calculate and store the pointer to the string end - _PLAY_CTX_SET(uinteger, _StringEndPtr, _PLAY_CTX_GET(uinteger, _CharPtr) + strLen) + ' 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) @@ -349,6 +381,7 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") _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 @@ -361,6 +394,10 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") 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 #ifdef _PLAY_BENCHMARK_MODE dim SysFrames as uinteger at $5c78 @@ -439,6 +476,35 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") _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 @@ -454,6 +520,10 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") Tempo = LastNumber UpdateMicroticksPerTick end if + + else if LastChar = code("H") then + halt = 1 + exit for ' TODO: process other commands here end if @@ -477,6 +547,13 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") _PLAY_CTX_NEXT_CHANNEL() next channel + if halt then + for channel = 0 to MaxChannel + SetChipVolume channel, 0 + next channel + exit do + end if + Wait MicroticksPerTick _ - TickGeneralOverheadInMicroticks _ - TickChannelCommandsOverheadInMicroticks * processedChannels From 61450a36bf07663c8083ba222329287cb33fc4d7 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 10 Jul 2026 22:33:35 +0200 Subject: [PATCH 161/169] refact: port to lark --- pyproject.toml | 17 +- src/api/errmsg.py | 5 + src/api/global_.py | 5 + src/api/lex.py | 245 + src/api/utils.py | 20 +- src/parsetab/__init__.py | 6 - src/parsetab/tabs.dbm.bak | 4 - src/parsetab/tabs.dbm.dat | Bin 1207998 -> 0 bytes src/parsetab/tabs.dbm.dir | 4 - src/ply/__init__.py | 5 - src/ply/lex.py | 901 ---- src/ply/yacc.py | 2482 --------- src/symbols/type_.py | 2 +- src/zxbasm/asmlex.py | 3 +- src/zxbasm/asmparse.py | 1244 ++--- src/zxbasm/asmparse_standalone.py | 3574 +++++++++++++ src/zxbasm/asmparse_zxnext_standalone.py | 3574 +++++++++++++ src/zxbc/py.typed | 0 src/zxbc/zxblex.py | 3 +- src/zxbc/zxbparser.py | 5613 ++++++++++----------- src/zxbc/zxbparser_standalone.py | 3574 +++++++++++++ src/zxbpp/base_pplex.py | 3 +- src/zxbpp/zxbasmpplex.py | 2 +- src/zxbpp/zxbpp.py | 971 ++-- src/zxbpp/zxbpp_standalone.py | 3574 +++++++++++++ src/zxbpp/zxbpplex.py | 3 +- tests/functional/cmdline/test_cmdline.txt | 9 + tests/functional/cmdline/test_errmsg.txt | 2 +- tests/zxbc/__init__.py | 6 - tests/zxbc/test_build_parsetab.py | 26 - 30 files changed, 17985 insertions(+), 7892 deletions(-) create mode 100644 src/api/lex.py delete mode 100644 src/parsetab/__init__.py delete mode 100644 src/parsetab/tabs.dbm.bak delete mode 100644 src/parsetab/tabs.dbm.dat delete mode 100644 src/parsetab/tabs.dbm.dir delete mode 100644 src/ply/__init__.py delete mode 100644 src/ply/lex.py delete mode 100644 src/ply/yacc.py create mode 100644 src/zxbasm/asmparse_standalone.py create mode 100644 src/zxbasm/asmparse_zxnext_standalone.py delete mode 100644 src/zxbc/py.typed create mode 100644 src/zxbc/zxbparser_standalone.py create mode 100644 src/zxbpp/zxbpp_standalone.py delete mode 100644 tests/zxbc/__init__.py delete mode 100644 tests/zxbc/test_build_parsetab.py diff --git a/pyproject.toml b/pyproject.toml index a52491fd2..d56126b70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,7 +92,7 @@ shell = """ branch = true omit = [ "tests/*", - "src/ply/*" + "src/**/*_standalone.py", ] [tool.pytest.ini_options] @@ -116,15 +116,18 @@ disable_error_code = [ "var-annotated", ] exclude = [ - 'src/ply/.*\.py$', - 'scratch/*', - '.venv/*', - 'venv/*', + 'scratch/.*', + '.venv/.*', + 'venv/.*', + 'src/.*_standalone\.py$', ] [[tool.mypy.overrides]] module = [ - "src.ply.*", + "src.zxbasm.asmparse_standalone", + "src.zxbasm.asmparse_zxnext_standalone", + "src.zxbc.zxbparser_standalone", + "src.zxbpp.zxbpp_standalone", ] follow_imports = "skip" @@ -134,7 +137,7 @@ target-version = "py314" exclude = [ ".venv/", "venv/", - "src/ply" # PLY, external 3rd party tool + "src/**/*standalone.py", ] [tool.ruff.lint] diff --git a/src/api/errmsg.py b/src/api/errmsg.py index 4a87a0cd2..97ccbca98 100644 --- a/src/api/errmsg.py +++ b/src/api/errmsg.py @@ -43,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 @@ -60,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 diff --git a/src/api/global_.py b/src/api/global_.py index 3bf9bac85..c432a4ed8 100644 --- a/src/api/global_.py +++ b/src/api/global_.py @@ -59,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/utils.py b/src/api/utils.py index ec44f24b1..2cdc07e5b 100644 --- a/src/api/utils.py +++ b/src/api/utils.py @@ -7,14 +7,13 @@ 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", @@ -30,9 +29,6 @@ like reading files or path management""" -SHELVE_PATH = os.path.join(constants.ZXBASIC_ROOT, "parsetab", "tabs.dbm") -SHELVE = shelve.open(SHELVE_PATH, protocol=5, flag="c") - T = TypeVar("T") @@ -184,20 +180,6 @@ def eval_to_num(expr: str) -> int | float | None: 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/parsetab/__init__.py b/src/parsetab/__init__.py deleted file mode 100644 index 906667a8d..000000000 --- a/src/parsetab/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# -------------------------------------------------------------------- -# 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/parsetab/tabs.dbm.bak b/src/parsetab/tabs.dbm.bak deleted file mode 100644 index 286dd8adf..000000000 --- a/src/parsetab/tabs.dbm.bak +++ /dev/null @@ -1,4 +0,0 @@ -'zxbpp', (0, 71563) -'asmparse', (71680, 234798) -'zxnext_asmparse', (306688, 259879) -'zxbparser', (566784, 641214) diff --git a/src/parsetab/tabs.dbm.dat b/src/parsetab/tabs.dbm.dat deleted file mode 100644 index dfbdc2e07ebf34c4b7ed4c8f834d2e48f7908c25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1207998 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

LNQH{>XfsA&B}B#_x06jm2ZzE*=Up;Sa;ge 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 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/type_.py b/src/symbols/type_.py index d463d102a..a443de414 100644 --- a/src/symbols/type_.py +++ b/src/symbols/type_.py @@ -173,7 +173,7 @@ class SymbolTYPEREF(Symbol): """ def __init__(self, type_: SymbolTYPE, lineno: int, filename: str = "", *, implicit: bool = False): - assert isinstance(type_, SymbolTYPE) + 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 diff --git a/src/zxbasm/asmlex.py b/src/zxbasm/asmlex.py index ec738a0a9..d3a782ef9 100755 --- a/src/zxbasm/asmlex.py +++ b/src/zxbasm/asmlex.py @@ -9,10 +9,9 @@ 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 55f26c082..58a7ff267 100755 --- a/src/zxbasm/asmparse.py +++ b/src/zxbasm/asmparse.py @@ -1,13 +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. +# © 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 @@ -15,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 @@ -25,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 @@ -34,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""" @@ -62,933 +56,465 @@ def init(): asm_gl.NAMESPACE = asm_gl.GLOBAL_NAMESPACE -# -------- GRAMMAR RULES for the preprocessor --------- +class AsmToken(Token): + pass -def p_start(p): - """start : program - | program endline - """ +class AsmLarkLexerAdapter(Lexer): + def __init__(self, lexer_conf: Any) -> None: + pass + 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 -def p_program_endline(p): - """endline : END NEWLINE - | endline NEWLINE - """ + tok = lexer.token() + if tok is None: + break + t = AsmToken(tok.type, tok.value, line=tok.lineno, column=lexer.find_column(tok)) + yield t -def p_program_endline2(p): - """endline : END expr NEWLINE - | END pexpr NEWLINE - """ - global AUTORUN_ADDR - AUTORUN_ADDR = p[2].eval() +class AsmTransformer(Transformer): + def start(self, items): + return items[0] -def p_program(p): - """program : line""" + def program(self, items): + return items[0] + def empty_program(self, items): + return None -def p_program_line(p): - """program : program line""" + 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_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 line_asm(self, items): + return None + def preprocessor_line(self, items): + return None -def p_line_asm(p): - """line : asms NEWLINE - | asms CO NEWLINE - """ + 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_asms_empty(p): - """asms :""" - p[0] = MEMORY.org + def asms_asm(self, items): + asm = items[0] + if isinstance(asm, Asm): + MEMORY.add_instruction(asm) + return MEMORY.org + def asms_asms_asm(self, items): + asm = items[2] + if isinstance(asm, Asm): + MEMORY.add_instruction(asm) + return items[0] -def p_asms_asm(p): - """asms : asm""" - p[0] = MEMORY.org - asm = p[1] - if isinstance(asm, Asm): - MEMORY.add_instruction(asm) + 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_asms_asms_asm(p): - """asms : asms CO asm""" - p[0] = p[1] - asm = p[3] - if isinstance(asm, Asm): - MEMORY.add_instruction(asm) + 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_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:])) - - -def p_PROC(p): - """asm : PROC""" - p[0] = None # Start of a PROC scope - MEMORY.enter_proc(p.lineno(1)) - - -def p_ENDP(p): - """asm : ENDP""" - p[0] = None # End of a PROC scope - MEMORY.exit_proc(p.lineno(1)) - + def endp_scope(self, items): + MEMORY.exit_proc(items[0].line) -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)) + def local_labels(self, items): + for label, line in items[1]: + MEMORY.set_label(label, line, local=True) - MEMORY.set_label(label, line, local=True) + 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_idlist(p): - """id_list : ID""" - p[0] = (Container(p[1], p.lineno(1)),) + 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_idlist_id(p): - """id_list : id_list COMMA ID""" - p[0] = p[1] + (Container(p[3], p.lineno(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_DEFB(p): # Define bytes - """asm : DEFB expr_list""" - p[0] = Asm(p.lineno(1), "DEFB", p[2]) + def expr_list_from_num(self, items): + return (items[0],) + def expr_list_plus_expr(self, items): + return items[0] + (items[2],) -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 expr_list_plus_string(self, items): + return items[0] + tuple(Expr.makenode(Container(ord(x), items[2].line)) for x in items[2]) - if len(p[2]) < 2: - num = Expr.makenode(Container(0, p.lineno(1))) # Defaults to 0 - p[2] = p[2] + (num,) + def number_list(self, items): + return (items[0],) - p[0] = Asm(p.lineno(1), "DEFS", p[2]) + 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_DEFW(p): # Define words - """asm : DEFW number_list""" - p[0] = Asm(p.lineno(1), "DEFW", p[2]) - - -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 p_expr_list_from_num(p): - """expr_list : expr - | pexpr - """ - p[0] = (p[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_expr_list_plus_expr(p): - """expr_list : expr_list COMMA expr - | expr_list COMMA pexpr - """ - p[0] = p[1] + (p[3],) - - -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 p_number_list(p): - """number_list : expr - | pexpr - """ - p[0] = (p[1],) - - -def p_number_list_number(p): - """number_list : number_list COMMA expr - | number_list COMMA pexpr - """ - p[0] = p[1] + (p[3],) - - -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 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 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 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 sbcadd(self, items): + return Asm(items[0].line, "%s %s,%s" % (items[0], items[1], items[3])) -def p_reg8(p): - """reg8 : H - | L - | regBCDE - """ - p[0] = p[1] + def arith_a_expr(self, items): + return Asm(items[0].line, "%s A,N" % items[0], items[3]) + def arith_a_reg_i(self, items): + return Asm(items[0].line, "%s A,%s" % (items[0], items[3][0]), items[3][1]) -def p_regBCDE(p): - """regBCDE : B - | C - | D - | E - """ - p[0] = p[1] + def bitwiseop_reg(self, items): + return Asm(items[0][1], "%s %s" % (items[0][0], items[1])) + def bitwiseop_reg_i(self, items): + return Asm(items[0][1], "%s %s" % (items[0][0], items[1][0]), items[1][1]) -def p_reg8i(p): - """reg8i : IXH - | IXL - | IYH - | IYL - """ - p[0] = p[1] + def bitwise_expr(self, items): + return Asm(items[0][1], "%s N" % items[0][0], items[1]) + def bitwise(self, items): + return (items[0], items[0].line) -def p_reg16(p): - """reg16 : BC - | DE - | HL - | reg16i - """ - p[0] = p[1] + def push_pop(self, items): + return Asm(items[0].line, "%s %s" % (items[0], items[1])) + def ld_addr_reg(self, items): + return Asm(items[0].line, "LD (NN),%s" % items[3], items[1]) -def p_reg16i(p): - """reg16i : IX - | IY - """ - p[0] = p[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_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 - | LP pexpr 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_): @@ -1005,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") @@ -1095,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/zxbc/py.typed b/src/zxbc/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/zxbc/zxblex.py b/src/zxbc/zxblex.py index 9d739029f..b92f960b4 100755 --- a/src/zxbc/zxblex.py +++ b/src/zxbc/zxblex.py @@ -11,9 +11,8 @@ 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 src.zxbc.keywords import KEYWORDS as reserved ASM = "" # Set to asm block when commenting diff --git a/src/zxbc/zxbparser.py b/src/zxbc/zxbparser.py index c213eb5a1..a9cd319f7 100755 --- a/src/zxbc/zxbparser.py +++ b/src/zxbc/zxbparser.py @@ -6,14 +6,13 @@ # 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, cast +from typing import Any, NamedTuple, cast import src.api.config import src.api.dataref @@ -50,7 +49,6 @@ 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 @@ -58,6 +56,8 @@ 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[:] @@ -199,7 +204,16 @@ 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) @@ -537,3110 +551,2685 @@ def make_break(lineno: int, p): # ---------------------------------------------------------------------- -# Grammar rules -# ---------------------------------------------------------------------- - - -def p_start(p): - """start : program""" - global ast, data_ast - - 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 - if zxblex.IN_STATE: - p.type = "NEWLINE" - p_error(p) - sys.exit(1) +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 - ast = p[0] = p[1] - __end = make_sentence(p.lexer.lineno, "END", make_number(0, lineno=p.lexer.lineno), 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 +def meta_line(meta): + return getattr(meta, "line", None) or (zxblex.lexer.lineno if hasattr(zxblex, "lexer") else 0) or 0 - 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 +class ZXBasicToken(Token): + def __eq__(self, other): + return str.__eq__(self, other) - __DEBUG__("Checking pending calls", 1) - if not src.api.check.check_pending_calls(): - return - - data_ast = make_sentence(p.lexer.lineno, "BLOCK") + def __hash__(self): + return str.__hash__(self) - # Appends variable declarations at the end. - for var in SYMBOL_TABLE.vars_: - data_ast.append_child(make_var_declaration(var)) - # Appends arrays declarations at the end. - for var in SYMBOL_TABLE.arrays: - data_ast.append_child(make_array_declaration(var)) +class ZXBasicLarkLexerAdapter(Lexer): + def __init__(self, lexer_conf: Any) -> None: + pass + def lex(self, data: Any, parser_state: Any = None) -> Any: + lexer = data + lookahead = [] -def p_program_program_line(p): - """program : program_line""" - p[0] = make_block(p[1], make_break(p.lineno(1), p[1])) - + 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 is_static(expr) and 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.type_, 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 - if len(remaining) != boundlist[0].count: + 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 + + 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)) - - -def p_const_vector(p): - """const_vector : LBRACE const_vector_list RBRACE - | LBRACE const_number_list RBRACE - """ - p[0] = p[2] - - -def p_const_vector_elem_list(p): - """const_number_list : expr""" - if p[1] is None: - return - - if not is_static(p[1]): - if isinstance(p[1], sym.UNARY): - tmp = make_constexpr(p.lineno(1), p[1]) + p0 = None else: - errmsg.syntax_error_not_constant(p.lexer.lineno) - p[0] = None - return - else: - tmp = p[1] + 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 - p[0] = [tmp] + 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_list(p): - """const_number_list : const_number_list COMMA expr""" - if p[1] is None or p[3] 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[3]): - if isinstance(p[3], sym.UNARY): - tmp = make_constexpr(p.lineno(2), p[3]) + 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.lineno(2)) - p[0] = None - return - else: - tmp = p[3] - - if p[1] is not None: - p[1].append(tmp) - p[0] = p[1] - - -def p_const_vector_list(p): - """const_vector_list : const_vector""" - p[0] = [p[1]] - - -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 - - 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 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], - ) + 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 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 -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)) + 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: - 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]) + p0 = make_builtin( + get_lineno(items[0]), + "USR", + make_typecast(Type.uinteger, items[1], get_lineno(items[0])), + type_=Type.uinteger, + ) + return p0 + def expr_rnd(self, meta, items): + p0 = None + p0 = make_builtin(get_lineno(items[0]), "RND", None, type_=Type.float_) + return p0 -def p_assignment(p): - """statement : lexpr expr""" - global LET_ASSIGNMENT + 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 - LET_ASSIGNMENT = False # Mark we're no longer using LET - p[0] = None - q = p[1:] - i = 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 - if q[1] is None: - return + 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 - if q[1].token == "VAR" and q[1].class_ == CLASS.unknown: - q[1] = SYMBOL_TABLE.access_var(q[1].name, p.lineno(i)) + 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 - 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() +def _handle_parse_error_inner(e): + from src.api import errmsg + from src.api.errmsg import error - if variable.class_ not in (CLASS.var, CLASS.array): - errmsg.syntax_error_cannot_assign_not_a_var(p.lineno(i), variable.name) - return + from .zxbparser_standalone import UnexpectedEOF, UnexpectedToken - if variable.class_ == CLASS.var and q1class_ == CLASS.array: - error(p.lineno(i), "Cannot assign an array to an scalar variable") - return + is_eof = isinstance(e, UnexpectedEOF) or (isinstance(e, UnexpectedToken) and e.token.type == "$END") - expr = make_typecast(variable.type_, q[1], p.lineno(i)) - p[0] = make_sentence(p.lineno(1), "LET", variable, expr) + 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: + 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 + # 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) -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)) - # variable is an array. If it has 0 bounds means they are undefined (param byref) - if len(variable.ref.bounds) and 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: - errmsg.warning_uninitalized_string_var(p.lineno(1), entry.name) - entry.to_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].type_, p[6].type_), p[7].type_) - 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)}" - 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, p.lineno(1), 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(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_.name) - - 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_.final == 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_.final != 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_ = _TYPEREF(gl.DEFAULT_TYPE) - errmsg.warning_implicit_type(p.lineno(1), p[1], entry.type_.name) - - 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_.name) - - 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""" - if p[3] is None or p[5] is None: - p[0] = None - return - - p[0] = make_builtin( - p.lineno(1), - "PEEK", - make_typecast(Type.uinteger, p[5], p.lineno(4)), - type_=cast(sym.TYPEREF, p[3]).type_, - ) - - -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], val, 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], asc, 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], sgn, 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/base_pplex.py b/src/zxbpp/base_pplex.py index 40f2ab5f4..cf5c79959 100644 --- a/src/zxbpp/base_pplex.py +++ b/src/zxbpp/base_pplex.py @@ -11,8 +11,7 @@ from dataclasses import dataclass 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 diff --git a/src/zxbpp/zxbasmpplex.py b/src/zxbpp/zxbasmpplex.py index 71e927820..2aceac0bd 100755 --- a/src/zxbpp/zxbasmpplex.py +++ b/src/zxbpp/zxbasmpplex.py @@ -9,7 +9,7 @@ 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 diff --git a/src/zxbpp/zxbpp.py b/src/zxbpp/zxbpp.py index 9339e4d8c..7d9010058 100755 --- a/src/zxbpp/zxbpp.py +++ b/src/zxbpp/zxbpp.py @@ -16,7 +16,6 @@ from typing import Any, Final, NamedTuple 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 @@ -26,6 +25,8 @@ 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(StrEnum): @@ -87,15 +88,6 @@ class 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: @@ -303,622 +295,471 @@ def to_int(expr: str | int) -> int: return expr -# -------- GRAMMAR RULES for the preprocessor --------- -def p_start(p): - """start : program""" - global OUTPUT +class PPToken(Token): + pass - OUTPUT += "".join(p[1]) +class LarkLexerAdapter(Lexer): + def __init__(self, lexer_conf: Any) -> None: + pass -def p_program(p): - """program : include_file - | line - | init - | undef - | ifdef - | require - | pragma - | errormsg - | warningmsg - """ - p[0] = p[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 = "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_program_tokenstring(p): - """program : defs NEWLINE""" - tmp = expand_macros(p[1], p.lineno(2)) - if tmp is None: - p[0] = [] - return + 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 - p[0] = [tmp] +class ZxbppTransformer(Transformer): + def start(self, items): + global OUTPUT + OUTPUT += "".join(items[0]) + return items[0] -def p_program_tokenstring_2(p): - """program : define NEWLINE""" - p[0] = p[1] + [p[2]] + 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_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 program_tokenstring_2(self, items): + return items[0] + [items[1]] + def program_char(self, items): + return items[0] + items[1] -def p_program_newline(p): - """program : program defs NEWLINE""" - tmp = expand_macros(p[2], p.lineno(3)) - if tmp is None: - p[0] = [] - return + 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 - p[0] = p[1] - p[0].append(tmp) + def program_newline_2(self, items): + return items[0] + [f'#line {items[2].line + 1} "{items[2].fname}"\n'] + def token(self, items): + return items[0] -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 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 include_file_empty(self, items): + return [items[1]] -def p_token(p): - """token : STRING - | TOKEN - | CONTINUE - | SEPARATOR - | NUMBER - """ - p[0] = p[1] + def include_once_empty(self, items): + return [items[1]] + 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 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 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_include_file_empty(p): - """include_file : include NEWLINE _ENDFILE_""" # This happens when an IFDEF is FALSE - p[0] = [p[2]] + 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_include_once_empty(p): - """include_file : include_once NEWLINE _ENDFILE_""" - p[0] = [p[2]] # Include once already included. Nothing done. + 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_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 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_include_fname(p): - """include : INCLUDE include_modifier FILENAME""" - modifier = p[2] - if modifier is None: - p[0] = [] - return + def expr_macrocall(self, items): + return str(items[0]()).strip() - filename = p[3] - if ENABLED: - arch = modifier.get("arch", "") - p[0] = include_file(filename, p.lineno(3), local_first=False, arch=arch) - else: - p[0] = [] - p.lexer.next_token = "_ENDFILE_" - - -def p_include_macro(p): - """include : INCLUDE include_modifier expr""" - modifier = p[2] - if modifier is None: - p[0] = [] - return - - expr = p[3] - global_fist = RE_GLOBAL_FIRST_FILENAME.match(expr) - local_first = RE_LOCAL_FIRST_FILENAME.match(expr) - if global_fist is None and local_first is None: - error(p.lineno(1), f"invalid filename {expr}") - p[0] = [] - return - - if ENABLED: - arch = modifier.get("arch", "") - p[0] = include_file(expr[1:-1], p.lineno(3), local_first=local_first is not None, arch=arch) - else: - p[0] = [] - p.lexer.next_token = "_ENDFILE_" + def expr_val(self, items): + return items[0] + def expr_str(self, items): + return items[0] -def p_include_once(p): - """include_once : INCLUDE ONCE include_modifier STRING""" - modifier = p[3] - if modifier is None: - p[0] = [] - return + def expr_par(self, items): + return items[1] - string = p[4] - if ENABLED: - arch = modifier.get("arch", "") - p[0] = include_once(string[1:-1], p.lineno(4), local_first=True, arch=arch) - else: - p[0] = [] + def expreq(self, items): + return "1" if items[0] == items[2] else "0" - if not p[0]: - p.lexer.next_token = "_ENDFILE_" + def exprne(self, items): + return "1" if items[0] != items[2] else "0" + def exprlt(self, items): + return "1" if to_int(items[0]) < to_int(items[2]) else "0" -def p_include_once_fname(p): - """include_once : INCLUDE ONCE include_modifier FILENAME""" - p[0] = [] - modifier = p[3] - if modifier is None: - return + def exprle(self, items): + return "1" if to_int(items[0]) <= to_int(items[2]) else "0" - filename = p[4] - if ENABLED: - arch = modifier.get("arch", "") - p[0] = include_once(filename, p.lineno(4), local_first=False, arch=arch) - else: - p[0] = [] + def exprgt(self, items): + return "1" if to_int(items[0]) > to_int(items[2]) else "0" - if not p[0]: - p.lexer.next_token = "_ENDFILE_" + def exprge(self, items): + return "1" if to_int(items[0]) >= to_int(items[2]) else "0" + def exprand(self, items): + return "1" if to_bool(items[0]) and to_bool(items[2]) else "0" -def p_include_modifier(p): - """include_modifier : - | LB ID CO ID RB - """ - if len(p) == 1: - p[0] = {} - return + def expror(self, items): + return "1" if to_bool(items[0]) or to_bool(items[2]) else "0" - modifier = p[2] - value = p[4] + def defs_list_eps(self, items): + return [] - if modifier == "arch": - p[0] = {"arch": value} - else: - p[0] = None - error(p.lineno(1), f"unknown modifier {modifier}") + def defs_list(self, items): + return items[0] + [items[1]] - return + def def_item_val(self, items): + return items[0] + def def_macrocall(self, items): + return items[0] -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 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_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 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_require_file(p): - """require : REQUIRE STRING NEWLINE""" - p[0] = ["#%s %s\n" % (p[1], utils.sanitize_filename(p[2]))] + 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_init(p): - """init : INIT ID NEWLINE""" - p[0] = ['#%s "%s"\n' % (p[1], p[2])] + def arglist(self, items): + items[0].addNewArg(items[2]) + return items[0] + def arg_eps(self, items): + return Arg() -def p_init_str(p): - """init : INIT STRING NEWLINE""" - p[0] = ["#%s %s\n" % (p[1], p[2])] + def arg_val(self, items): + return items[0] + def argstring_token_single(self, items): + return Arg(items[0]) -def p_undef(p): - """undef : UNDEF ID""" - if ENABLED: - ID_TABLE.undef(p[2]) + def argstring_macrocall_single(self, items): + return Arg(items[0]) - p[0] = [] + def argstring_argslist(self, items): + return Arg(items[1]) + def argstring_token(self, items): + items[0].addToken(items[1]) + return items[0] -def p_errormsg(p): - """errormsg : ERROR TEXT""" - if ENABLED: - error(p.lineno(1), p[2]) - p[0] = [] + def argstring_macrocall(self, items): + items[0].addToken(items[1]) + return items[0] + def argstring_argstring(self, items): + items[0].addToken(items[2]) + return items[0] -def p_warningmsg(p): - """warningmsg : WARNING TEXT""" - if ENABLED: - warning(p.lineno(1), p[2]) - p[0] = [] +lark_parser = Lark_StandAlone(lexer=LarkLexerAdapter, transformer=ZxbppTransformer()) -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") +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: - 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])] - - -def p_pragma_id(p): - """pragma : PRAGMA ID""" - p[0] = ["#%s %s" % (p[1], p[2])] - - -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 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] - - -def p_exprand(p): - """expr : expr AND expr""" - p[0] = "1" if to_bool(p[1]) and to_bool(p[3]) else "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 p_exprne(p): - """expr : expr NE expr""" - p[0] = "1" if p[1] != p[3] else "0" - - -def p_expreq(p): - """expr : expr EQ expr""" - p[0] = "1" if p[1] == p[3] else "0" - - -def p_exprlt(p): - """expr : expr LT expr""" - p[0] = "1" if to_int(p[1]) < to_int(p[3]) else "0" - - -def p_exprle(p): - """expr : expr LE expr""" - p[0] = "1" if to_int(p[1]) <= to_int(p[3]) else "0" - - -def p_exprgt(p): - """expr : expr GT expr""" - p[0] = "1" if to_int(p[1]) > to_int(p[3]) else "0" - - -def p_exprge(p): - """expr : expr GE expr""" - p[0] = "1" if to_int(p[1]) >= to_int(p[3]) else "0" - - -def p_expr_par(p): - """expr : LLP expr RRP""" - p[0] = p[2] - - -def p_defs_list_eps(p): - """defs :""" - p[0] = [] - - -def p_defs_list(p): - """defs : defs def""" - p[0] = p[1] - p[0].append(p[2]) - - -def p_def(p): - """def : token - | COMMA - | RRP - | LLP - """ - p[0] = p[1] - - -def p_def_macrocall(p): - """def : macrocall %prec DUMMY""" - p[0] = p[1] - - -def p_macrocall(p): - """macrocall : ID""" - p[0] = MacroCall(p.lexer.current_file, p.lineno(1), ID_TABLE, p[1], None) - - -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 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 p_macrocall_stringizing(p): - """macrocall : STRINGIZING macrocall""" - p[0] = Stringizing(p.lexer.current_file, p[2].lineno, ID_TABLE, p[2]) - - -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 p_arglist(p): - """arglist : arglist COMMA arg""" - p[1].addNewArg(p[3]) - p[0] = p[1] - - -def p_arglist_arg(p): - """arglist : arg""" - p[0] = ArgList(ID_TABLE) - p[0].addNewArg(p[1]) - - -def p_arg_eps(p): - """arg :""" - p[0] = Arg() - - -def p_arg_argstring(p): - """arg : argstring""" - p[0] = p[1] - - -def p_argstring(p): - """argstring : token - | macrocall %prec DUMMY - """ - p[0] = Arg(p[1]) - - -def p_argstring_argslist(p): - """argstring : LLP arglist RRP""" - p[0] = Arg(p[2]) - - -def p_argstring_token(p): - """argstring : argstring token - | argstring macrocall %prec DUMMY - """ - p[0] = p[1] - p[0].addToken(p[2]) - - -def p_argstring_argstring(p): - """argstring : argstring LLP arglist RRP""" - p[0] = p[1] - p[0].addToken(p[3]) - - -# --- YYERROR - - -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], - ) + 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"): @@ -932,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 @@ -960,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]) @@ -970,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 = {} 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 d8ab506c7..a3e34d5c6 100755 --- a/src/zxbpp/zxbpplex.py +++ b/src/zxbpp/zxbpplex.py @@ -10,8 +10,7 @@ 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 diff --git a/tests/functional/cmdline/test_cmdline.txt b/tests/functional/cmdline/test_cmdline.txt index 3fcf536b1..14241bd61 100644 --- a/tests/functional/cmdline/test_cmdline.txt +++ b/tests/functional/cmdline/test_cmdline.txt @@ -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..e3ac87e8c 100644 --- a/tests/functional/cmdline/test_errmsg.txt +++ b/tests/functional/cmdline/test_errmsg.txt @@ -53,7 +53,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/zxbc/__init__.py b/tests/zxbc/__init__.py deleted file mode 100644 index 906667a8d..000000000 --- a/tests/zxbc/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# -------------------------------------------------------------------- -# 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/zxbc/test_build_parsetab.py b/tests/zxbc/test_build_parsetab.py deleted file mode 100644 index c6745d7ec..000000000 --- a/tests/zxbc/test_build_parsetab.py +++ /dev/null @@ -1,26 +0,0 @@ -# -------------------------------------------------------------------- -# 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 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" From be7ac84417a872506e4e53eef9ce5adc22838d5c Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 11 Jul 2026 13:10:18 +0200 Subject: [PATCH 162/169] fix: Fixes wrong sigil allowed --- src/api/symboltable/symboltable.py | 61 ++++++++++++++++++------------ 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/src/api/symboltable/symboltable.py b/src/api/symboltable/symboltable.py index da01eec75..507b3731e 100644 --- a/src/api/symboltable/symboltable.py +++ b/src/api/symboltable/symboltable.py @@ -68,25 +68,38 @@ 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 @@ -162,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 @@ -190,7 +203,7 @@ def check_is_undeclared( 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 @@ -217,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 @@ -355,7 +368,7 @@ 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) @@ -426,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: @@ -472,7 +485,7 @@ def access_call(self, id_: str, lineno: int, scope=None, type_=None) -> symbols. 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 @@ -497,7 +510,7 @@ def declare_variable( 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, @@ -510,7 +523,7 @@ def declare_variable( 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_ref=type_)) assert entry is not None @@ -585,7 +598,7 @@ def declare_type(self, name: str, lineno: int, type_: symbols.TYPE) -> symbols.T 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, @@ -607,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)) @@ -620,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: @@ -704,7 +717,7 @@ def declare_array( 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_ref=type_)) assert entry is not None @@ -759,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" From fa9572fb8c5e5530c8d6b0f56acb42a864b5698e Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 11 Jul 2026 14:52:08 +0200 Subject: [PATCH 163/169] test: update tests --- tests/functional/cmdline/test_errmsg.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/cmdline/test_errmsg.txt b/tests/functional/cmdline/test_errmsg.txt index e3ac87e8c..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') From 303596ba7d71a06d420498344627f99df720689f Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 11 Jul 2026 18:23:49 +0200 Subject: [PATCH 164/169] docs: try Pygmens 2.20.x --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 685fa14f4..97c7eb6e6 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -19,7 +19,7 @@ packaging==24.2 paginate==0.5.7 pathspec==0.12.1 platformdirs==4.3.6 -Pygments==2.18.0 +Pygments==2.20.* pymdown-extensions==10.21.3 python-dateutil==2.9.0.post0 PyYAML==6.0.2 From d41e8efa6b201ab9c01d68cf1b13255820d7b408 Mon Sep 17 00:00:00 2001 From: "Oleg S. Kostenko" Date: Sun, 12 Jul 2026 16:51:14 +0300 Subject: [PATCH 165/169] Play library: update comments --- src/lib/arch/zx48k/stdlib/play.bas | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/lib/arch/zx48k/stdlib/play.bas b/src/lib/arch/zx48k/stdlib/play.bas index 2493b03c8..330e56b6c 100644 --- a/src/lib/arch/zx48k/stdlib/play.bas +++ b/src/lib/arch/zx48k/stdlib/play.bas @@ -73,6 +73,12 @@ ' ' - 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 = "") @@ -332,7 +338,7 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") 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. + ' 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 @@ -560,7 +566,7 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") loop until finishedChannels = ChannelCount #ifdef _PLAY_BENCHMARK_MODE - print "Play duration: "; SysFrames - startTime; " ticks." + print "Play duration: "; SysFrames - startTime; " frames." #endif asm From 30338cceb41fc399bc90247067859365819a0470 Mon Sep 17 00:00:00 2001 From: "Oleg S. Kostenko" Date: Sun, 12 Jul 2026 16:53:56 +0300 Subject: [PATCH 166/169] Play library: implement volume effects commands --- src/lib/arch/zx48k/stdlib/play.bas | 69 ++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/src/lib/arch/zx48k/stdlib/play.bas b/src/lib/arch/zx48k/stdlib/play.bas index 330e56b6c..8ae976d24 100644 --- a/src/lib/arch/zx48k/stdlib/play.bas +++ b/src/lib/arch/zx48k/stdlib/play.bas @@ -37,9 +37,11 @@ ' - 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 ' ' The following commands are not implemented yet: -' - W, U, X - set volume effects ' - !! - comments ' - M - channel mixer control ' - Y, Z - MIDI control (also you can't now pass more than 3 parameters to Play). @@ -138,6 +140,18 @@ dim _Play_NoteIndexes(code("A") to code("G")) as ubyte = { _ /'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`). @@ -191,6 +205,10 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") 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. @@ -240,6 +258,7 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") 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. @@ -254,6 +273,10 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") ' 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. @@ -327,11 +350,23 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") _PLAY_WRITE_TO_REGISTER(channel * 2 + 1, divider >> 8) end sub - ' Set volume on the sound chip for a channel. + ' 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. sub SetChipMixer(value as ubyte) _PLAY_WRITE_TO_REGISTER(7, value) @@ -392,6 +427,7 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") _PLAY_CTX_NEXT_CHANNEL() next channel + EnvelopeShape = DefaultEnvelopeShape Tempo = DefaultTempo UpdateMicroticksPerTick SetChipMixer DefaultMixer @@ -404,6 +440,8 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") 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 @@ -413,6 +451,7 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") do finishedChannels = 0 processedChannels = 0 + mustInitEnvelope = 0 _PLAY_CTX_FIRST_CHANNEL() @@ -449,7 +488,16 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") _PLAY_CTX_SET(byte, _SemitoneAdjustment, 0) SetChipPitchDivider channel, _Play_NoteDividers(dividerIndex) - SetChipVolume channel, _PLAY_CTX_GET(ubyte, _Volume) + + 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 @@ -519,6 +567,17 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") 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("T") then ReadNumber @@ -553,6 +612,10 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") _PLAY_CTX_NEXT_CHANNEL() next channel + if mustInitEnvelope then + SetChipEnvelopeShape EnvelopeShape + end if + if halt then for channel = 0 to MaxChannel SetChipVolume channel, 0 From 26c711fb455074237ff6ce1030fd5c13ed38f4cd Mon Sep 17 00:00:00 2001 From: "Oleg S. Kostenko" Date: Sun, 12 Jul 2026 16:55:36 +0300 Subject: [PATCH 167/169] Play library: implement mixer control command --- src/lib/arch/zx48k/stdlib/play.bas | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/lib/arch/zx48k/stdlib/play.bas b/src/lib/arch/zx48k/stdlib/play.bas index 8ae976d24..7d19d43e9 100644 --- a/src/lib/arch/zx48k/stdlib/play.bas +++ b/src/lib/arch/zx48k/stdlib/play.bas @@ -40,10 +40,10 @@ ' - 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 -' - M - channel mixer control ' - Y, Z - MIDI control (also you can't now pass more than 3 parameters to Play). ' ' Notes: @@ -344,12 +344,17 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") end asm end sub - ' Set pitch on the sound chip for a channel. - sub SetChipPitchDivider(channel as ubyte, divider as uinteger) + ' 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) @@ -368,6 +373,9 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") 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 @@ -487,7 +495,14 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") _PLAY_CTX_SET(byte, _SemitoneAdjustment, 0) - SetChipPitchDivider channel, _Play_NoteDividers(dividerIndex) + 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 @@ -578,6 +593,11 @@ sub Play(channel0 as string, channel1 as string = "", channel2 as string = "") 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 From 2cbe677c7620a050439fd2e935fb5ebfa100dcf1 Mon Sep 17 00:00:00 2001 From: "Oleg S. Kostenko" Date: Sun, 12 Jul 2026 17:02:40 +0300 Subject: [PATCH 168/169] Play library: update example --- examples/play/evolution.bas | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/examples/play/evolution.bas b/examples/play/evolution.bas index cb63c9198..1da411d8d 100644 --- a/examples/play/evolution.bas +++ b/examples/play/evolution.bas @@ -5,20 +5,27 @@ rem (C) 2026 by Ollibony cls -let z$ = "T160 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& O5 3g" +let t$ = "T160 W0 X4000 " -let b$ = "O5 ((1gab&&&ab&&e&&&d&) 1abC&&&bC&&e&&&d& 1ega&5&&&) 3e" +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 c$ = "O5 9&&&&& 1bCD&&&CD&&b&&&g& 1CDE&&&DE&&C&&&g& 1Cba&5&&& O4 3b" +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 a$ = z$ + x$ +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)" -print ink 1; "Channel A" -print a$: print -print ink 1; "Channel B" -print b$: print -print ink 1; "Channel C" -print c$: print +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$ From 8d1240fea7e0d2c75663f71f825a5fb3a087d3f6 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 19 Jul 2026 23:48:07 +0200 Subject: [PATCH 169/169] fix: bug in type conversion --- src/arch/z80/backend/generic.py | 2 +- tests/functional/arch/zx48k/bool_crash.asm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arch/z80/backend/generic.py b/src/arch/z80/backend/generic.py index 630db6514..08bb9286e 100644 --- a/src/arch/z80/backend/generic.py +++ b/src/arch/z80/backend/generic.py @@ -345,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/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